@layr-labs/ecloud-sdk 1.0.0-dev.7 → 1.0.0-dev.8

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/index.cjs CHANGED
@@ -174,6 +174,7 @@ __export(index_exports, {
174
174
  EMPTY_CONTAINER_POLICY: () => EMPTY_CONTAINER_POLICY,
175
175
  ERC20ABI: () => ERC20_default,
176
176
  ForbiddenError: () => ForbiddenError,
177
+ InsufficientGasError: () => InsufficientGasError,
177
178
  JwtProvider: () => JwtProvider,
178
179
  NoopClient: () => NoopClient,
179
180
  NotFoundError: () => NotFoundError,
@@ -183,9 +184,12 @@ __export(index_exports, {
183
184
  TimeoutError: () => TimeoutError,
184
185
  USDCCreditsABI: () => USDCCredits_default,
185
186
  UserApiClient: () => UserApiClient,
187
+ WATCH_DEFAULT_TIMEOUT_SECONDS: () => WATCH_DEFAULT_TIMEOUT_SECONDS,
188
+ WatchTimeoutError: () => WatchTimeoutError,
186
189
  addHexPrefix: () => addHexPrefix,
187
190
  addMetric: () => addMetric,
188
191
  addMetricWithDimensions: () => addMetricWithDimensions,
192
+ assertSufficientGas: () => assertSufficientGas,
189
193
  assertValidFilePath: () => assertValidFilePath,
190
194
  assertValidImageReference: () => assertValidImageReference,
191
195
  assertValidPrivateKey: () => assertValidPrivateKey,
@@ -287,7 +291,7 @@ __export(index_exports, {
287
291
  module.exports = __toCommonJS(index_exports);
288
292
 
289
293
  // src/client/modules/compute/app/index.ts
290
- var import_viem6 = require("viem");
294
+ var import_viem7 = require("viem");
291
295
 
292
296
  // src/client/common/docker/build.ts
293
297
  var child_process = __toESM(require("child_process"), 1);
@@ -576,7 +580,74 @@ var PushPermissionError = class extends Error {
576
580
  var import_handlebars = __toESM(require("handlebars"), 1);
577
581
 
578
582
  // src/client/common/templates/Dockerfile.layered.tmpl
579
- var Dockerfile_layered_default = '{{#if includeTLS}}\n# Get Caddy from official image\nFROM caddy:2.10.2-alpine AS caddy\n{{/if}}\n\nFROM {{baseImage}}\n\n{{#if originalUser}}\n# Switch to root to perform setup (base image has non-root USER: {{originalUser}})\nUSER root\n{{/if}}\n\n# Copy core TEE components\nCOPY compute-source-env.sh /usr/local/bin/\nCOPY kms-client /usr/local/bin/\nCOPY kms-signing-public-key.pem /usr/local/bin/\n\n{{#if includeTLS}}\n# Copy Caddy from official image\nCOPY --from=caddy /usr/bin/caddy /usr/local/bin/caddy\n\n# Copy TLS components\nCOPY tls-keygen /usr/local/bin/\nCOPY Caddyfile /etc/caddy/\n{{/if}}\n\n{{#if originalUser}}\n# Make binaries executable (755 for executables, 644 for keys)\nRUN chmod 755 /usr/local/bin/compute-source-env.sh \\\n && chmod 755 /usr/local/bin/kms-client{{#if includeTLS}} \\\n && chmod 755 /usr/local/bin/tls-keygen \\\n && chmod 755 /usr/local/bin/caddy{{/if}} \\\n && chmod 644 /usr/local/bin/kms-signing-public-key.pem\n\n# Store original user - entrypoint will drop privileges to this user after TEE setup\nENV __ECLOUD_ORIGINAL_USER={{originalUser}}\n{{else}}\n# Make binaries executable (preserve existing permissions, just add execute)\nRUN chmod +x /usr/local/bin/compute-source-env.sh \\\n && chmod +x /usr/local/bin/kms-client{{#if includeTLS}} \\\n && chmod +x /usr/local/bin/tls-keygen{{/if}}\n{{/if}}\n\n{{#if logRedirect}}\n\nLABEL tee.launch_policy.log_redirect={{logRedirect}}\n{{/if}}\n{{#if resourceUsageAllow}}\n\nLABEL tee.launch_policy.monitoring_memory_allow={{resourceUsageAllow}}\n{{/if}}\n\nLABEL eigenx_cli_version={{ecloudCLIVersion}}\nLABEL eigenx_vm_image=eigen\n\n{{#if includeTLS}}\n# Expose both HTTP and HTTPS ports for Caddy\nEXPOSE 80 443\n{{/if}}\n\nENTRYPOINT ["/usr/local/bin/compute-source-env.sh"]\nCMD {{{originalCmd}}}\n';
583
+ var Dockerfile_layered_default = `{{#if includeTLS}}
584
+ # Get Caddy from official image
585
+ FROM caddy:2.10.2-alpine AS caddy
586
+ {{/if}}
587
+
588
+ FROM {{baseImage}}
589
+
590
+ {{#if originalUser}}
591
+ # Switch to root to perform setup (base image has non-root USER: {{originalUser}})
592
+ USER root
593
+ {{/if}}
594
+
595
+ # Copy core TEE components
596
+ # CA bundle for kms-client / tls-keygen to validate HTTPS calls to
597
+ # eigencloud.xyz endpoints. Bundled at a non-standard path and consumed
598
+ # only via SSL_CERT_FILE in compute-source-env.sh, so the user's
599
+ # /etc/ssl/ is never touched.
600
+ COPY --from=alpine:3.20.10 /etc/ssl/certs/ca-certificates.crt /usr/local/share/eigenx-ca-certs.crt
601
+ COPY compute-source-env.sh /usr/local/bin/
602
+ COPY kms-client /usr/local/bin/
603
+ COPY kms-signing-public-key.pem /usr/local/bin/
604
+
605
+ {{#if includeTLS}}
606
+ # Copy Caddy from official image
607
+ COPY --from=caddy /usr/bin/caddy /usr/local/bin/caddy
608
+
609
+ # Copy TLS components
610
+ COPY tls-keygen /usr/local/bin/
611
+ COPY Caddyfile /etc/caddy/
612
+ {{/if}}
613
+
614
+ {{#if originalUser}}
615
+ # Make binaries executable (755 for executables, 644 for keys)
616
+ RUN chmod 755 /usr/local/bin/compute-source-env.sh \\
617
+ && chmod 755 /usr/local/bin/kms-client{{#if includeTLS}} \\
618
+ && chmod 755 /usr/local/bin/tls-keygen \\
619
+ && chmod 755 /usr/local/bin/caddy{{/if}} \\
620
+ && chmod 644 /usr/local/bin/kms-signing-public-key.pem
621
+
622
+ # Store original user - entrypoint will drop privileges to this user after TEE setup
623
+ ENV __ECLOUD_ORIGINAL_USER={{originalUser}}
624
+ {{else}}
625
+ # Make binaries executable (preserve existing permissions, just add execute)
626
+ RUN chmod +x /usr/local/bin/compute-source-env.sh \\
627
+ && chmod +x /usr/local/bin/kms-client{{#if includeTLS}} \\
628
+ && chmod +x /usr/local/bin/tls-keygen{{/if}}
629
+ {{/if}}
630
+
631
+ {{#if logRedirect}}
632
+
633
+ LABEL tee.launch_policy.log_redirect={{logRedirect}}
634
+ {{/if}}
635
+ {{#if resourceUsageAllow}}
636
+
637
+ LABEL tee.launch_policy.monitoring_memory_allow={{resourceUsageAllow}}
638
+ {{/if}}
639
+
640
+ LABEL eigenx_cli_version={{ecloudCLIVersion}}
641
+ LABEL eigenx_vm_image=eigen
642
+
643
+ {{#if includeTLS}}
644
+ # Expose both HTTP and HTTPS ports for Caddy
645
+ EXPOSE 80 443
646
+ {{/if}}
647
+
648
+ ENTRYPOINT ["/usr/local/bin/compute-source-env.sh"]
649
+ CMD {{{originalCmd}}}
650
+ `;
580
651
 
581
652
  // src/client/common/templates/dockerfileTemplate.ts
582
653
  function processDockerfileTemplate(data) {
@@ -593,7 +664,7 @@ echo "compute-source-env.sh: Running setup script..."
593
664
 
594
665
  # Fetch and source environment variables from KMS
595
666
  echo "Fetching secrets from KMS..."
596
- if /usr/local/bin/kms-client \\
667
+ if SSL_CERT_FILE=/usr/local/share/eigenx-ca-certs.crt /usr/local/bin/kms-client \\
597
668
  --kms-server-url "{{kmsServerURL}}" \\
598
669
  --kms-signing-key-file /usr/local/bin/kms-signing-public-key.pem \\
599
670
  --userapi-url "{{userAPIURL}}" \\
@@ -651,7 +722,8 @@ setup_tls() {
651
722
 
652
723
  echo "compute-source-env.sh: Obtaining TLS certificate using $challenge challenge..."
653
724
  # Pass the API URL for certificate persistence
654
- if ! MNEMONIC="$mnemonic" DOMAIN="$domain" API_URL="{{userAPIURL}}" /usr/local/bin/tls-keygen \\
725
+ if ! SSL_CERT_FILE=/usr/local/share/eigenx-ca-certs.crt \\
726
+ MNEMONIC="$mnemonic" DOMAIN="$domain" API_URL="{{userAPIURL}}" /usr/local/bin/tls-keygen \\
655
727
  -challenge "$challenge" \\
656
728
  $staging_flag; then
657
729
  echo "compute-source-env.sh: ERROR - Failed to obtain TLS certificate"
@@ -1044,16 +1116,7 @@ async function extractDigestFromSinglePlatform(manifest, imageRef) {
1044
1116
  architecture: inspectData[0].Architecture
1045
1117
  } : null;
1046
1118
  if (!config) {
1047
- if (manifest.config?.digest) {
1048
- const digest = hexStringToBytes32(manifest.config.digest);
1049
- const registry = extractRegistryName(imageRef);
1050
- return {
1051
- digest,
1052
- registry,
1053
- platform: DOCKER_PLATFORM
1054
- };
1055
- }
1056
- throw new Error(`Could not determine platform for ${imageRef}`);
1119
+ throw createPlatformErrorMessage(imageRef, ["unknown (could not determine architecture)"]);
1057
1120
  }
1058
1121
  const platform2 = `${config.os}/${config.architecture}`;
1059
1122
  if (platform2 === DOCKER_PLATFORM) {
@@ -1125,14 +1188,11 @@ Image: ${imageRef}
1125
1188
  Found platform(s): ${platforms.join(", ")}
1126
1189
  Required platform: ${DOCKER_PLATFORM}
1127
1190
 
1128
- To fix this issue:
1129
- 1. Manual fix:
1130
- a. Rebuild your image with the correct platform:
1131
- docker build --platform ${DOCKER_PLATFORM} -t ${imageRef} .
1132
- b. Push the rebuilt image to your remote registry:
1133
- docker push ${imageRef}
1134
-
1135
- 2. Or use the SDK to build with the correct platform automatically.`;
1191
+ To fix, either:
1192
+ 1. Rebuild the image for ${DOCKER_PLATFORM} and push it:
1193
+ docker buildx build --platform ${DOCKER_PLATFORM} -t ${imageRef} --push .
1194
+ 2. Or use a verifiable build (--verifiable --repo <repo> --commit <sha>), which
1195
+ builds server-side and needs no local Docker.`;
1136
1196
  return new Error(errorMsg);
1137
1197
  }
1138
1198
 
@@ -1237,6 +1297,8 @@ async function prepareRelease(options, logger) {
1237
1297
  logger.info(`Waiting ${REGISTRY_PROPAGATION_WAIT_SECONDS} seconds for registry propagation...`);
1238
1298
  await new Promise((resolve2) => setTimeout(resolve2, REGISTRY_PROPAGATION_WAIT_SECONDS * 1e3));
1239
1299
  } else {
1300
+ logger.info("Verifying image platform (linux/amd64)...");
1301
+ await getImageDigestAndName(imageRef);
1240
1302
  logger.info("Checking if image needs layering...");
1241
1303
  finalImageRef = await layerRemoteImageIfNeeded(
1242
1304
  {
@@ -6442,12 +6504,41 @@ async function undelegate(options, logger = noopLogger) {
6442
6504
  return hash;
6443
6505
  }
6444
6506
 
6507
+ // src/client/common/gas/insufficientGas.ts
6508
+ var import_viem4 = require("viem");
6509
+ var InsufficientGasError = class extends Error {
6510
+ constructor(args) {
6511
+ const requiredEth = (0, import_viem4.formatEther)(args.requiredWei);
6512
+ const availableEth = (0, import_viem4.formatEther)(args.availableWei);
6513
+ super(
6514
+ `Insufficient ETH for gas: wallet ${args.address} has ${availableEth} ETH but this transaction needs ~${requiredEth} ETH.
6515
+ Compute credits do not pay on-chain gas \u2014 fund the wallet with ETH and retry.`
6516
+ );
6517
+ this.name = "InsufficientGasError";
6518
+ this.address = args.address;
6519
+ this.requiredWei = args.requiredWei;
6520
+ this.availableWei = args.availableWei;
6521
+ this.requiredEth = requiredEth;
6522
+ this.availableEth = availableEth;
6523
+ }
6524
+ };
6525
+ async function assertSufficientGas(args) {
6526
+ const availableWei = await args.publicClient.getBalance({ address: args.address });
6527
+ if (availableWei < args.gasEstimate.maxCostWei) {
6528
+ throw new InsufficientGasError({
6529
+ address: args.address,
6530
+ requiredWei: args.gasEstimate.maxCostWei,
6531
+ availableWei
6532
+ });
6533
+ }
6534
+ }
6535
+
6445
6536
  // src/client/common/utils/userapi.ts
6446
6537
  var import_axios2 = __toESM(require("axios"), 1);
6447
6538
 
6448
6539
  // src/client/common/utils/auth.ts
6449
- var import_viem4 = require("viem");
6450
- var APP_CONTROLLER_ABI = (0, import_viem4.parseAbi)([
6540
+ var import_viem5 = require("viem");
6541
+ var APP_CONTROLLER_ABI = (0, import_viem5.parseAbi)([
6451
6542
  "function calculateApiPermissionDigestHash(bytes4 permission, uint256 expiry) view returns (bytes32)"
6452
6543
  ]);
6453
6544
  async function calculatePermissionSignature(options) {
@@ -6505,6 +6596,7 @@ var import_axios = __toESM(require("axios"), 1);
6505
6596
  var MAX_RETRIES = 5;
6506
6597
  var INITIAL_BACKOFF_MS = 1e3;
6507
6598
  var MAX_BACKOFF_MS = 3e4;
6599
+ var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 502, 503, 504]);
6508
6600
  function sleep(ms) {
6509
6601
  return new Promise((resolve2) => setTimeout(resolve2, ms));
6510
6602
  }
@@ -6524,7 +6616,7 @@ async function requestWithRetry(config) {
6524
6616
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
6525
6617
  const res = await (0, import_axios.default)({ ...config, validateStatus: () => true });
6526
6618
  lastResponse = res;
6527
- if (res.status !== 429) {
6619
+ if (!RETRYABLE_STATUSES.has(res.status)) {
6528
6620
  return res;
6529
6621
  }
6530
6622
  if (attempt < MAX_RETRIES) {
@@ -6553,7 +6645,7 @@ var CanViewAppLogsPermission = "0x2fd3f2fe";
6553
6645
  var CanViewSensitiveAppInfoPermission = "0x0e67b22f";
6554
6646
  var CanUpdateAppProfilePermission = "0x036fef61";
6555
6647
  function getDefaultClientId() {
6556
- const version = true ? "1.0.0-dev.7" : "0.0.0";
6648
+ const version = true ? "1.0.0-dev.8" : "0.0.0";
6557
6649
  return `ecloud-sdk/v${version}`;
6558
6650
  }
6559
6651
  var UserApiClient = class {
@@ -6891,8 +6983,36 @@ function transformAppRelease(raw) {
6891
6983
 
6892
6984
  // src/client/common/contract/watcher.ts
6893
6985
  var WATCH_POLL_INTERVAL_SECONDS = 5;
6986
+ var WATCH_HEARTBEAT_INTERVAL_SECONDS = 30;
6987
+ var WATCH_DEFAULT_TIMEOUT_SECONDS = 10 * 60;
6894
6988
  var APP_STATUS_RUNNING = "Running";
6895
6989
  var APP_STATUS_FAILED = "Failed";
6990
+ var WatchTimeoutError = class extends Error {
6991
+ constructor(args) {
6992
+ super(
6993
+ args.message ?? `Timed out after ${args.elapsedSeconds}s waiting for app ${args.appId} (last status: ${args.lastStatus ?? "unknown"})`
6994
+ );
6995
+ this.name = "WatchTimeoutError";
6996
+ this.appId = args.appId;
6997
+ this.elapsedSeconds = args.elapsedSeconds;
6998
+ this.lastStatus = args.lastStatus;
6999
+ this.timeoutSeconds = args.timeoutSeconds;
7000
+ }
7001
+ };
7002
+ function resolveWatchTimeoutSeconds(explicit) {
7003
+ if (typeof explicit === "number" && Number.isFinite(explicit) && explicit > 0) {
7004
+ return Math.floor(explicit);
7005
+ }
7006
+ const raw = process.env.ECLOUD_WATCH_TIMEOUT_SECONDS;
7007
+ if (raw === void 0 || raw === "") {
7008
+ return WATCH_DEFAULT_TIMEOUT_SECONDS;
7009
+ }
7010
+ const parsed = Number(raw);
7011
+ if (!Number.isFinite(parsed) || parsed <= 0) {
7012
+ return WATCH_DEFAULT_TIMEOUT_SECONDS;
7013
+ }
7014
+ return Math.floor(parsed);
7015
+ }
6896
7016
  async function watchUntilRunning(options, logger) {
6897
7017
  const { walletClient, publicClient, environmentConfig, appId } = options;
6898
7018
  const userApiClient = new UserApiClient(environmentConfig, walletClient, publicClient);
@@ -6923,8 +7043,20 @@ async function watchUntilRunning(options, logger) {
6923
7043
  return false;
6924
7044
  };
6925
7045
  const startTime = Date.now();
7046
+ const timeoutSeconds = resolveWatchTimeoutSeconds(options.timeoutSeconds);
6926
7047
  let lastLoggedStatus;
7048
+ let lastHeartbeatAt = startTime;
6927
7049
  while (true) {
7050
+ const elapsedMs = Date.now() - startTime;
7051
+ const elapsed = Math.round(elapsedMs / 1e3);
7052
+ if (elapsed >= timeoutSeconds) {
7053
+ throw new WatchTimeoutError({
7054
+ appId,
7055
+ elapsedSeconds: elapsed,
7056
+ lastStatus: lastLoggedStatus,
7057
+ timeoutSeconds
7058
+ });
7059
+ }
6928
7060
  try {
6929
7061
  const info = await userApiClient.getInfos([appId], 1);
6930
7062
  if (info.length === 0) {
@@ -6934,16 +7066,22 @@ async function watchUntilRunning(options, logger) {
6934
7066
  const appInfo = info[0];
6935
7067
  const currentStatus = appInfo.status;
6936
7068
  const currentIP = appInfo.ip || "";
6937
- const elapsed = Math.round((Date.now() - startTime) / 1e3);
6938
7069
  if (currentStatus !== lastLoggedStatus) {
6939
7070
  logger.info(`Status: ${currentStatus} (${elapsed}s)`);
6940
7071
  lastLoggedStatus = currentStatus;
7072
+ lastHeartbeatAt = Date.now();
7073
+ } else if (Date.now() - lastHeartbeatAt >= WATCH_HEARTBEAT_INTERVAL_SECONDS * 1e3) {
7074
+ logger.info(`Status: ${currentStatus} (${elapsed}s)`);
7075
+ lastHeartbeatAt = Date.now();
6941
7076
  }
6942
7077
  if (stopCondition(currentStatus, currentIP)) {
6943
7078
  return currentIP || void 0;
6944
7079
  }
6945
7080
  await sleep2(WATCH_POLL_INTERVAL_SECONDS * 1e3);
6946
7081
  } catch (error) {
7082
+ if (error instanceof WatchTimeoutError) {
7083
+ throw error;
7084
+ }
6947
7085
  logger.warn(`Failed to fetch app info: ${error.message}`);
6948
7086
  await sleep2(WATCH_POLL_INTERVAL_SECONDS * 1e3);
6949
7087
  }
@@ -6952,6 +7090,7 @@ async function watchUntilRunning(options, logger) {
6952
7090
  var APP_STATUS_STOPPED = "Stopped";
6953
7091
  async function watchUntilUpgradeComplete(options, logger) {
6954
7092
  const { walletClient, publicClient, environmentConfig, appId } = options;
7093
+ const timeoutSeconds = resolveWatchTimeoutSeconds(options.timeoutSeconds);
6955
7094
  const userApiClient = new UserApiClient(environmentConfig, walletClient, publicClient);
6956
7095
  let initialStatus;
6957
7096
  let initialIP;
@@ -6989,7 +7128,20 @@ async function watchUntilUpgradeComplete(options, logger) {
6989
7128
  }
6990
7129
  return false;
6991
7130
  };
7131
+ const startTime = Date.now();
7132
+ const deadline = startTime + timeoutSeconds * 1e3;
7133
+ let lastLoggedStatus;
7134
+ let lastObservedStatus;
6992
7135
  while (true) {
7136
+ if (Date.now() >= deadline) {
7137
+ const elapsedSeconds = Math.round((Date.now() - startTime) / 1e3);
7138
+ throw new WatchTimeoutError({
7139
+ appId,
7140
+ lastStatus: lastObservedStatus,
7141
+ elapsedSeconds,
7142
+ timeoutSeconds
7143
+ });
7144
+ }
6993
7145
  try {
6994
7146
  const info = await userApiClient.getInfos([appId], 1);
6995
7147
  if (info.length === 0) {
@@ -6999,11 +7151,23 @@ async function watchUntilUpgradeComplete(options, logger) {
6999
7151
  const appInfo = info[0];
7000
7152
  const currentStatus = appInfo.status;
7001
7153
  const currentIP = appInfo.ip || "";
7154
+ lastObservedStatus = currentStatus;
7155
+ const elapsed = Math.round((Date.now() - startTime) / 1e3);
7156
+ if (currentStatus !== lastLoggedStatus) {
7157
+ logger.info(`Status: ${currentStatus} (${elapsed}s)`);
7158
+ lastLoggedStatus = currentStatus;
7159
+ }
7002
7160
  if (stopCondition(currentStatus, currentIP)) {
7003
7161
  return;
7004
7162
  }
7005
7163
  await sleep2(WATCH_POLL_INTERVAL_SECONDS * 1e3);
7006
7164
  } catch (error) {
7165
+ if (error instanceof WatchTimeoutError) {
7166
+ throw error;
7167
+ }
7168
+ if (typeof error?.message === "string" && error.message.includes("Failed")) {
7169
+ throw error;
7170
+ }
7007
7171
  logger.warn(`Failed to fetch app info: ${error.message}`);
7008
7172
  await sleep2(WATCH_POLL_INTERVAL_SECONDS * 1e3);
7009
7173
  }
@@ -7016,7 +7180,7 @@ function sleep2(ms) {
7016
7180
  // src/client/common/utils/validation.ts
7017
7181
  var import_fs = __toESM(require("fs"), 1);
7018
7182
  var import_path = __toESM(require("path"), 1);
7019
- var import_viem5 = require("viem");
7183
+ var import_viem6 = require("viem");
7020
7184
  function validateAppName(name) {
7021
7185
  if (!name) {
7022
7186
  throw new Error("App name cannot be empty");
@@ -7168,7 +7332,7 @@ function validateAppID(appID) {
7168
7332
  throw new Error("App ID is required");
7169
7333
  }
7170
7334
  const normalized = typeof appID === "string" ? addHexPrefix(appID) : appID;
7171
- if ((0, import_viem5.isAddress)(normalized)) {
7335
+ if ((0, import_viem6.isAddress)(normalized)) {
7172
7336
  return normalized;
7173
7337
  }
7174
7338
  throw new Error(`Invalid app ID: '${appID}' is not a valid address`);
@@ -8224,6 +8388,11 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
8224
8388
  executions: batch.executions,
8225
8389
  authorizationList
8226
8390
  });
8391
+ await assertSufficientGas({
8392
+ publicClient: batch.publicClient,
8393
+ address: batch.walletClient.account.address,
8394
+ gasEstimate
8395
+ });
8227
8396
  const data = {
8228
8397
  appId: batch.appId,
8229
8398
  salt: batch.salt,
@@ -8482,6 +8651,11 @@ async function prepareDeploy(options, logger = defaultLogger) {
8482
8651
  executions: batch.executions,
8483
8652
  authorizationList
8484
8653
  });
8654
+ await assertSufficientGas({
8655
+ publicClient: batch.publicClient,
8656
+ address: batch.walletClient.account.address,
8657
+ gasEstimate
8658
+ });
8485
8659
  const data = {
8486
8660
  appId: batch.appId,
8487
8661
  salt: batch.salt,
@@ -8518,7 +8692,7 @@ async function executeDeploy(options) {
8518
8692
  }
8519
8693
  );
8520
8694
  }
8521
- async function watchDeployment(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry) {
8695
+ async function watchDeployment(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry, opts) {
8522
8696
  return withSDKTelemetry(
8523
8697
  {
8524
8698
  functionName: "watchDeployment",
@@ -8534,7 +8708,8 @@ async function watchDeployment(appId, walletClient, publicClient, environmentCon
8534
8708
  walletClient,
8535
8709
  publicClient,
8536
8710
  environmentConfig,
8537
- appId
8711
+ appId,
8712
+ timeoutSeconds: opts?.timeoutSeconds
8538
8713
  },
8539
8714
  logger
8540
8715
  );
@@ -8630,6 +8805,11 @@ async function prepareUpgradeFromVerifiableBuild(options, logger = defaultLogger
8630
8805
  executions: batch.executions,
8631
8806
  authorizationList
8632
8807
  });
8808
+ await assertSufficientGas({
8809
+ publicClient: batch.publicClient,
8810
+ address: batch.walletClient.account.address,
8811
+ gasEstimate
8812
+ });
8633
8813
  const data = {
8634
8814
  appId: batch.appId,
8635
8815
  executions: batch.executions,
@@ -8819,6 +8999,11 @@ async function prepareUpgrade(options, logger = defaultLogger) {
8819
8999
  executions: batch.executions,
8820
9000
  authorizationList
8821
9001
  });
9002
+ await assertSufficientGas({
9003
+ publicClient: batch.publicClient,
9004
+ address: batch.walletClient.account.address,
9005
+ gasEstimate
9006
+ });
8822
9007
  const data = {
8823
9008
  appId: batch.appId,
8824
9009
  executions: batch.executions,
@@ -8853,7 +9038,7 @@ async function executeUpgrade(options) {
8853
9038
  }
8854
9039
  );
8855
9040
  }
8856
- async function watchUpgrade(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry) {
9041
+ async function watchUpgrade(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry, opts) {
8857
9042
  return withSDKTelemetry(
8858
9043
  {
8859
9044
  functionName: "watchUpgrade",
@@ -8869,7 +9054,8 @@ async function watchUpgrade(appId, walletClient, publicClient, environmentConfig
8869
9054
  walletClient,
8870
9055
  publicClient,
8871
9056
  environmentConfig,
8872
- appId
9057
+ appId,
9058
+ timeoutSeconds: opts?.timeoutSeconds
8873
9059
  },
8874
9060
  logger
8875
9061
  );
@@ -9527,27 +9713,27 @@ async function logs(options, walletClient, publicClient, environmentConfig, logg
9527
9713
  }
9528
9714
 
9529
9715
  // src/client/modules/compute/app/index.ts
9530
- var CONTROLLER_ABI = (0, import_viem6.parseAbi)([
9716
+ var CONTROLLER_ABI = (0, import_viem7.parseAbi)([
9531
9717
  "function startApp(address appId)",
9532
9718
  "function stopApp(address appId)",
9533
9719
  "function terminateApp(address appId)"
9534
9720
  ]);
9535
9721
  function encodeStartAppData(appId) {
9536
- return (0, import_viem6.encodeFunctionData)({
9722
+ return (0, import_viem7.encodeFunctionData)({
9537
9723
  abi: CONTROLLER_ABI,
9538
9724
  functionName: "startApp",
9539
9725
  args: [appId]
9540
9726
  });
9541
9727
  }
9542
9728
  function encodeStopAppData(appId) {
9543
- return (0, import_viem6.encodeFunctionData)({
9729
+ return (0, import_viem7.encodeFunctionData)({
9544
9730
  abi: CONTROLLER_ABI,
9545
9731
  functionName: "stopApp",
9546
9732
  args: [appId]
9547
9733
  });
9548
9734
  }
9549
9735
  function encodeTerminateAppData(appId) {
9550
- return (0, import_viem6.encodeFunctionData)({
9736
+ return (0, import_viem7.encodeFunctionData)({
9551
9737
  abi: CONTROLLER_ABI,
9552
9738
  functionName: "terminateApp",
9553
9739
  args: [appId]
@@ -9561,7 +9747,7 @@ function createAppModule(ctx) {
9561
9747
  }
9562
9748
  const account = walletClient.account;
9563
9749
  const environment = getEnvironmentConfig(ctx.environment);
9564
- const logger = getLogger(ctx.verbose);
9750
+ const logger = ctx.logger ?? getLogger(ctx.verbose);
9565
9751
  return {
9566
9752
  async create(opts) {
9567
9753
  return createApp(opts, logger);
@@ -9673,14 +9859,15 @@ function createAppModule(ctx) {
9673
9859
  imageRef: result.imageRef
9674
9860
  };
9675
9861
  },
9676
- async watchDeployment(appId) {
9862
+ async watchDeployment(appId, opts) {
9677
9863
  return watchDeployment(
9678
9864
  appId,
9679
9865
  walletClient,
9680
9866
  publicClient,
9681
9867
  environment,
9682
9868
  logger,
9683
- skipTelemetry
9869
+ skipTelemetry,
9870
+ opts
9684
9871
  );
9685
9872
  },
9686
9873
  // Granular upgrade control
@@ -9738,8 +9925,16 @@ function createAppModule(ctx) {
9738
9925
  imageRef: result.imageRef
9739
9926
  };
9740
9927
  },
9741
- async watchUpgrade(appId) {
9742
- return watchUpgrade(appId, walletClient, publicClient, environment, logger, skipTelemetry);
9928
+ async watchUpgrade(appId, opts) {
9929
+ return watchUpgrade(
9930
+ appId,
9931
+ walletClient,
9932
+ publicClient,
9933
+ environment,
9934
+ logger,
9935
+ skipTelemetry,
9936
+ opts
9937
+ );
9743
9938
  },
9744
9939
  // Profile management
9745
9940
  async setProfile(appId, profile) {
@@ -9790,7 +9985,7 @@ function createAppModule(ctx) {
9790
9985
  },
9791
9986
  async () => {
9792
9987
  const pendingMessage = `Starting app ${appId}...`;
9793
- const data = (0, import_viem6.encodeFunctionData)({
9988
+ const data = (0, import_viem7.encodeFunctionData)({
9794
9989
  abi: CONTROLLER_ABI,
9795
9990
  functionName: "startApp",
9796
9991
  args: [appId]
@@ -9822,7 +10017,7 @@ function createAppModule(ctx) {
9822
10017
  },
9823
10018
  async () => {
9824
10019
  const pendingMessage = `Stopping app ${appId}...`;
9825
- const data = (0, import_viem6.encodeFunctionData)({
10020
+ const data = (0, import_viem7.encodeFunctionData)({
9826
10021
  abi: CONTROLLER_ABI,
9827
10022
  functionName: "stopApp",
9828
10023
  args: [appId]
@@ -9854,7 +10049,7 @@ function createAppModule(ctx) {
9854
10049
  },
9855
10050
  async () => {
9856
10051
  const pendingMessage = `Terminating app ${appId}...`;
9857
- const data = (0, import_viem6.encodeFunctionData)({
10052
+ const data = (0, import_viem7.encodeFunctionData)({
9858
10053
  abi: CONTROLLER_ABI,
9859
10054
  functionName: "terminateApp",
9860
10055
  args: [appId]
@@ -9921,7 +10116,7 @@ function createComputeModule(config) {
9921
10116
  }
9922
10117
 
9923
10118
  // src/client/modules/billing/index.ts
9924
- var import_viem7 = require("viem");
10119
+ var import_viem8 = require("viem");
9925
10120
 
9926
10121
  // src/client/common/abis/USDCCredits.json
9927
10122
  var USDCCredits_default = [
@@ -10106,7 +10301,7 @@ function createBillingModule(config) {
10106
10301
  executions.push({
10107
10302
  target: usdcAddress,
10108
10303
  value: 0n,
10109
- callData: (0, import_viem7.encodeFunctionData)({
10304
+ callData: (0, import_viem8.encodeFunctionData)({
10110
10305
  abi: ERC20_default,
10111
10306
  functionName: "approve",
10112
10307
  args: [creditsAddress, opts.amount]
@@ -10116,7 +10311,7 @@ function createBillingModule(config) {
10116
10311
  executions.push({
10117
10312
  target: creditsAddress,
10118
10313
  value: 0n,
10119
- callData: (0, import_viem7.encodeFunctionData)({
10314
+ callData: (0, import_viem8.encodeFunctionData)({
10120
10315
  abi: USDCCredits_default,
10121
10316
  functionName: "purchaseCreditsFor",
10122
10317
  args: [opts.amount, targetAccount]
@@ -11253,6 +11448,7 @@ var JwtProvider = class {
11253
11448
  EMPTY_CONTAINER_POLICY,
11254
11449
  ERC20ABI,
11255
11450
  ForbiddenError,
11451
+ InsufficientGasError,
11256
11452
  JwtProvider,
11257
11453
  NoopClient,
11258
11454
  NotFoundError,
@@ -11262,9 +11458,12 @@ var JwtProvider = class {
11262
11458
  TimeoutError,
11263
11459
  USDCCreditsABI,
11264
11460
  UserApiClient,
11461
+ WATCH_DEFAULT_TIMEOUT_SECONDS,
11462
+ WatchTimeoutError,
11265
11463
  addHexPrefix,
11266
11464
  addMetric,
11267
11465
  addMetricWithDimensions,
11466
+ assertSufficientGas,
11268
11467
  assertValidFilePath,
11269
11468
  assertValidImageReference,
11270
11469
  assertValidPrivateKey,