@layr-labs/ecloud-sdk 0.2.0-dev.2 → 0.2.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.
package/dist/compute.cjs CHANGED
@@ -40,6 +40,7 @@ module.exports = __toCommonJS(compute_exports);
40
40
 
41
41
  // src/client/modules/compute/app/index.ts
42
42
  var import_viem9 = require("viem");
43
+ var import_accounts5 = require("viem/accounts");
43
44
 
44
45
  // src/client/common/config/environment.ts
45
46
  var SEPOLIA_CHAIN_ID = 11155111;
@@ -162,6 +163,7 @@ async function buildDockerImage(buildContext, dockerfilePath, tag, logger) {
162
163
  tag,
163
164
  "-f",
164
165
  dockerfilePath,
166
+ "--load",
165
167
  "--progress=plain",
166
168
  buildContext
167
169
  ];
@@ -287,7 +289,7 @@ async function pullDockerImage(docker, imageTag, platform2 = "linux/amd64", logg
287
289
  var child_process2 = __toESM(require("child_process"), 1);
288
290
  var import_child_process = require("child_process");
289
291
  var import_util2 = require("util");
290
- var execAsync = (0, import_util2.promisify)(import_child_process.exec);
292
+ var execFileAsync = (0, import_util2.promisify)(import_child_process.execFile);
291
293
  async function pushDockerImage(docker, imageRef, logger) {
292
294
  logger?.info?.(`Pushing image ${imageRef}...`);
293
295
  return new Promise((resolve2, reject) => {
@@ -328,9 +330,9 @@ async function pushDockerImage(docker, imageRef, logger) {
328
330
  if (!output.includes("digest:") && !output.includes("pushed") && !output.includes("Pushed")) {
329
331
  logger?.debug?.("No clear success indicator in push output, verifying...");
330
332
  }
331
- logger?.info?.("Image push completed successfully");
332
333
  try {
333
334
  await verifyImageExists(imageRef, logger);
335
+ logger?.info?.("Image push completed successfully");
334
336
  resolve2();
335
337
  } catch (error) {
336
338
  reject(error);
@@ -354,7 +356,7 @@ async function verifyImageExists(imageRef, logger) {
354
356
  let retries = 5;
355
357
  while (retries > 0) {
356
358
  try {
357
- await execAsync(`docker manifest inspect ${imageRef}`, {
359
+ await execFileAsync("docker", ["manifest", "inspect", imageRef], {
358
360
  maxBuffer: 10 * 1024 * 1024,
359
361
  timeout: 1e4
360
362
  // 10 second timeout
@@ -828,10 +830,10 @@ async function setupLayeredBuildDirectory(environmentConfig, layeredDockerfileCo
828
830
  // src/client/common/registry/digest.ts
829
831
  var child_process3 = __toESM(require("child_process"), 1);
830
832
  var import_util3 = require("util");
831
- var execFileAsync = (0, import_util3.promisify)(child_process3.execFile);
833
+ var execFileAsync2 = (0, import_util3.promisify)(child_process3.execFile);
832
834
  async function getImageDigestAndName(imageRef) {
833
835
  try {
834
- const { stdout } = await execFileAsync(
836
+ const { stdout } = await execFileAsync2(
835
837
  "docker",
836
838
  ["manifest", "inspect", imageRef],
837
839
  { maxBuffer: 10 * 1024 * 1024 }
@@ -871,7 +873,7 @@ function extractDigestFromMultiPlatform(manifest, imageRef) {
871
873
  }
872
874
  async function extractDigestFromSinglePlatform(manifest, imageRef) {
873
875
  try {
874
- const { stdout } = await execFileAsync("docker", ["inspect", imageRef], {
876
+ const { stdout } = await execFileAsync2("docker", ["inspect", imageRef], {
875
877
  maxBuffer: 10 * 1024 * 1024
876
878
  });
877
879
  const inspectData = JSON.parse(stdout);
@@ -1169,6 +1171,67 @@ Please verify the image exists: docker manifest inspect ${finalImageRef}`
1169
1171
  };
1170
1172
  }
1171
1173
 
1174
+ // src/client/common/release/prebuilt.ts
1175
+ async function createReleaseFromImageDigest(options, logger) {
1176
+ const { imageRef, imageDigest, envFilePath, instanceType, environmentConfig, appId } = options;
1177
+ if (!/^sha256:[0-9a-f]{64}$/i.test(imageDigest)) {
1178
+ throw new Error(`imageDigest must be in format sha256:<64 hex>, got: ${imageDigest}`);
1179
+ }
1180
+ let publicEnv = {};
1181
+ let privateEnv = {};
1182
+ if (envFilePath) {
1183
+ logger.info("Parsing environment file...");
1184
+ const parsed = parseAndValidateEnvFile(envFilePath);
1185
+ publicEnv = parsed.public;
1186
+ privateEnv = parsed.private;
1187
+ } else {
1188
+ logger.info("Continuing without environment file");
1189
+ }
1190
+ publicEnv["EIGEN_MACHINE_TYPE_PUBLIC"] = instanceType;
1191
+ logger.info(`Instance type: ${instanceType}`);
1192
+ logger.info("Encrypting environment variables...");
1193
+ const { encryptionKey } = getKMSKeysForEnvironment(
1194
+ environmentConfig.name,
1195
+ environmentConfig.build
1196
+ );
1197
+ const protectedHeaders = getAppProtectedHeaders(appId);
1198
+ const privateEnvBytes = Buffer.from(JSON.stringify(privateEnv));
1199
+ const encryptedEnvStr = await encryptRSAOAEPAndAES256GCM(
1200
+ encryptionKey,
1201
+ privateEnvBytes,
1202
+ protectedHeaders
1203
+ );
1204
+ const digestHex = imageDigest.split(":")[1];
1205
+ const digestBytes = new Uint8Array(Buffer.from(digestHex, "hex"));
1206
+ if (digestBytes.length !== 32) {
1207
+ throw new Error(`Digest must be exactly 32 bytes, got ${digestBytes.length}`);
1208
+ }
1209
+ const registry = extractRegistryNameNoDocker(imageRef);
1210
+ return {
1211
+ rmsRelease: {
1212
+ artifacts: [{ digest: digestBytes, registry }],
1213
+ upgradeByTime: Math.floor(Date.now() / 1e3) + 3600
1214
+ },
1215
+ publicEnv: new Uint8Array(Buffer.from(JSON.stringify(publicEnv))),
1216
+ encryptedEnv: new Uint8Array(Buffer.from(encryptedEnvStr))
1217
+ };
1218
+ }
1219
+ function extractRegistryNameNoDocker(imageRef) {
1220
+ let name = imageRef;
1221
+ const tagIndex = name.lastIndexOf(":");
1222
+ if (tagIndex !== -1 && !name.substring(tagIndex + 1).includes("/")) {
1223
+ name = name.substring(0, tagIndex);
1224
+ }
1225
+ const digestIndex = name.indexOf("@");
1226
+ if (digestIndex !== -1) {
1227
+ name = name.substring(0, digestIndex);
1228
+ }
1229
+ if ([...name].filter((c) => c === "/").length === 1) {
1230
+ name = `docker.io/${name}`;
1231
+ }
1232
+ return name;
1233
+ }
1234
+
1172
1235
  // src/client/common/contract/caller.ts
1173
1236
  var import_accounts2 = require("viem/accounts");
1174
1237
 
@@ -2205,8 +2268,55 @@ var ERC7702Delegator_default = [
2205
2268
  ];
2206
2269
 
2207
2270
  // src/client/common/contract/eip7702.ts
2271
+ var EXECUTE_BATCH_MODE = "0x0100000000000000000000000000000000000000000000000000000000000000";
2272
+ var GAS_LIMIT_BUFFER_PERCENTAGE = 20n;
2273
+ var GAS_PRICE_BUFFER_PERCENTAGE = 100n;
2274
+ function encodeExecuteBatchData(executions) {
2275
+ const encodedExecutions = (0, import_viem.encodeAbiParameters)(
2276
+ [
2277
+ {
2278
+ type: "tuple[]",
2279
+ components: [
2280
+ { name: "target", type: "address" },
2281
+ { name: "value", type: "uint256" },
2282
+ { name: "callData", type: "bytes" }
2283
+ ]
2284
+ }
2285
+ ],
2286
+ [executions]
2287
+ );
2288
+ return (0, import_viem.encodeFunctionData)({
2289
+ abi: ERC7702Delegator_default,
2290
+ functionName: "execute",
2291
+ args: [EXECUTE_BATCH_MODE, encodedExecutions]
2292
+ });
2293
+ }
2294
+ async function estimateBatchGas(options) {
2295
+ const { publicClient, account, executions } = options;
2296
+ const executeBatchData = encodeExecuteBatchData(executions);
2297
+ const [gasTipCap, block, estimatedGas] = await Promise.all([
2298
+ publicClient.estimateMaxPriorityFeePerGas(),
2299
+ publicClient.getBlock(),
2300
+ publicClient.estimateGas({
2301
+ account,
2302
+ to: account,
2303
+ data: executeBatchData
2304
+ })
2305
+ ]);
2306
+ const baseFee = block.baseFeePerGas ?? 0n;
2307
+ const maxFeePerGas = (baseFee + gasTipCap) * (100n + GAS_PRICE_BUFFER_PERCENTAGE) / 100n;
2308
+ const gasLimit = estimatedGas * (100n + GAS_LIMIT_BUFFER_PERCENTAGE) / 100n;
2309
+ const maxCostWei = gasLimit * maxFeePerGas;
2310
+ return {
2311
+ gasLimit,
2312
+ maxFeePerGas,
2313
+ maxPriorityFeePerGas: gasTipCap,
2314
+ maxCostWei,
2315
+ maxCostEth: formatETH(maxCostWei)
2316
+ };
2317
+ }
2208
2318
  async function checkERC7702Delegation(publicClient, account, delegatorAddress) {
2209
- const code = await publicClient.getBytecode({ address: account });
2319
+ const code = await publicClient.getCode({ address: account });
2210
2320
  if (!code) {
2211
2321
  return false;
2212
2322
  }
@@ -2223,36 +2333,7 @@ async function executeBatch(options, logger) {
2223
2333
  if (!chain) {
2224
2334
  throw new Error("Wallet client must have a chain");
2225
2335
  }
2226
- const encodedExecutions = (0, import_viem.encodeAbiParameters)(
2227
- [
2228
- {
2229
- type: "tuple[]",
2230
- components: [
2231
- { name: "target", type: "address" },
2232
- { name: "value", type: "uint256" },
2233
- { name: "callData", type: "bytes" }
2234
- ]
2235
- }
2236
- ],
2237
- [executions]
2238
- );
2239
- const executeBatchMode = "0x0100000000000000000000000000000000000000000000000000000000000000";
2240
- let executeBatchData;
2241
- try {
2242
- executeBatchData = (0, import_viem.encodeFunctionData)({
2243
- abi: ERC7702Delegator_default,
2244
- functionName: "execute",
2245
- args: [executeBatchMode, encodedExecutions]
2246
- });
2247
- } catch {
2248
- const functionSignature = "execute(bytes32,bytes)";
2249
- const selector = (0, import_viem.keccak256)((0, import_viem.toBytes)(functionSignature)).slice(0, 10);
2250
- const encodedParams = (0, import_viem.encodeAbiParameters)(
2251
- [{ type: "bytes32" }, { type: "bytes" }],
2252
- [executeBatchMode, encodedExecutions]
2253
- );
2254
- executeBatchData = (0, import_viem.concat)([selector, encodedParams]);
2255
- }
2336
+ const executeBatchData = encodeExecuteBatchData(executions);
2256
2337
  const isDelegated2 = await checkERC7702Delegation(
2257
2338
  publicClient,
2258
2339
  account.address,
@@ -2385,12 +2466,23 @@ function stripHexPrefix(value) {
2385
2466
  }
2386
2467
 
2387
2468
  // src/client/common/utils/userapi.ts
2469
+ function isJsonObject(value) {
2470
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2471
+ }
2472
+ function readString(obj, key) {
2473
+ const v = obj[key];
2474
+ return typeof v === "string" ? v : void 0;
2475
+ }
2476
+ function readNumber(obj, key) {
2477
+ const v = obj[key];
2478
+ return typeof v === "number" && Number.isFinite(v) ? v : void 0;
2479
+ }
2388
2480
  var MAX_ADDRESS_COUNT = 5;
2389
2481
  var CanViewAppLogsPermission = "0x2fd3f2fe";
2390
2482
  var CanViewSensitiveAppInfoPermission = "0x0e67b22f";
2391
2483
  var CanUpdateAppProfilePermission = "0x036fef61";
2392
2484
  function getDefaultClientId() {
2393
- const version = true ? "0.2.0-dev.2" : "0.0.0";
2485
+ const version = true ? "0.2.0-dev.3" : "0.0.0";
2394
2486
  return `ecloud-sdk/v${version}`;
2395
2487
  }
2396
2488
  var UserApiClient = class {
@@ -2424,6 +2516,31 @@ var UserApiClient = class {
2424
2516
  };
2425
2517
  });
2426
2518
  }
2519
+ /**
2520
+ * Get app details from UserAPI (includes releases and build/provenance info when available).
2521
+ *
2522
+ * Endpoint: GET /apps/:appAddress
2523
+ */
2524
+ async getApp(appAddress) {
2525
+ const endpoint = `${this.config.userApiServerURL}/apps/${appAddress}`;
2526
+ const res = await this.makeAuthenticatedRequest(endpoint);
2527
+ const raw = await res.json();
2528
+ if (!isJsonObject(raw)) {
2529
+ throw new Error("Unexpected /apps/:id response: expected object");
2530
+ }
2531
+ const id = readString(raw, "id");
2532
+ if (!id) {
2533
+ throw new Error("Unexpected /apps/:id response: missing 'id'");
2534
+ }
2535
+ const releasesRaw = raw.releases;
2536
+ const releases = Array.isArray(releasesRaw) ? releasesRaw.map((r) => transformAppRelease(r)).filter((r) => !!r) : [];
2537
+ return {
2538
+ id,
2539
+ creator: readString(raw, "creator"),
2540
+ contractStatus: readString(raw, "contract_status") ?? readString(raw, "contractStatus"),
2541
+ releases
2542
+ };
2543
+ }
2427
2544
  /**
2428
2545
  * Get available SKUs (instance types) from UserAPI
2429
2546
  */
@@ -2596,6 +2713,48 @@ Please check:
2596
2713
  };
2597
2714
  }
2598
2715
  };
2716
+ function transformAppReleaseBuild(raw) {
2717
+ if (!isJsonObject(raw)) return void 0;
2718
+ const depsRaw = raw.dependencies;
2719
+ const deps = isJsonObject(depsRaw) ? Object.fromEntries(
2720
+ Object.entries(depsRaw).flatMap(([digest, depRaw]) => {
2721
+ const parsed = transformAppReleaseBuild(depRaw);
2722
+ return parsed ? [[digest, parsed]] : [];
2723
+ })
2724
+ ) : void 0;
2725
+ return {
2726
+ buildId: readString(raw, "build_id") ?? readString(raw, "buildId"),
2727
+ billingAddress: readString(raw, "billing_address") ?? readString(raw, "billingAddress"),
2728
+ repoUrl: readString(raw, "repo_url") ?? readString(raw, "repoUrl"),
2729
+ gitRef: readString(raw, "git_ref") ?? readString(raw, "gitRef"),
2730
+ status: readString(raw, "status"),
2731
+ buildType: readString(raw, "build_type") ?? readString(raw, "buildType"),
2732
+ imageName: readString(raw, "image_name") ?? readString(raw, "imageName"),
2733
+ imageDigest: readString(raw, "image_digest") ?? readString(raw, "imageDigest"),
2734
+ imageUrl: readString(raw, "image_url") ?? readString(raw, "imageUrl"),
2735
+ provenanceJson: raw.provenance_json ?? raw.provenanceJson,
2736
+ provenanceSignature: readString(raw, "provenance_signature") ?? readString(raw, "provenanceSignature"),
2737
+ createdAt: readString(raw, "created_at") ?? readString(raw, "createdAt"),
2738
+ updatedAt: readString(raw, "updated_at") ?? readString(raw, "updatedAt"),
2739
+ errorMessage: readString(raw, "error_message") ?? readString(raw, "errorMessage"),
2740
+ dependencies: deps
2741
+ };
2742
+ }
2743
+ function transformAppRelease(raw) {
2744
+ if (!isJsonObject(raw)) return void 0;
2745
+ return {
2746
+ appId: readString(raw, "appId") ?? readString(raw, "app_id"),
2747
+ rmsReleaseId: readString(raw, "rmsReleaseId") ?? readString(raw, "rms_release_id"),
2748
+ imageDigest: readString(raw, "imageDigest") ?? readString(raw, "image_digest"),
2749
+ registryUrl: readString(raw, "registryUrl") ?? readString(raw, "registry_url"),
2750
+ publicEnv: readString(raw, "publicEnv") ?? readString(raw, "public_env"),
2751
+ encryptedEnv: readString(raw, "encryptedEnv") ?? readString(raw, "encrypted_env"),
2752
+ upgradeByTime: readNumber(raw, "upgradeByTime") ?? readNumber(raw, "upgrade_by_time"),
2753
+ createdAt: readString(raw, "createdAt") ?? readString(raw, "created_at"),
2754
+ createdAtBlock: readString(raw, "createdAtBlock") ?? readString(raw, "created_at_block"),
2755
+ build: raw.build ? transformAppReleaseBuild(raw.build) : void 0
2756
+ };
2757
+ }
2599
2758
 
2600
2759
  // src/client/common/abis/AppController.json
2601
2760
  var AppController_default = [
@@ -4142,6 +4301,15 @@ var PermissionController_default = [
4142
4301
  ];
4143
4302
 
4144
4303
  // src/client/common/contract/caller.ts
4304
+ function formatETH(wei) {
4305
+ const eth = Number(wei) / 1e18;
4306
+ const costStr = eth.toFixed(6);
4307
+ const trimmed = costStr.replace(/\.?0+$/, "");
4308
+ if (trimmed === "0" && wei > 0n) {
4309
+ return "<0.000001";
4310
+ }
4311
+ return trimmed;
4312
+ }
4145
4313
  async function calculateAppID(privateKey, rpcUrl, environmentConfig, salt) {
4146
4314
  const privateKeyHex = addHexPrefix(privateKey);
4147
4315
  const account = (0, import_accounts2.privateKeyToAccount)(privateKeyHex);
@@ -4246,20 +4414,20 @@ async function prepareDeployBatch(options, logger) {
4246
4414
  environmentConfig
4247
4415
  };
4248
4416
  }
4249
- async function executeDeployBatch(prepared, gas, logger) {
4417
+ async function executeDeployBatch(data, context, gas, logger) {
4250
4418
  const pendingMessage = "Deploying new app...";
4251
4419
  const txHash = await executeBatch(
4252
4420
  {
4253
- walletClient: prepared.walletClient,
4254
- publicClient: prepared.publicClient,
4255
- environmentConfig: prepared.environmentConfig,
4256
- executions: prepared.executions,
4421
+ walletClient: context.walletClient,
4422
+ publicClient: context.publicClient,
4423
+ environmentConfig: context.environmentConfig,
4424
+ executions: data.executions,
4257
4425
  pendingMessage,
4258
4426
  gas
4259
4427
  },
4260
4428
  logger
4261
4429
  );
4262
- return { appId: prepared.appId, txHash };
4430
+ return { appId: data.appId, txHash };
4263
4431
  }
4264
4432
  async function deployApp(options, logger) {
4265
4433
  const prepared = await prepareDeployBatch(
@@ -4273,7 +4441,17 @@ async function deployApp(options, logger) {
4273
4441
  },
4274
4442
  logger
4275
4443
  );
4276
- return executeDeployBatch(prepared, options.gas, logger);
4444
+ const data = {
4445
+ appId: prepared.appId,
4446
+ salt: prepared.salt,
4447
+ executions: prepared.executions
4448
+ };
4449
+ const context = {
4450
+ walletClient: prepared.walletClient,
4451
+ publicClient: prepared.publicClient,
4452
+ environmentConfig: prepared.environmentConfig
4453
+ };
4454
+ return executeDeployBatch(data, context, options.gas, logger);
4277
4455
  }
4278
4456
  async function prepareUpgradeBatch(options) {
4279
4457
  const {
@@ -4369,14 +4547,14 @@ async function prepareUpgradeBatch(options) {
4369
4547
  environmentConfig
4370
4548
  };
4371
4549
  }
4372
- async function executeUpgradeBatch(prepared, gas, logger) {
4373
- const pendingMessage = `Upgrading app ${prepared.appId}...`;
4550
+ async function executeUpgradeBatch(data, context, gas, logger) {
4551
+ const pendingMessage = `Upgrading app ${data.appId}...`;
4374
4552
  const txHash = await executeBatch(
4375
4553
  {
4376
- walletClient: prepared.walletClient,
4377
- publicClient: prepared.publicClient,
4378
- environmentConfig: prepared.environmentConfig,
4379
- executions: prepared.executions,
4554
+ walletClient: context.walletClient,
4555
+ publicClient: context.publicClient,
4556
+ environmentConfig: context.environmentConfig,
4557
+ executions: data.executions,
4380
4558
  pendingMessage,
4381
4559
  gas
4382
4560
  },
@@ -4394,7 +4572,16 @@ async function upgradeApp(options, logger) {
4394
4572
  publicLogs: options.publicLogs,
4395
4573
  needsPermissionChange: options.needsPermissionChange
4396
4574
  });
4397
- return executeUpgradeBatch(prepared, options.gas, logger);
4575
+ const data = {
4576
+ appId: prepared.appId,
4577
+ executions: prepared.executions
4578
+ };
4579
+ const context = {
4580
+ walletClient: prepared.walletClient,
4581
+ publicClient: prepared.publicClient,
4582
+ environmentConfig: prepared.environmentConfig
4583
+ };
4584
+ return executeUpgradeBatch(data, context, options.gas, logger);
4398
4585
  }
4399
4586
  async function sendAndWaitForTransaction(options, logger) {
4400
4587
  const {
@@ -5065,6 +5252,97 @@ async function withSDKTelemetry(options, action) {
5065
5252
  }
5066
5253
 
5067
5254
  // src/client/modules/compute/app/deploy.ts
5255
+ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger) {
5256
+ return withSDKTelemetry(
5257
+ {
5258
+ functionName: "prepareDeployFromVerifiableBuild",
5259
+ skipTelemetry: options.skipTelemetry,
5260
+ properties: {
5261
+ environment: options.environment || "sepolia"
5262
+ }
5263
+ },
5264
+ async () => {
5265
+ if (!options.privateKey) throw new Error("privateKey is required for deployment");
5266
+ if (!options.imageRef) throw new Error("imageRef is required for deployment");
5267
+ if (!options.imageDigest) throw new Error("imageDigest is required for deployment");
5268
+ assertValidImageReference(options.imageRef);
5269
+ validateAppName(options.appName);
5270
+ validateLogVisibility(options.logVisibility);
5271
+ if (!/^sha256:[0-9a-f]{64}$/i.test(options.imageDigest)) {
5272
+ throw new Error(
5273
+ `imageDigest must be in format sha256:<64 hex>, got: ${options.imageDigest}`
5274
+ );
5275
+ }
5276
+ const { publicLogs } = validateLogVisibility(options.logVisibility);
5277
+ validateResourceUsageMonitoring(options.resourceUsageMonitoring);
5278
+ logger.debug("Performing preflight checks...");
5279
+ const preflightCtx = await doPreflightChecks(
5280
+ {
5281
+ privateKey: options.privateKey,
5282
+ rpcUrl: options.rpcUrl,
5283
+ environment: options.environment
5284
+ },
5285
+ logger
5286
+ );
5287
+ logger.debug("Checking quota availability...");
5288
+ await checkQuotaAvailable(preflightCtx);
5289
+ const salt = generateRandomSalt();
5290
+ logger.debug(`Generated salt: ${Buffer.from(salt).toString("hex")}`);
5291
+ logger.debug("Calculating app ID...");
5292
+ const appIDToBeDeployed = await calculateAppID(
5293
+ preflightCtx.privateKey,
5294
+ options.rpcUrl || preflightCtx.rpcUrl,
5295
+ preflightCtx.environmentConfig,
5296
+ salt
5297
+ );
5298
+ logger.info(``);
5299
+ logger.info(`App ID: ${appIDToBeDeployed}`);
5300
+ logger.info(``);
5301
+ const release = await createReleaseFromImageDigest(
5302
+ {
5303
+ imageRef: options.imageRef,
5304
+ imageDigest: options.imageDigest,
5305
+ envFilePath: options.envFilePath,
5306
+ instanceType: options.instanceType,
5307
+ environmentConfig: preflightCtx.environmentConfig,
5308
+ appId: appIDToBeDeployed
5309
+ },
5310
+ logger
5311
+ );
5312
+ logger.debug("Preparing deploy batch...");
5313
+ const batch = await prepareDeployBatch(
5314
+ {
5315
+ privateKey: preflightCtx.privateKey,
5316
+ rpcUrl: options.rpcUrl || preflightCtx.rpcUrl,
5317
+ environmentConfig: preflightCtx.environmentConfig,
5318
+ salt,
5319
+ release,
5320
+ publicLogs
5321
+ },
5322
+ logger
5323
+ );
5324
+ logger.debug("Estimating gas...");
5325
+ const gasEstimate = await estimateBatchGas({
5326
+ publicClient: batch.publicClient,
5327
+ account: batch.walletClient.account.address,
5328
+ executions: batch.executions
5329
+ });
5330
+ const data = {
5331
+ appId: batch.appId,
5332
+ salt: batch.salt,
5333
+ executions: batch.executions
5334
+ };
5335
+ return {
5336
+ prepared: {
5337
+ data,
5338
+ appName: options.appName,
5339
+ imageRef: options.imageRef
5340
+ },
5341
+ gasEstimate
5342
+ };
5343
+ }
5344
+ );
5345
+ }
5068
5346
  function validateDeployOptions(options) {
5069
5347
  if (!options.privateKey) {
5070
5348
  throw new Error("privateKey is required for deployment");
@@ -5213,6 +5491,141 @@ function generateRandomSalt() {
5213
5491
  crypto.getRandomValues(salt);
5214
5492
  return salt;
5215
5493
  }
5494
+ async function prepareDeploy(options, logger = defaultLogger) {
5495
+ return withSDKTelemetry(
5496
+ {
5497
+ functionName: "prepareDeploy",
5498
+ skipTelemetry: options.skipTelemetry,
5499
+ properties: {
5500
+ environment: options.environment || "sepolia"
5501
+ }
5502
+ },
5503
+ async () => {
5504
+ validateDeployOptions(options);
5505
+ const { logRedirect, publicLogs } = validateLogVisibility(options.logVisibility);
5506
+ const resourceUsageAllow = validateResourceUsageMonitoring(options.resourceUsageMonitoring);
5507
+ logger.debug("Performing preflight checks...");
5508
+ const preflightCtx = await doPreflightChecks(
5509
+ {
5510
+ privateKey: options.privateKey,
5511
+ rpcUrl: options.rpcUrl,
5512
+ environment: options.environment
5513
+ },
5514
+ logger
5515
+ );
5516
+ logger.debug("Checking quota availability...");
5517
+ await checkQuotaAvailable(preflightCtx);
5518
+ logger.debug("Checking Docker...");
5519
+ await ensureDockerIsRunning();
5520
+ const dockerfilePath = options.dockerfilePath || "";
5521
+ const imageRef = options.imageRef || "";
5522
+ const appName = options.appName;
5523
+ const envFilePath = options.envFilePath || "";
5524
+ const instanceType = options.instanceType;
5525
+ const salt = generateRandomSalt();
5526
+ logger.debug(`Generated salt: ${Buffer.from(salt).toString("hex")}`);
5527
+ logger.debug("Calculating app ID...");
5528
+ const appIDToBeDeployed = await calculateAppID(
5529
+ preflightCtx.privateKey,
5530
+ options.rpcUrl || preflightCtx.rpcUrl,
5531
+ preflightCtx.environmentConfig,
5532
+ salt
5533
+ );
5534
+ logger.info(``);
5535
+ logger.info(`App ID: ${appIDToBeDeployed}`);
5536
+ logger.info(``);
5537
+ logger.info("Preparing release...");
5538
+ const { release, finalImageRef } = await prepareRelease(
5539
+ {
5540
+ dockerfilePath,
5541
+ imageRef,
5542
+ envFilePath,
5543
+ logRedirect,
5544
+ resourceUsageAllow,
5545
+ instanceType,
5546
+ environmentConfig: preflightCtx.environmentConfig,
5547
+ appId: appIDToBeDeployed
5548
+ },
5549
+ logger
5550
+ );
5551
+ logger.debug("Preparing deploy batch...");
5552
+ const batch = await prepareDeployBatch(
5553
+ {
5554
+ privateKey: preflightCtx.privateKey,
5555
+ rpcUrl: options.rpcUrl || preflightCtx.rpcUrl,
5556
+ environmentConfig: preflightCtx.environmentConfig,
5557
+ salt,
5558
+ release,
5559
+ publicLogs
5560
+ },
5561
+ logger
5562
+ );
5563
+ logger.debug("Estimating gas...");
5564
+ const gasEstimate = await estimateBatchGas({
5565
+ publicClient: batch.publicClient,
5566
+ account: batch.walletClient.account.address,
5567
+ executions: batch.executions
5568
+ });
5569
+ const data = {
5570
+ appId: batch.appId,
5571
+ salt: batch.salt,
5572
+ executions: batch.executions
5573
+ };
5574
+ return {
5575
+ prepared: {
5576
+ data,
5577
+ appName,
5578
+ imageRef: finalImageRef
5579
+ },
5580
+ gasEstimate
5581
+ };
5582
+ }
5583
+ );
5584
+ }
5585
+ async function executeDeploy(options) {
5586
+ const { prepared, context, gas, logger = defaultLogger, skipTelemetry } = options;
5587
+ return withSDKTelemetry(
5588
+ {
5589
+ functionName: "executeDeploy",
5590
+ skipTelemetry
5591
+ },
5592
+ async () => {
5593
+ logger.info("Deploying on-chain...");
5594
+ const { appId, txHash } = await executeDeployBatch(prepared.data, context, gas, logger);
5595
+ return {
5596
+ appId,
5597
+ txHash,
5598
+ appName: prepared.appName,
5599
+ imageRef: prepared.imageRef
5600
+ };
5601
+ }
5602
+ );
5603
+ }
5604
+ async function watchDeployment(appId, privateKey, rpcUrl, environment, logger = defaultLogger, clientId, skipTelemetry) {
5605
+ return withSDKTelemetry(
5606
+ {
5607
+ functionName: "watchDeployment",
5608
+ skipTelemetry,
5609
+ properties: {
5610
+ environment
5611
+ }
5612
+ },
5613
+ async () => {
5614
+ const environmentConfig = getEnvironmentConfig(environment);
5615
+ logger.info("Waiting for app to start...");
5616
+ return watchUntilRunning(
5617
+ {
5618
+ privateKey,
5619
+ rpcUrl,
5620
+ environmentConfig,
5621
+ appId,
5622
+ clientId
5623
+ },
5624
+ logger
5625
+ );
5626
+ }
5627
+ );
5628
+ }
5216
5629
 
5217
5630
  // src/client/common/utils/permissions.ts
5218
5631
  var import_viem8 = require("viem");
@@ -5240,6 +5653,81 @@ async function checkAppLogPermission(preflightCtx, appAddress, logger) {
5240
5653
  }
5241
5654
 
5242
5655
  // src/client/modules/compute/app/upgrade.ts
5656
+ async function prepareUpgradeFromVerifiableBuild(options, logger = defaultLogger) {
5657
+ return withSDKTelemetry(
5658
+ {
5659
+ functionName: "prepareUpgradeFromVerifiableBuild",
5660
+ skipTelemetry: options.skipTelemetry,
5661
+ properties: {
5662
+ environment: options.environment || "sepolia"
5663
+ }
5664
+ },
5665
+ async () => {
5666
+ logger.debug("Performing preflight checks...");
5667
+ const preflightCtx = await doPreflightChecks(
5668
+ {
5669
+ privateKey: options.privateKey,
5670
+ rpcUrl: options.rpcUrl,
5671
+ environment: options.environment
5672
+ },
5673
+ logger
5674
+ );
5675
+ const appID = validateUpgradeOptions(options);
5676
+ assertValidImageReference(options.imageRef);
5677
+ if (!/^sha256:[0-9a-f]{64}$/i.test(options.imageDigest)) {
5678
+ throw new Error(
5679
+ `imageDigest must be in format sha256:<64 hex>, got: ${options.imageDigest}`
5680
+ );
5681
+ }
5682
+ const { publicLogs } = validateLogVisibility(options.logVisibility);
5683
+ validateResourceUsageMonitoring(options.resourceUsageMonitoring);
5684
+ const envFilePath = options.envFilePath || "";
5685
+ logger.info("Preparing release (verifiable build, no local layering)...");
5686
+ const release = await createReleaseFromImageDigest(
5687
+ {
5688
+ imageRef: options.imageRef,
5689
+ imageDigest: options.imageDigest,
5690
+ envFilePath,
5691
+ instanceType: options.instanceType,
5692
+ environmentConfig: preflightCtx.environmentConfig,
5693
+ appId: appID
5694
+ },
5695
+ logger
5696
+ );
5697
+ logger.debug("Checking current log permission state...");
5698
+ const currentlyPublic = await checkAppLogPermission(preflightCtx, appID, logger);
5699
+ const needsPermissionChange = currentlyPublic !== publicLogs;
5700
+ logger.debug("Preparing upgrade batch...");
5701
+ const batch = await prepareUpgradeBatch({
5702
+ privateKey: preflightCtx.privateKey,
5703
+ rpcUrl: options.rpcUrl || preflightCtx.rpcUrl,
5704
+ environmentConfig: preflightCtx.environmentConfig,
5705
+ appId: appID,
5706
+ release,
5707
+ publicLogs,
5708
+ needsPermissionChange
5709
+ });
5710
+ logger.debug("Estimating gas...");
5711
+ const gasEstimate = await estimateBatchGas({
5712
+ publicClient: batch.publicClient,
5713
+ account: batch.walletClient.account.address,
5714
+ executions: batch.executions
5715
+ });
5716
+ const data = {
5717
+ appId: batch.appId,
5718
+ executions: batch.executions
5719
+ };
5720
+ return {
5721
+ prepared: {
5722
+ data,
5723
+ appId: appID,
5724
+ imageRef: options.imageRef
5725
+ },
5726
+ gasEstimate
5727
+ };
5728
+ }
5729
+ );
5730
+ }
5243
5731
  function validateUpgradeOptions(options) {
5244
5732
  if (!options.privateKey) {
5245
5733
  throw new Error("privateKey is required for upgrade");
@@ -5344,6 +5832,125 @@ async function upgrade(options, logger = defaultLogger) {
5344
5832
  }
5345
5833
  );
5346
5834
  }
5835
+ async function prepareUpgrade(options, logger = defaultLogger) {
5836
+ return withSDKTelemetry(
5837
+ {
5838
+ functionName: "prepareUpgrade",
5839
+ skipTelemetry: options.skipTelemetry,
5840
+ properties: {
5841
+ environment: options.environment || "sepolia"
5842
+ }
5843
+ },
5844
+ async () => {
5845
+ logger.debug("Performing preflight checks...");
5846
+ const preflightCtx = await doPreflightChecks(
5847
+ {
5848
+ privateKey: options.privateKey,
5849
+ rpcUrl: options.rpcUrl,
5850
+ environment: options.environment
5851
+ },
5852
+ logger
5853
+ );
5854
+ const appID = validateUpgradeOptions(options);
5855
+ const { logRedirect, publicLogs } = validateLogVisibility(options.logVisibility);
5856
+ const resourceUsageAllow = validateResourceUsageMonitoring(options.resourceUsageMonitoring);
5857
+ logger.debug("Checking Docker...");
5858
+ await ensureDockerIsRunning();
5859
+ const dockerfilePath = options.dockerfilePath || "";
5860
+ const imageRef = options.imageRef || "";
5861
+ const envFilePath = options.envFilePath || "";
5862
+ const instanceType = options.instanceType;
5863
+ logger.info("Preparing release...");
5864
+ const { release, finalImageRef } = await prepareRelease(
5865
+ {
5866
+ dockerfilePath,
5867
+ imageRef,
5868
+ envFilePath,
5869
+ logRedirect,
5870
+ resourceUsageAllow,
5871
+ instanceType,
5872
+ environmentConfig: preflightCtx.environmentConfig,
5873
+ appId: appID
5874
+ },
5875
+ logger
5876
+ );
5877
+ logger.debug("Checking current log permission state...");
5878
+ const currentlyPublic = await checkAppLogPermission(preflightCtx, appID, logger);
5879
+ const needsPermissionChange = currentlyPublic !== publicLogs;
5880
+ logger.debug("Preparing upgrade batch...");
5881
+ const batch = await prepareUpgradeBatch({
5882
+ privateKey: preflightCtx.privateKey,
5883
+ rpcUrl: options.rpcUrl || preflightCtx.rpcUrl,
5884
+ environmentConfig: preflightCtx.environmentConfig,
5885
+ appId: appID,
5886
+ release,
5887
+ publicLogs,
5888
+ needsPermissionChange
5889
+ });
5890
+ logger.debug("Estimating gas...");
5891
+ const gasEstimate = await estimateBatchGas({
5892
+ publicClient: batch.publicClient,
5893
+ account: batch.walletClient.account.address,
5894
+ executions: batch.executions
5895
+ });
5896
+ const data = {
5897
+ appId: batch.appId,
5898
+ executions: batch.executions
5899
+ };
5900
+ return {
5901
+ prepared: {
5902
+ data,
5903
+ appId: appID,
5904
+ imageRef: finalImageRef
5905
+ },
5906
+ gasEstimate
5907
+ };
5908
+ }
5909
+ );
5910
+ }
5911
+ async function executeUpgrade(options) {
5912
+ const { prepared, context, gas, logger = defaultLogger, skipTelemetry } = options;
5913
+ return withSDKTelemetry(
5914
+ {
5915
+ functionName: "executeUpgrade",
5916
+ skipTelemetry
5917
+ },
5918
+ async () => {
5919
+ logger.info("Upgrading on-chain...");
5920
+ const txHash = await executeUpgradeBatch(prepared.data, context, gas, logger);
5921
+ return {
5922
+ appId: prepared.appId,
5923
+ imageRef: prepared.imageRef,
5924
+ txHash
5925
+ };
5926
+ }
5927
+ );
5928
+ }
5929
+ async function watchUpgrade(appId, privateKey, rpcUrl, environment, logger = defaultLogger, clientId, skipTelemetry) {
5930
+ return withSDKTelemetry(
5931
+ {
5932
+ functionName: "watchUpgrade",
5933
+ skipTelemetry,
5934
+ properties: {
5935
+ environment
5936
+ }
5937
+ },
5938
+ async () => {
5939
+ const environmentConfig = getEnvironmentConfig(environment);
5940
+ logger.info("Waiting for upgrade to complete...");
5941
+ await watchUntilUpgradeComplete(
5942
+ {
5943
+ privateKey,
5944
+ rpcUrl,
5945
+ environmentConfig,
5946
+ appId,
5947
+ clientId
5948
+ },
5949
+ logger
5950
+ );
5951
+ }
5952
+ );
5953
+ }
5347
5954
 
5348
5955
  // src/client/modules/compute/app/create.ts
5349
5956
  var fs7 = __toESM(require("fs"), 1);
@@ -5441,8 +6048,8 @@ var path4 = __toESM(require("path"), 1);
5441
6048
  var os3 = __toESM(require("os"), 1);
5442
6049
  var import_child_process2 = require("child_process");
5443
6050
  var import_util4 = require("util");
5444
- var execAsync2 = (0, import_util4.promisify)(import_child_process2.exec);
5445
- var execFileAsync2 = (0, import_util4.promisify)(import_child_process2.execFile);
6051
+ var execAsync = (0, import_util4.promisify)(import_child_process2.exec);
6052
+ var execFileAsync3 = (0, import_util4.promisify)(import_child_process2.execFile);
5446
6053
  async function fetchTemplate(repoURL, ref, targetDir, config, logger) {
5447
6054
  if (!repoURL) {
5448
6055
  throw new Error("repoURL is required");
@@ -5451,13 +6058,13 @@ async function fetchTemplate(repoURL, ref, targetDir, config, logger) {
5451
6058
  Cloning repo: ${repoURL} \u2192 ${targetDir}
5452
6059
  `);
5453
6060
  try {
5454
- await execAsync2(`git clone --no-checkout --progress ${repoURL} ${targetDir}`, {
6061
+ await execAsync(`git clone --no-checkout --progress ${repoURL} ${targetDir}`, {
5455
6062
  maxBuffer: 10 * 1024 * 1024
5456
6063
  });
5457
- await execFileAsync2("git", ["-C", targetDir, "checkout", "--quiet", ref], {
6064
+ await execFileAsync3("git", ["-C", targetDir, "checkout", "--quiet", ref], {
5458
6065
  maxBuffer: 10 * 1024 * 1024
5459
6066
  });
5460
- await execFileAsync2(
6067
+ await execFileAsync3(
5461
6068
  "git",
5462
6069
  ["-C", targetDir, "submodule", "update", "--init", "--recursive", "--progress"],
5463
6070
  { maxBuffer: 10 * 1024 * 1024 }
@@ -5502,14 +6109,14 @@ Cloning template: ${repoURL} \u2192 extracting ${subPath}
5502
6109
  }
5503
6110
  async function cloneSparse(repoURL, ref, subPath, tempDir) {
5504
6111
  try {
5505
- await execFileAsync2("git", ["init", tempDir]);
5506
- await execFileAsync2("git", ["-C", tempDir, "remote", "add", "origin", repoURL]);
5507
- await execFileAsync2("git", ["-C", tempDir, "config", "core.sparseCheckout", "true"]);
6112
+ await execFileAsync3("git", ["init", tempDir]);
6113
+ await execFileAsync3("git", ["-C", tempDir, "remote", "add", "origin", repoURL]);
6114
+ await execFileAsync3("git", ["-C", tempDir, "config", "core.sparseCheckout", "true"]);
5508
6115
  const sparseCheckoutPath = path4.join(tempDir, ".git/info/sparse-checkout");
5509
6116
  fs5.writeFileSync(sparseCheckoutPath, `${subPath}
5510
6117
  `);
5511
- await execFileAsync2("git", ["-C", tempDir, "fetch", "origin", ref]);
5512
- await execFileAsync2("git", ["-C", tempDir, "checkout", ref]);
6118
+ await execFileAsync3("git", ["-C", tempDir, "fetch", "origin", ref]);
6119
+ await execFileAsync3("git", ["-C", tempDir, "checkout", ref]);
5513
6120
  } catch (error) {
5514
6121
  throw new Error(`Failed to clone sparse repository: ${error.message}`);
5515
6122
  }
@@ -6084,6 +6691,187 @@ function createAppModule(ctx) {
6084
6691
  imageRef: result.imageRef
6085
6692
  };
6086
6693
  },
6694
+ // Granular deploy control
6695
+ async prepareDeploy(opts) {
6696
+ return prepareDeploy(
6697
+ {
6698
+ privateKey,
6699
+ rpcUrl: ctx.rpcUrl,
6700
+ environment: ctx.environment,
6701
+ appName: opts.name,
6702
+ instanceType: opts.instanceType,
6703
+ dockerfilePath: opts.dockerfile,
6704
+ envFilePath: opts.envFile,
6705
+ imageRef: opts.imageRef,
6706
+ logVisibility: opts.logVisibility,
6707
+ resourceUsageMonitoring: opts.resourceUsageMonitoring,
6708
+ skipTelemetry
6709
+ },
6710
+ logger
6711
+ );
6712
+ },
6713
+ async prepareDeployFromVerifiableBuild(opts) {
6714
+ return prepareDeployFromVerifiableBuild(
6715
+ {
6716
+ privateKey,
6717
+ rpcUrl: ctx.rpcUrl,
6718
+ environment: ctx.environment,
6719
+ appName: opts.name,
6720
+ instanceType: opts.instanceType,
6721
+ envFilePath: opts.envFile,
6722
+ imageRef: opts.imageRef,
6723
+ imageDigest: opts.imageDigest,
6724
+ logVisibility: opts.logVisibility,
6725
+ resourceUsageMonitoring: opts.resourceUsageMonitoring,
6726
+ skipTelemetry
6727
+ },
6728
+ logger
6729
+ );
6730
+ },
6731
+ async executeDeploy(prepared, gas) {
6732
+ const account = (0, import_accounts5.privateKeyToAccount)(privateKey);
6733
+ const chain = getChainFromID(environment.chainID);
6734
+ const publicClient = (0, import_viem9.createPublicClient)({
6735
+ chain,
6736
+ transport: (0, import_viem9.http)(ctx.rpcUrl)
6737
+ });
6738
+ const walletClient = (0, import_viem9.createWalletClient)({
6739
+ account,
6740
+ chain,
6741
+ transport: (0, import_viem9.http)(ctx.rpcUrl)
6742
+ });
6743
+ const result = await executeDeploy({
6744
+ prepared,
6745
+ context: {
6746
+ walletClient,
6747
+ publicClient,
6748
+ environmentConfig: environment
6749
+ },
6750
+ gas,
6751
+ logger,
6752
+ skipTelemetry
6753
+ });
6754
+ return {
6755
+ appId: result.appId,
6756
+ txHash: result.txHash,
6757
+ appName: result.appName,
6758
+ imageRef: result.imageRef
6759
+ };
6760
+ },
6761
+ async watchDeployment(appId) {
6762
+ return watchDeployment(
6763
+ appId,
6764
+ privateKey,
6765
+ ctx.rpcUrl,
6766
+ ctx.environment,
6767
+ logger,
6768
+ ctx.clientId,
6769
+ skipTelemetry
6770
+ );
6771
+ },
6772
+ // Granular upgrade control
6773
+ async prepareUpgrade(appId, opts) {
6774
+ return prepareUpgrade(
6775
+ {
6776
+ appId,
6777
+ privateKey,
6778
+ rpcUrl: ctx.rpcUrl,
6779
+ environment: ctx.environment,
6780
+ instanceType: opts.instanceType,
6781
+ dockerfilePath: opts.dockerfile,
6782
+ envFilePath: opts.envFile,
6783
+ imageRef: opts.imageRef,
6784
+ logVisibility: opts.logVisibility,
6785
+ resourceUsageMonitoring: opts.resourceUsageMonitoring,
6786
+ skipTelemetry
6787
+ },
6788
+ logger
6789
+ );
6790
+ },
6791
+ async prepareUpgradeFromVerifiableBuild(appId, opts) {
6792
+ return prepareUpgradeFromVerifiableBuild(
6793
+ {
6794
+ appId,
6795
+ privateKey,
6796
+ rpcUrl: ctx.rpcUrl,
6797
+ environment: ctx.environment,
6798
+ instanceType: opts.instanceType,
6799
+ envFilePath: opts.envFile,
6800
+ imageRef: opts.imageRef,
6801
+ imageDigest: opts.imageDigest,
6802
+ logVisibility: opts.logVisibility,
6803
+ resourceUsageMonitoring: opts.resourceUsageMonitoring,
6804
+ skipTelemetry
6805
+ },
6806
+ logger
6807
+ );
6808
+ },
6809
+ async executeUpgrade(prepared, gas) {
6810
+ const account = (0, import_accounts5.privateKeyToAccount)(privateKey);
6811
+ const chain = getChainFromID(environment.chainID);
6812
+ const publicClient = (0, import_viem9.createPublicClient)({
6813
+ chain,
6814
+ transport: (0, import_viem9.http)(ctx.rpcUrl)
6815
+ });
6816
+ const walletClient = (0, import_viem9.createWalletClient)({
6817
+ account,
6818
+ chain,
6819
+ transport: (0, import_viem9.http)(ctx.rpcUrl)
6820
+ });
6821
+ const result = await executeUpgrade({
6822
+ prepared,
6823
+ context: {
6824
+ walletClient,
6825
+ publicClient,
6826
+ environmentConfig: environment
6827
+ },
6828
+ gas,
6829
+ logger,
6830
+ skipTelemetry
6831
+ });
6832
+ return {
6833
+ appId: result.appId,
6834
+ txHash: result.txHash,
6835
+ imageRef: result.imageRef
6836
+ };
6837
+ },
6838
+ async watchUpgrade(appId) {
6839
+ return watchUpgrade(
6840
+ appId,
6841
+ privateKey,
6842
+ ctx.rpcUrl,
6843
+ ctx.environment,
6844
+ logger,
6845
+ ctx.clientId,
6846
+ skipTelemetry
6847
+ );
6848
+ },
6849
+ // Profile management
6850
+ async setProfile(appId, profile) {
6851
+ return withSDKTelemetry(
6852
+ {
6853
+ functionName: "setProfile",
6854
+ skipTelemetry,
6855
+ properties: { environment: ctx.environment }
6856
+ },
6857
+ async () => {
6858
+ const userApiClient = new UserApiClient(
6859
+ environment,
6860
+ privateKey,
6861
+ ctx.rpcUrl,
6862
+ ctx.clientId
6863
+ );
6864
+ return userApiClient.uploadAppProfile(
6865
+ appId,
6866
+ profile.name,
6867
+ profile.website,
6868
+ profile.description,
6869
+ profile.xURL,
6870
+ profile.imagePath
6871
+ );
6872
+ }
6873
+ );
6874
+ },
6087
6875
  async logs(opts) {
6088
6876
  return logs(
6089
6877
  {