@layr-labs/ecloud-sdk 1.0.0-devep7 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/compute.js CHANGED
@@ -139,8 +139,8 @@ import * as child_process from "child_process";
139
139
  import { promisify } from "util";
140
140
 
141
141
  // src/client/common/constants.ts
142
- import { sepolia, mainnet } from "viem/chains";
143
- var SUPPORTED_CHAINS = [mainnet, sepolia];
142
+ import { sepolia, mainnet, baseSepolia } from "viem/chains";
143
+ var SUPPORTED_CHAINS = [mainnet, sepolia, baseSepolia];
144
144
  var DOCKER_PLATFORM = "linux/amd64";
145
145
  var REGISTRY_PROPAGATION_WAIT_SECONDS = 3;
146
146
  var LAYERED_DOCKERFILE_NAME = "Dockerfile.eigencompute";
@@ -148,7 +148,6 @@ var ENV_SOURCE_SCRIPT_NAME = "compute-source-env.sh";
148
148
  var KMS_CLIENT_BINARY_NAME = "kms-client";
149
149
  var KMS_SIGNING_KEY_NAME = "kms-signing-public-key.pem";
150
150
  var TLS_KEYGEN_BINARY_NAME = "tls-keygen";
151
- var DRAIN_WATCHER_BINARY_NAME = "ecloud-drain-watcher";
152
151
  var CADDYFILE_NAME = "Caddyfile";
153
152
  var LAYERED_BUILD_DIR_PREFIX = "ecloud-layered-build";
154
153
 
@@ -435,12 +434,14 @@ USER root
435
434
  {{/if}}
436
435
 
437
436
  # Copy core TEE components
437
+ # CA bundle for kms-client / tls-keygen to validate HTTPS calls to
438
+ # eigencloud.xyz endpoints. Bundled at a non-standard path and consumed
439
+ # only via SSL_CERT_FILE in compute-source-env.sh, so the user's
440
+ # /etc/ssl/ is never touched.
441
+ COPY --from=alpine:3.20.10 /etc/ssl/certs/ca-certificates.crt /usr/local/share/eigenx-ca-certs.crt
438
442
  COPY compute-source-env.sh /usr/local/bin/
439
443
  COPY kms-client /usr/local/bin/
440
444
  COPY kms-signing-public-key.pem /usr/local/bin/
441
- {{#if includeDrainWatcher}}
442
- COPY ecloud-drain-watcher /usr/local/bin/
443
- {{/if}}
444
445
 
445
446
  {{#if includeTLS}}
446
447
  # Copy Caddy from official image
@@ -454,8 +455,7 @@ COPY Caddyfile /etc/caddy/
454
455
  {{#if originalUser}}
455
456
  # Make binaries executable (755 for executables, 644 for keys)
456
457
  RUN chmod 755 /usr/local/bin/compute-source-env.sh \\
457
- && chmod 755 /usr/local/bin/kms-client{{#if includeDrainWatcher}} \\
458
- && chmod 755 /usr/local/bin/ecloud-drain-watcher{{/if}}{{#if includeTLS}} \\
458
+ && chmod 755 /usr/local/bin/kms-client{{#if includeTLS}} \\
459
459
  && chmod 755 /usr/local/bin/tls-keygen \\
460
460
  && chmod 755 /usr/local/bin/caddy{{/if}} \\
461
461
  && chmod 644 /usr/local/bin/kms-signing-public-key.pem
@@ -465,8 +465,7 @@ ENV __ECLOUD_ORIGINAL_USER={{originalUser}}
465
465
  {{else}}
466
466
  # Make binaries executable (preserve existing permissions, just add execute)
467
467
  RUN chmod +x /usr/local/bin/compute-source-env.sh \\
468
- && chmod +x /usr/local/bin/kms-client{{#if includeDrainWatcher}} \\
469
- && chmod +x /usr/local/bin/ecloud-drain-watcher{{/if}}{{#if includeTLS}} \\
468
+ && chmod +x /usr/local/bin/kms-client{{#if includeTLS}} \\
470
469
  && chmod +x /usr/local/bin/tls-keygen{{/if}}
471
470
  {{/if}}
472
471
 
@@ -479,36 +478,8 @@ LABEL tee.launch_policy.log_redirect={{logRedirect}}
479
478
  LABEL tee.launch_policy.monitoring_memory_allow={{resourceUsageAllow}}
480
479
  {{/if}}
481
480
 
482
- # Allow-list the envvars the ecloud-platform sets via GCE \`tee-env-*\`
483
- # metadata. Without this label, Confidential Space's launcher rejects
484
- # any \`tee-env-*\` override at container-start with
485
- # "env var {...} is not allowed to be overridden on this image" and
486
- # exits with code 1 \u2014 which terminates the VM before the entrypoint
487
- # ever runs. User-supplied env vars flow through KMS (not tee-env-*)
488
- # and don't need to be listed here.
489
- #
490
- # Entries:
491
- # - ECLOUD_PD_EXPECTED set on PD-backed apps so the entrypoint
492
- # (compute-source-env.sh) knows to wait for
493
- # the persistent disk before exec'ing the
494
- # user workload.
495
- # - ECLOUD_PLATFORM_HOST the platform-routed hostname
496
- # (<addr>.<env>.eigencloud.xyz) so the
497
- # entrypoint's setup_tls can issue an ACME
498
- # cert for it. Injected by the CLI into
499
- # publicEnv at deploy/upgrade time and
500
- # propagated by ecloud-platform's
501
- # compute.go as a tee-env-* metadata key.
502
- #
503
- # The CS launcher parses this label as a comma-separated list
504
- # (go-tpm-tools/launcher/spec/launch_policy.go:185 \u2014 strings.Split
505
- # on ","). Quotes are not required; keep the value bare for
506
- # consistency with compute-tee's and eigenx-kms's existing labels.
507
- LABEL tee.launch_policy.allow_env_override=ECLOUD_PD_EXPECTED,ECLOUD_PLATFORM_HOST
508
-
509
481
  LABEL eigenx_cli_version={{ecloudCLIVersion}}
510
482
  LABEL eigenx_vm_image=eigen
511
- LABEL eigenx_container_contract=v1
512
483
 
513
484
  {{#if includeTLS}}
514
485
  # Expose both HTTP and HTTPS ports for Caddy
@@ -530,54 +501,11 @@ import Handlebars2 from "handlebars";
530
501
 
531
502
  // src/client/common/templates/compute-source-env.sh.tmpl
532
503
  var compute_source_env_sh_default = `#!/bin/sh
533
- # EigenCompute container entrypoint script
534
- # This script handles KMS secret fetching, TLS setup, and privilege dropping
535
- # before executing the user's application.
536
- #
537
- # Handlebars template variables (replaced at build time by the CLI):
538
- # kmsServerURL - URL of the KMS server
539
- # userAPIURL - URL of the user API (ecloud-platform)
540
- # The KMS signing public key is copied into the image as
541
- # /usr/local/bin/kms-signing-public-key.pem at layer-build time by the CLI.
542
- #
543
- # ecloud-platform divergence from compute-tee:
544
- # This script emits ECLOUD_READY / ECLOUD_FAIL / ECLOUD_AWAITING_USERDATA /
545
- # ECLOUD_DETACHED markers to stdout at key lifecycle points. The GCP
546
- # provisioner's serial-console watcher in ecloud-platform
547
- # (pkg/services/infraService/providers/gcp/compute.go) parses those
548
- # markers to gate "VM ready" and to coordinate the prewarm-detach
549
- # upgrade flow. Without the markers, the platform's waitForStartupReady
550
- # times out at ~10 minutes per deploy, rollback fires, and the VM is
551
- # deleted \u2014 seen in dev on 2026-05-04 with an older copy of this
552
- # template that lacked the markers.
553
- #
554
- # Prewarm-detach contract:
555
- # - If ECLOUD_PD_EXPECTED=1 and /mnt/disks/userdata is not present at boot,
556
- # emit ECLOUD_AWAITING_USERDATA and wait until the disk is attached.
557
- # - On SIGTERM (drain-requested), forward to child, wait for exit, sync
558
- # + unmount /mnt/disks/userdata, emit ECLOUD_DETACHED, exit.
559
- # - ECLOUD_READY is emitted once runtime is bootstrapped (same as before).
560
- # - ECLOUD_FAIL is emitted on any unrecoverable setup error.
561
- # Keep the markers on any line that resolves a lifecycle outcome.
562
- #
563
- # This file is kept in lockstep with
564
- # ecloud-platform/pkg/services/buildService/assets/compute-source-env.sh.tmpl
565
- # \u2014 if you change one, change the other. Differences vs the platform copy
566
- # are intentionally minimal:
567
- # - Handlebars placeholders use the CLI's naming (kmsServerURL,
568
- # userAPIURL) rather than the platform's (KMS_SERVER_URL,
569
- # USER_API_URL). (See top of file for real placeholder syntax \u2014
570
- # not repeated here so Handlebars doesn't expand it in this comment.)
571
- # - KMS signing key is read from a file the CLI copies into the image,
572
- # not heredoc-embedded in the script, because the CLI's image
573
- # layering writes it as a separate file (kms-signing-public-key.pem).
574
- # - TLS binary is \`tls-keygen\` (CLI-bundled) not \`tls-client\`.
575
-
576
504
  echo "compute-source-env.sh: Running setup script..."
577
505
 
578
506
  # Fetch and source environment variables from KMS
579
507
  echo "Fetching secrets from KMS..."
580
- if /usr/local/bin/kms-client \\
508
+ if SSL_CERT_FILE=/usr/local/share/eigenx-ca-certs.crt /usr/local/bin/kms-client \\
581
509
  --kms-server-url "{{kmsServerURL}}" \\
582
510
  --kms-signing-key-file /usr/local/bin/kms-signing-public-key.pem \\
583
511
  --userapi-url "{{userAPIURL}}" \\
@@ -588,187 +516,93 @@ if /usr/local/bin/kms-client \\
588
516
  else
589
517
  echo "compute-source-env.sh: ERROR - Failed to fetch environment variables from KMS"
590
518
  echo "compute-source-env.sh: Exiting - cannot start user workload without KMS secrets"
591
- echo "ECLOUD_FAIL kms_bootstrap"
592
519
  exit 1
593
520
  fi
594
521
 
595
- # issue_cert_for runs tls-keygen for a single hostname and copies the
596
- # produced fullchain/privkey into $1's output directory ($2). Returns
597
- # 0 on success, non-zero on any failure (caller decides whether that's
598
- # fatal). tls-keygen writes to /run/tls/{fullchain,privkey}.pem by
599
- # default; we move those to a per-host subdirectory so the two-site
600
- # Caddyfile can select the right cert by path.
601
- issue_cert_for() {
602
- local host="$1"
603
- local cert_dir="$2"
604
- local mnemonic="$3"
605
- local challenge="$4"
606
- local staging_flag="$5"
607
-
608
- echo "compute-source-env.sh: Obtaining TLS certificate for $host (challenge=$challenge)..."
609
- # Remove any stale default outputs from a prior tls-keygen run
610
- rm -f /run/tls/fullchain.pem /run/tls/privkey.pem
611
- if ! MNEMONIC="$mnemonic" DOMAIN="$host" API_URL="{{userAPIURL}}" /usr/local/bin/tls-keygen \\
612
- -challenge "$challenge" \\
613
- $staging_flag; then
614
- return 1
615
- fi
616
- mkdir -p "$cert_dir"
617
- mv /run/tls/fullchain.pem "$cert_dir/fullchain.pem"
618
- mv /run/tls/privkey.pem "$cert_dir/privkey.pem"
619
- echo "compute-source-env.sh: Cert for $host written to $cert_dir"
620
- }
621
-
622
- # Setup TLS: issues certs for
623
- # - ECLOUD_PLATFORM_HOST (platform-routed <addr>.<env>.eigencloud.xyz),
624
- # when the CLI/platform has set it
625
- # - DOMAIN (user-supplied custom domain), when set and non-localhost
626
- #
627
- # No client-side DNS precheck. Earlier versions tried to gate ACME on
628
- # "does this hostname resolve to my external IP" but that's wrong for
629
- # the platform-routing model (DNS points at the shared nginx NLB, not
630
- # the VM) and was preventing cert issuance on the production path.
631
- # tls-client (eigencompute-containers/tls-client) does its own DNS
632
- # poll before calling ACME and surfaces a clear error when challenges
633
- # can't reach the VM, which is the right place for that check \u2014
634
- # attempting it here from inside the VM cannot tell platform-routed
635
- # from compute-tee-routed apps.
522
+ # Setup TLS if tls-keygen is present (which means TLS was configured at build time)
636
523
  setup_tls() {
637
524
  # If tls-keygen isn't present, TLS wasn't configured during build
638
525
  if [ ! -x /usr/local/bin/tls-keygen ]; then
639
526
  echo "compute-source-env.sh: TLS not configured (no tls-keygen binary)"
640
527
  return 0
641
528
  fi
642
-
643
- local platform_host="\${ECLOUD_PLATFORM_HOST:-}"
644
- local user_domain="\${DOMAIN:-}"
529
+
530
+ local domain="\${DOMAIN:-}"
645
531
  local mnemonic="\${MNEMONIC:-}"
646
-
647
- # Normalize "localhost" DOMAIN to unset \u2014 compute-tee apps often
648
- # set DOMAIN=localhost as a sentinel for "no custom domain".
649
- if [ "$user_domain" = "localhost" ]; then
650
- user_domain=""
651
- fi
652
-
653
- # If the user set DOMAIN to the same value as the platform
654
- # hostname, collapse them. Without this we'd burn a second ACME
655
- # issuance for the exact same name (each VM boot counts against
656
- # Let's Encrypt's 5-certs-per-domain-per-week limit), and Caddy
657
- # would then refuse to start because two site blocks bound to
658
- # the same address is a config error. Also unset DOMAIN so the
659
- # DOMAIN site block in the Caddyfile falls back to its
660
- # "localhost.user.invalid" placeholder and stays dormant.
661
- if [ -n "$platform_host" ] && [ "$user_domain" = "$platform_host" ]; then
662
- echo "compute-source-env.sh: DOMAIN matches ECLOUD_PLATFORM_HOST; skipping duplicate cert"
663
- user_domain=""
664
- unset DOMAIN
665
- fi
666
-
667
- if [ -z "$platform_host" ] && [ -z "$user_domain" ]; then
668
- echo "compute-source-env.sh: TLS skipped (neither ECLOUD_PLATFORM_HOST nor DOMAIN set)"
669
- return 0
532
+
533
+ # Since tls-keygen is present, TLS is expected - validate requirements
534
+ if [ -z "$domain" ] || [ "$domain" = "localhost" ]; then
535
+ echo "compute-source-env.sh: ERROR - TLS binary present but DOMAIN not configured or is localhost"
536
+ echo "compute-source-env.sh: Set DOMAIN environment variable to a valid domain"
537
+ exit 1
670
538
  fi
671
-
539
+
672
540
  if [ -z "$mnemonic" ]; then
673
- echo "compute-source-env.sh: ERROR - TLS requested but MNEMONIC not available"
674
- echo "ECLOUD_FAIL tls_mnemonic_missing"
541
+ echo "compute-source-env.sh: ERROR - TLS binary present but MNEMONIC not available"
542
+ echo "compute-source-env.sh: Cannot obtain TLS certificate without mnemonic"
675
543
  exit 1
676
544
  fi
677
-
545
+
678
546
  if [ ! -x /usr/local/bin/caddy ]; then
679
- echo "compute-source-env.sh: ERROR - TLS requested but Caddy not found"
680
- echo "ECLOUD_FAIL tls_caddy_missing"
547
+ echo "compute-source-env.sh: ERROR - TLS binary present but Caddy not found"
681
548
  exit 1
682
549
  fi
683
-
550
+
551
+ echo "compute-source-env.sh: Setting up TLS for domain: $domain"
552
+
553
+ # Obtain TLS certificate using ACME
554
+ # Default to http-01, but allow override via ACME_CHALLENGE env var
684
555
  local challenge="\${ACME_CHALLENGE:-http-01}"
556
+
557
+ # Check if we should use staging (for testing)
685
558
  local staging_flag=""
686
559
  if [ "\${ACME_STAGING:-false}" = "true" ]; then
687
560
  staging_flag="-staging"
688
- echo "compute-source-env.sh: Using Let's Encrypt STAGING environment"
689
- fi
690
-
691
- local certs_issued=0
692
-
693
- if [ -n "$platform_host" ]; then
694
- if issue_cert_for "$platform_host" "/run/tls/platform" "$mnemonic" "$challenge" "$staging_flag"; then
695
- certs_issued=$((certs_issued + 1))
696
- else
697
- echo "compute-source-env.sh: ERROR - failed to issue cert for platform host $platform_host"
698
- echo "ECLOUD_FAIL tls_setup"
699
- exit 1
700
- fi
701
- fi
702
-
703
- if [ -n "$user_domain" ]; then
704
- if issue_cert_for "$user_domain" "/run/tls/domain" "$mnemonic" "$challenge" "$staging_flag"; then
705
- certs_issued=$((certs_issued + 1))
706
- else
707
- echo "compute-source-env.sh: ERROR - failed to issue cert for user domain $user_domain"
708
- echo "ECLOUD_FAIL tls_setup"
709
- exit 1
710
- fi
561
+ echo "compute-source-env.sh: Using Let's Encrypt STAGING environment (certificates won't be trusted)"
711
562
  fi
712
-
713
- if [ "$certs_issued" -eq 0 ]; then
714
- echo "compute-source-env.sh: no certs issued, skipping Caddy (app will serve plaintext until DNS is wired)"
715
- return 0
563
+
564
+ echo "compute-source-env.sh: Obtaining TLS certificate using $challenge challenge..."
565
+ # Pass the API URL for certificate persistence
566
+ if ! SSL_CERT_FILE=/usr/local/share/eigenx-ca-certs.crt \\
567
+ MNEMONIC="$mnemonic" DOMAIN="$domain" API_URL="{{userAPIURL}}" /usr/local/bin/tls-keygen \\
568
+ -challenge "$challenge" \\
569
+ $staging_flag; then
570
+ echo "compute-source-env.sh: ERROR - Failed to obtain TLS certificate"
571
+ echo "compute-source-env.sh: Certificate issuance failed for $domain"
572
+ exit 1
716
573
  fi
717
-
718
- # Caddy's validate step checks that every \`tls <cert> <key>\` file
719
- # exists, even on site blocks bound to dormant placeholder
720
- # hostnames. The default Caddyfile declares both a platform site
721
- # and a user-domain site; when only one is configured, the other
722
- # block's cert paths are never populated and validate fails with
723
- # "Invalid Caddyfile". Point the unused block at the issued
724
- # block's cert files so validate passes \u2014 the dormant block can't
725
- # receive real traffic (its hostname falls back to
726
- # localhost.{platform,user}.invalid, which Caddy routes by SNI and
727
- # never matches public traffic), so the symlink is never actually
728
- # presented. Skipped when a user-supplied Caddyfile is in use,
729
- # since we don't know what cert paths it references.
730
- if [ -d /run/tls/platform ] && [ ! -e /run/tls/domain/fullchain.pem ]; then
731
- mkdir -p /run/tls/domain
732
- ln -sf /run/tls/platform/fullchain.pem /run/tls/domain/fullchain.pem
733
- ln -sf /run/tls/platform/privkey.pem /run/tls/domain/privkey.pem
734
- elif [ -d /run/tls/domain ] && [ ! -e /run/tls/platform/fullchain.pem ]; then
735
- mkdir -p /run/tls/platform
736
- ln -sf /run/tls/domain/fullchain.pem /run/tls/platform/fullchain.pem
737
- ln -sf /run/tls/domain/privkey.pem /run/tls/platform/privkey.pem
574
+
575
+ echo "compute-source-env.sh: TLS certificate obtained successfully"
576
+
577
+ # Validate Caddyfile before starting
578
+ if ! /usr/local/bin/caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile 2>/dev/null; then
579
+ echo "compute-source-env.sh: ERROR - Invalid Caddyfile"
580
+ echo "compute-source-env.sh: TLS was requested (DOMAIN=$domain) but setup failed"
581
+ exit 1
738
582
  fi
739
-
740
- # Validate Caddyfile before starting. Don't redirect stderr \u2014 when
741
- # validate fails, Caddy's diagnostic is the only signal that lands
742
- # in ReadinessError.SerialTail, so silencing it leaves operators
743
- # staring at a bare "tls_invalid_caddyfile" with no detail.
744
- if [ -f /etc/caddy/Caddyfile ]; then
745
- if ! /usr/local/bin/caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile; then
746
- echo "compute-source-env.sh: ERROR - Invalid Caddyfile"
747
- echo "ECLOUD_FAIL tls_invalid_caddyfile"
583
+
584
+ # Start Caddy in background
585
+ echo "compute-source-env.sh: Starting Caddy reverse proxy..."
586
+
587
+ # Check if Caddy logs should be enabled
588
+ if [ "\${ENABLE_CADDY_LOGS:-false}" = "true" ]; then
589
+ if ! /usr/local/bin/caddy start --config /etc/caddy/Caddyfile --adapter caddyfile 2>&1; then
590
+ echo "compute-source-env.sh: ERROR - Failed to start Caddy"
591
+ echo "compute-source-env.sh: TLS was requested (DOMAIN=$domain) but setup failed"
748
592
  exit 1
749
593
  fi
750
-
751
- echo "compute-source-env.sh: Starting Caddy reverse proxy..."
752
- if [ "\${ENABLE_CADDY_LOGS:-false}" = "true" ]; then
753
- if ! /usr/local/bin/caddy start --config /etc/caddy/Caddyfile --adapter caddyfile 2>&1; then
754
- echo "compute-source-env.sh: ERROR - Failed to start Caddy"
755
- echo "ECLOUD_FAIL tls_caddy_start"
756
- exit 1
757
- fi
758
- else
759
- if ! /usr/local/bin/caddy start --config /etc/caddy/Caddyfile --adapter caddyfile >/dev/null 2>&1; then
760
- echo "compute-source-env.sh: ERROR - Failed to start Caddy"
761
- echo "ECLOUD_FAIL tls_caddy_start"
762
- exit 1
763
- fi
764
- fi
765
-
766
- sleep 2
767
- echo "compute-source-env.sh: Caddy started successfully"
768
594
  else
769
- echo "compute-source-env.sh: No Caddyfile found, skipping Caddy"
595
+ # Redirect Caddy output to /dev/null to silence logs
596
+ if ! /usr/local/bin/caddy start --config /etc/caddy/Caddyfile --adapter caddyfile >/dev/null 2>&1; then
597
+ echo "compute-source-env.sh: ERROR - Failed to start Caddy"
598
+ echo "compute-source-env.sh: TLS was requested (DOMAIN=$domain) but setup failed"
599
+ exit 1
600
+ fi
770
601
  fi
771
-
602
+
603
+ # Give Caddy a moment to fully initialize
604
+ sleep 2
605
+ echo "compute-source-env.sh: Caddy started successfully"
772
606
  return 0
773
607
  }
774
608
 
@@ -779,233 +613,15 @@ setup_tls
779
613
  export KMS_SERVER_URL="{{kmsServerURL}}"
780
614
  export KMS_PUBLIC_KEY="$(cat /usr/local/bin/kms-signing-public-key.pem)"
781
615
 
782
- # \u2500\u2500 Prewarm-detach: wait for PD if expected \u2500\u2500
783
- # Orchestrator sets ECLOUD_PD_EXPECTED=1 on apps using StorageBackend=pd.
784
- # When the prewarm path is used, the new VM boots WITHOUT the disk; we
785
- # signal awaiting-userdata and poll until the disk is attached.
786
- USERDATA_MOUNT="/mnt/disks/userdata"
787
- USERDATA_DEV="/dev/disk/by-id/google-persistent_storage_1"
788
-
789
- wait_for_userdata() {
790
- if [ "\${ECLOUD_PD_EXPECTED:-0}" != "1" ]; then
791
- return 0
792
- fi
793
- if mountpoint -q "$USERDATA_MOUNT" 2>/dev/null; then
794
- echo "compute-source-env.sh: userdata already mounted at $USERDATA_MOUNT"
795
- return 0
796
- fi
797
- # Refuse to proceed if the tools we need for safe first-attach
798
- # detection are missing. Without blkid we cannot tell an empty new
799
- # disk from an already-formatted one \u2014 running mkfs.ext4 on the
800
- # latter would destroy data.
801
- if ! command -v blkid >/dev/null 2>&1; then
802
- echo "ECLOUD_FAIL pd_tools_missing"
803
- exit 1
804
- fi
805
- echo "ECLOUD_AWAITING_USERDATA"
806
- echo "compute-source-env.sh: waiting for PD at $USERDATA_DEV..."
807
- # Poll for up to 10 minutes (120 * 5s). The orchestrator's overall
808
- # attach timeout is shorter; the ceiling here just bounds the wait
809
- # for manual / diagnostic scenarios.
810
- local i=0
811
- local mount_failures=0
812
- while [ "$i" -lt 120 ]; do
813
- if [ -e "$USERDATA_DEV" ]; then
814
- mkdir -p "$USERDATA_MOUNT"
815
- if mount -o noatime "$USERDATA_DEV" "$USERDATA_MOUNT" 2>/dev/null; then
816
- echo "compute-source-env.sh: PD mounted at $USERDATA_MOUNT"
817
- return 0
818
- fi
819
- # Disk present but mount failed. Check whether it has a
820
- # recognized filesystem. \`blkid -s TYPE -o value\` prints the
821
- # FS type (empty if none). We only mkfs when there is
822
- # demonstrably NO filesystem \u2014 never on the basis of blkid
823
- # returning non-zero alone, which could mean "blkid missing"
824
- # or "device busy".
825
- local fstype
826
- fstype=$(blkid -s TYPE -o value "$USERDATA_DEV" 2>/dev/null)
827
- if [ -z "$fstype" ]; then
828
- echo "compute-source-env.sh: formatting $USERDATA_DEV (first attach)"
829
- mkfs.ext4 -F -L eclouddata "$USERDATA_DEV" >/dev/null 2>&1 || {
830
- echo "ECLOUD_FAIL pd_mkfs_failed"
831
- exit 1
832
- }
833
- mount -o noatime "$USERDATA_DEV" "$USERDATA_MOUNT" || {
834
- echo "ECLOUD_FAIL pd_mount_after_format_failed"
835
- exit 1
836
- }
837
- return 0
838
- fi
839
- # Disk has a filesystem but mount still failed. Give it a
840
- # few retries to cover transient cases (device busy, udev
841
- # still settling), but don't pretend this is an attach
842
- # timeout if it persists.
843
- mount_failures=$((mount_failures + 1))
844
- if [ "$mount_failures" -ge 6 ]; then
845
- echo "ECLOUD_FAIL pd_mount_failed"
846
- exit 1
847
- fi
848
- else
849
- # Device disappeared (e.g. udev re-enumeration between
850
- # attach and mount). Reset the consecutive-failure counter
851
- # so only true back-to-back mount failures trip
852
- # pd_mount_failed; a device blip should not steal retries.
853
- mount_failures=0
854
- fi
855
- i=$((i + 1))
856
- sleep 5
857
- done
858
- echo "ECLOUD_FAIL pd_attach_timeout"
859
- exit 1
860
- }
861
-
862
- wait_for_userdata
863
-
864
- # \u2500\u2500 Prewarm-detach: install SIGTERM handler for graceful drain \u2500\u2500
865
- # Orchestrator signals drain by setting the instance metadata key
866
- # ECLOUD_DRAIN_REQUESTED=1, which a host-level agent translates into
867
- # SIGTERM on PID 1. On SIGTERM we:
868
- # 1. Forward to the child (wakes the user's app for graceful exit)
869
- # 2. Wait for child exit
870
- # 3. Sync + unmount the PD
871
- # 4. Emit ECLOUD_DETACHED so the orchestrator can proceed to detach
872
- CHILD_PID=""
873
- _DRAIN_IN_PROGRESS=0
874
-
875
- drain_handler() {
876
- # Guard against re-entry if SIGTERM arrives twice (e.g. both the
877
- # drain_watcher and an external signal fire in quick succession).
878
- if [ "$_DRAIN_IN_PROGRESS" = "1" ]; then
879
- return 0
880
- fi
881
- _DRAIN_IN_PROGRESS=1
882
- echo "compute-source-env.sh: received drain signal, forwarding to child pgid=$CHILD_PID"
883
- if [ -n "$CHILD_PID" ]; then
884
- # Send to the process group so intermediate wrappers (su, sh -c,
885
- # etc.) don't swallow the signal. The leading \`-\` targets the
886
- # pgid, which equals the direct child's pid for a shell-backgrounded
887
- # process. Fall back to the pid alone if pgid signaling fails
888
- # (e.g. kernel older than 3.9 or PID namespace edge cases).
889
- kill -TERM -"$CHILD_PID" 2>/dev/null || kill -TERM "$CHILD_PID" 2>/dev/null || true
890
- # Give the app up to 30s to exit cleanly.
891
- local i=0
892
- while [ "$i" -lt 30 ] && kill -0 "$CHILD_PID" 2>/dev/null; do
893
- i=$((i + 1))
894
- sleep 1
895
- done
896
- if kill -0 "$CHILD_PID" 2>/dev/null; then
897
- echo "compute-source-env.sh: child did not exit in 30s, sending SIGKILL"
898
- kill -KILL -"$CHILD_PID" 2>/dev/null || kill -KILL "$CHILD_PID" 2>/dev/null || true
899
- # Reap the process so its in-flight I/O is flushed to the
900
- # filesystem before we sync + unmount. SIGKILL schedules
901
- # death; wait guarantees it's complete.
902
- wait "$CHILD_PID" 2>/dev/null || true
903
- fi
904
- fi
905
- if [ "\${ECLOUD_PD_EXPECTED:-0}" = "1" ] && mountpoint -q "$USERDATA_MOUNT" 2>/dev/null; then
906
- sync
907
- if umount "$USERDATA_MOUNT" 2>/dev/null; then
908
- echo "compute-source-env.sh: unmounted $USERDATA_MOUNT cleanly"
909
- else
910
- # Force lazy unmount as last resort \u2014 orchestrator still needs
911
- # the DETACHED signal to proceed.
912
- umount -l "$USERDATA_MOUNT" 2>/dev/null || true
913
- echo "compute-source-env.sh: WARNING - used lazy unmount on $USERDATA_MOUNT"
914
- fi
915
- # ECLOUD_DETACHED is strictly a PD-lifecycle signal. Only emit
916
- # it when we actually had a PD mount in play, so serial-log
917
- # parsers and alerting for non-PD apps don't see spurious
918
- # lifecycle markers on routine container SIGTERM.
919
- echo "ECLOUD_DETACHED"
920
- fi
921
- # Always exit 0: drain is a managed shutdown and the orchestrator
922
- # waits on ECLOUD_DETACHED, not the container exit code. Forwarding
923
- # the child's exit status here would make a crash-during-drain look
924
- # like a drain failure to whatever reads the container exit code.
925
- exit 0
926
- }
927
- trap drain_handler TERM
928
-
929
- # \u2500\u2500 Prewarm-detach: background drain watcher \u2500\u2500
930
- # Container metadata delivery in Confidential Space is limited, so we
931
- # poll the instance metadata server for ECLOUD_DRAIN_REQUESTED and
932
- # raise SIGTERM on ourselves when it flips to "1".
933
- #
934
- # Try wget first (present in most Alpine bases), fall back to curl.
935
- # If neither is present, drain watcher is disabled \u2014 the orchestrator
936
- # will hit its drain timeout and fail the upgrade explicitly, which is
937
- # the correct behavior (we cannot silently ignore a drain request).
938
- _fetch_drain_flag() {
939
- local url="http://metadata.google.internal/computeMetadata/v1/instance/attributes/ECLOUD_DRAIN_REQUESTED"
940
- if command -v wget >/dev/null 2>&1; then
941
- wget -q --tries=1 --timeout=2 --header='Metadata-Flavor: Google' -O - "$url" 2>/dev/null
942
- elif command -v curl >/dev/null 2>&1; then
943
- curl -sf --max-time 2 -H 'Metadata-Flavor: Google' "$url" 2>/dev/null
944
- else
945
- return 2
946
- fi
947
- }
948
-
949
- drain_watcher() {
950
- # Preflight: confirm we have an HTTP client
951
- if ! _fetch_drain_flag >/dev/null 2>&1; then
952
- # Either no http client available OR metadata server not
953
- # responding yet. If no client, give up and log; otherwise the
954
- # loop below will retry.
955
- if ! command -v wget >/dev/null 2>&1 && ! command -v curl >/dev/null 2>&1; then
956
- echo "compute-source-env.sh: WARNING - no wget/curl; drain_watcher disabled"
957
- return 0
958
- fi
959
- fi
960
- while true; do
961
- local v
962
- v=$(_fetch_drain_flag || true)
963
- if [ "$v" = "1" ]; then
964
- echo "compute-source-env.sh: drain_watcher saw ECLOUD_DRAIN_REQUESTED=1, signaling PID 1"
965
- # The CS launcher runs this script directly as PID 1, so
966
- # kill -TERM 1 delivers SIGTERM to the shell that installed
967
- # the drain_handler trap. If the launch mechanism ever
968
- # wraps this script in another process, this assumption
969
- # breaks and drain will silently no-op \u2014 audit here.
970
- kill -TERM 1 2>/dev/null || true
971
- return 0
972
- fi
973
- sleep 2
974
- done
975
- }
976
-
977
- if [ "\${ECLOUD_PD_EXPECTED:-0}" = "1" ]; then
978
- # Assumption: the orchestrator only flips ECLOUD_DRAIN_REQUESTED=1
979
- # after observing ECLOUD_AWAITING_USERDATA (old VM) or
980
- # ECLOUD_READY (new VM), so CHILD_PID is always set by the time
981
- # drain_handler fires. If drain somehow arrived in the tiny window
982
- # between this watcher spawn and CHILD_PID assignment below,
983
- # drain_handler would skip the child-kill branch and still emit
984
- # ECLOUD_DETACHED \u2014 harmless because there's nothing to drain yet.
985
- if [ -x /usr/local/bin/ecloud-drain-watcher ]; then
986
- /usr/local/bin/ecloud-drain-watcher &
987
- else
988
- drain_watcher &
989
- fi
990
- fi
991
-
992
616
  echo "compute-source-env.sh: Environment sourced."
993
- echo "ECLOUD_READY runtime_bootstrapped"
994
617
 
995
618
  # Drop privileges to original user for the application command
996
619
  if [ -n "$__ECLOUD_ORIGINAL_USER" ] && [ "$(id -u)" = "0" ]; then
997
620
  echo "compute-source-env.sh: Dropping privileges to user: $__ECLOUD_ORIGINAL_USER"
998
- # Must background the child so our trap can fire; exec replaces PID 1.
999
- su -s /bin/sh "$__ECLOUD_ORIGINAL_USER" -c 'exec "$@"' -- sh "$@" &
1000
- CHILD_PID=$!
1001
- wait "$CHILD_PID"
1002
- exit $?
621
+ exec su -s /bin/sh "$__ECLOUD_ORIGINAL_USER" -c 'exec "$@"' -- sh "$@"
1003
622
  fi
1004
623
 
1005
- "$@" &
1006
- CHILD_PID=$!
1007
- wait "$CHILD_PID"
1008
- exit $?
624
+ exec "$@"
1009
625
  `;
1010
626
 
1011
627
  // src/client/common/templates/scriptTemplate.ts
@@ -1014,14 +630,6 @@ function processScriptTemplate(data) {
1014
630
  return template(data);
1015
631
  }
1016
632
 
1017
- // src/client/common/templates/Caddyfile.default.tmpl
1018
- var Caddyfile_default_default = '# Caddy configuration for automatic HTTPS\n#\n# Two sites can be configured at runtime via env vars that\n# ecloud-platform / the CLI inject into tee-env metadata:\n#\n# ECLOUD_PLATFORM_HOST \u2014 the platform-derived hostname\n# (<addr>.<env>.eigencloud.xyz). Always set\n# for platform-routed apps. Cert at\n# /run/tls/platform/fullchain.pem.\n# DOMAIN \u2014 optional user-supplied custom domain.\n# Cert at /run/tls/domain/fullchain.pem.\n#\n# When a variable is unset, Caddy substitutes the default listed\n# after the colon. We use distinct defaults per site so each block\n# binds to a unique name at bootstrap \u2014 both blocks overlapping\n# on "localhost" would cause Caddy to reject the config.\n{$ECLOUD_PLATFORM_HOST:localhost.platform.invalid} {\n tls /run/tls/platform/fullchain.pem /run/tls/platform/privkey.pem\n\n reverse_proxy localhost:{$APP_PORT:3000} {\n health_uri /health\n health_interval 30s\n health_timeout 5s\n health_status 200\n }\n\n header {\n X-Content-Type-Options "nosniff"\n X-Frame-Options "DENY"\n X-XSS-Protection "1; mode=block"\n Referrer-Policy "strict-origin-when-cross-origin"\n -Server\n }\n\n log {\n output stdout\n format console\n level INFO\n }\n\n request_body {\n max_size 10MB\n }\n}\n\n{$DOMAIN:localhost.user.invalid} {\n tls /run/tls/domain/fullchain.pem /run/tls/domain/privkey.pem\n\n reverse_proxy localhost:{$APP_PORT:3000} {\n health_uri /health\n health_interval 30s\n health_timeout 5s\n health_status 200\n }\n\n header {\n X-Content-Type-Options "nosniff"\n X-Frame-Options "DENY"\n X-XSS-Protection "1; mode=block"\n Referrer-Policy "strict-origin-when-cross-origin"\n -Server\n }\n\n log {\n output stdout\n format console\n level INFO\n }\n\n request_body {\n max_size 10MB\n }\n}\n\n# HTTP endpoint (optional, for health checks or redirects)\n:80 {\n # Redirect to HTTPS for any real (non-placeholder) host\n @for_domain expression {host} != "localhost.platform.invalid" && {host} != "localhost.user.invalid" && {host} != "localhost"\n redir @for_domain https://{host}{uri} permanent\n\n # Health check endpoint (always available via HTTP)\n handle /health {\n respond "OK" 200\n }\n}\n';
1019
-
1020
- // src/client/common/templates/caddyfileTemplate.ts
1021
- function getDefaultCaddyfile() {
1022
- return Caddyfile_default_default;
1023
- }
1024
-
1025
633
  // keys/mainnet-alpha/prod/kms-encryption-public-key.pem
1026
634
  var kms_encryption_public_key_default = "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0kHU86k17ofCIGcJKDcf\nAFurFhSLeWmOL0bwWLCeVnTPG0MMHtJOq+woE0XXSWw6lzm+jzavBBTwKde1dgal\nAp91vULAZFMUpiUdd2dNUVtvU89qW0Pgf1Eu5FDj7BkY/SnyECbWJM4ga0BmpiGy\nnQwLNN9mMGhjVoVLn2zwEGZ7JzS9Nz11EZKO/k/9DcO6LaoIFmKuvVf3jl6lvZg8\naeA0LoZXjkycHlRUt/kfKwZnhakUaYHP1ksV7ZNmolS5GYDTSKGB2KPPNR1s4/Xu\nu8zeEFC8HuGRU8XuuBeaAunitnGhbNVREUNJGff6HZOGB6CIFNXjbQETeZ3p5uro\n0v+hd1QqQYBv7+DEaMCmGnJNGAyIMr2mn4vr7wGsIj0HonlSHmQ8rmdUhL2ocNTc\nLhKgZiZmBuDpSbFW/r53R2G7CHcqaqGeUBnT54QCH4zsYKw0/4dOtwFxQpTyBf9/\n+k+KaWEJYKkx9d9OzKGyAvzrTDVOFoajddiJ6LPvRlMdOUQr3hl4IAC0/nh9lhHq\nD0R+i5WAU96TkdAe7B7iTGH2D22k0KUPR6Q9W3aF353SLxQAMPNrgG4QQufAdRJn\nAF+8ntun5TkTqjTWRSwAsUJZ1z4wb96DympWJbDi0OciJRZ3Fz3j9+amC43yCHGg\naaEMjdt35ewbztUSc04F10MCAwEAAQ==\n-----END PUBLIC KEY-----";
1027
635
 
@@ -1196,9 +804,15 @@ async function layerLocalImage(options, logger) {
1196
804
  const imageConfig = await extractImageConfig(docker, sourceImageRef);
1197
805
  const originalCmd = imageConfig.cmd.length > 0 ? imageConfig.cmd : imageConfig.entrypoint;
1198
806
  const originalUser = imageConfig.user;
1199
- const includeTLS = true;
1200
- const drainWatcherSource = findBinary("ecloud-drain-watcher-linux-amd64");
1201
- const includeDrainWatcher = fs.existsSync(drainWatcherSource);
807
+ let includeTLS = false;
808
+ if (envFilePath && fs.existsSync(envFilePath)) {
809
+ const envContent = fs.readFileSync(envFilePath, "utf-8");
810
+ const domainMatch = envContent.match(/^DOMAIN=(.+)$/m);
811
+ if (domainMatch && domainMatch[1] && domainMatch[1] !== "localhost") {
812
+ includeTLS = true;
813
+ logger.debug(`Found DOMAIN=${domainMatch[1]} in ${envFilePath}, including TLS components`);
814
+ }
815
+ }
1202
816
  const layeredDockerfileContent = processDockerfileTemplate({
1203
817
  baseImage: sourceImageRef,
1204
818
  originalCmd: JSON.stringify(originalCmd),
@@ -1206,9 +820,8 @@ async function layerLocalImage(options, logger) {
1206
820
  logRedirect,
1207
821
  resourceUsageAllow,
1208
822
  includeTLS,
1209
- ecloudCLIVersion: "0.1.0",
823
+ ecloudCLIVersion: "0.1.0"
1210
824
  // TODO: Get from package.json
1211
- includeDrainWatcher
1212
825
  });
1213
826
  const scriptContent = processScriptTemplate({
1214
827
  kmsServerURL: environmentConfig.kmsServerURL,
@@ -1218,8 +831,7 @@ async function layerLocalImage(options, logger) {
1218
831
  environmentConfig,
1219
832
  layeredDockerfileContent,
1220
833
  scriptContent,
1221
- includeTLS,
1222
- includeDrainWatcher ? drainWatcherSource : void 0
834
+ includeTLS
1223
835
  // logger
1224
836
  );
1225
837
  try {
@@ -1234,7 +846,7 @@ async function layerLocalImage(options, logger) {
1234
846
  fs.rmSync(tempDir, { recursive: true, force: true });
1235
847
  }
1236
848
  }
1237
- async function setupLayeredBuildDirectory(environmentConfig, layeredDockerfileContent, scriptContent, includeTLS, drainWatcherSource) {
849
+ async function setupLayeredBuildDirectory(environmentConfig, layeredDockerfileContent, scriptContent, includeTLS) {
1238
850
  const tempDir = fs.mkdtempSync(path2.join(os.tmpdir(), LAYERED_BUILD_DIR_PREFIX));
1239
851
  try {
1240
852
  const layeredDockerfilePath = path2.join(tempDir, LAYERED_DOCKERFILE_NAME);
@@ -1258,11 +870,6 @@ async function setupLayeredBuildDirectory(environmentConfig, layeredDockerfileCo
1258
870
  }
1259
871
  fs.copyFileSync(kmsClientSource, kmsClientPath);
1260
872
  fs.chmodSync(kmsClientPath, 493);
1261
- if (drainWatcherSource && fs.existsSync(drainWatcherSource)) {
1262
- const drainWatcherPath = path2.join(tempDir, DRAIN_WATCHER_BINARY_NAME);
1263
- fs.copyFileSync(drainWatcherSource, drainWatcherPath);
1264
- fs.chmodSync(drainWatcherPath, 493);
1265
- }
1266
873
  if (includeTLS) {
1267
874
  const tlsKeygenPath = path2.join(tempDir, TLS_KEYGEN_BINARY_NAME);
1268
875
  const tlsKeygenSource = findBinary("tls-keygen-linux-amd64");
@@ -1273,13 +880,15 @@ async function setupLayeredBuildDirectory(environmentConfig, layeredDockerfileCo
1273
880
  }
1274
881
  fs.copyFileSync(tlsKeygenSource, tlsKeygenPath);
1275
882
  fs.chmodSync(tlsKeygenPath, 493);
1276
- const userCaddyfilePath = path2.join(process.cwd(), CADDYFILE_NAME);
1277
- const destCaddyfilePath = path2.join(tempDir, CADDYFILE_NAME);
1278
- if (fs.existsSync(userCaddyfilePath)) {
1279
- fs.copyFileSync(userCaddyfilePath, destCaddyfilePath);
1280
- fs.chmodSync(destCaddyfilePath, 420);
883
+ const caddyfilePath = path2.join(process.cwd(), CADDYFILE_NAME);
884
+ if (fs.existsSync(caddyfilePath)) {
885
+ const caddyfileContent = fs.readFileSync(caddyfilePath);
886
+ const destCaddyfilePath = path2.join(tempDir, CADDYFILE_NAME);
887
+ fs.writeFileSync(destCaddyfilePath, caddyfileContent, { mode: 420 });
1281
888
  } else {
1282
- fs.writeFileSync(destCaddyfilePath, getDefaultCaddyfile(), { mode: 420 });
889
+ throw new Error(
890
+ "TLS is enabled (DOMAIN is set) but Caddyfile not found. Run configure TLS to set up TLS configuration"
891
+ );
1283
892
  }
1284
893
  }
1285
894
  return tempDir;
@@ -1347,16 +956,7 @@ async function extractDigestFromSinglePlatform(manifest, imageRef) {
1347
956
  architecture: inspectData[0].Architecture
1348
957
  } : null;
1349
958
  if (!config) {
1350
- if (manifest.config?.digest) {
1351
- const digest = hexStringToBytes32(manifest.config.digest);
1352
- const registry = extractRegistryName(imageRef);
1353
- return {
1354
- digest,
1355
- registry,
1356
- platform: DOCKER_PLATFORM
1357
- };
1358
- }
1359
- throw new Error(`Could not determine platform for ${imageRef}`);
959
+ throw createPlatformErrorMessage(imageRef, ["unknown (could not determine architecture)"]);
1360
960
  }
1361
961
  const platform2 = `${config.os}/${config.architecture}`;
1362
962
  if (platform2 === DOCKER_PLATFORM) {
@@ -1428,14 +1028,11 @@ Image: ${imageRef}
1428
1028
  Found platform(s): ${platforms.join(", ")}
1429
1029
  Required platform: ${DOCKER_PLATFORM}
1430
1030
 
1431
- To fix this issue:
1432
- 1. Manual fix:
1433
- a. Rebuild your image with the correct platform:
1434
- docker build --platform ${DOCKER_PLATFORM} -t ${imageRef} .
1435
- b. Push the rebuilt image to your remote registry:
1436
- docker push ${imageRef}
1437
-
1438
- 2. Or use the SDK to build with the correct platform automatically.`;
1031
+ To fix, either:
1032
+ 1. Rebuild the image for ${DOCKER_PLATFORM} and push it:
1033
+ docker buildx build --platform ${DOCKER_PLATFORM} -t ${imageRef} --push .
1034
+ 2. Or use a verifiable build (--verifiable --repo <repo> --commit <sha>), which
1035
+ builds server-side and needs no local Docker.`;
1439
1036
  return new Error(errorMsg);
1440
1037
  }
1441
1038
 
@@ -1540,6 +1137,8 @@ async function prepareRelease(options, logger) {
1540
1137
  logger.info(`Waiting ${REGISTRY_PROPAGATION_WAIT_SECONDS} seconds for registry propagation...`);
1541
1138
  await new Promise((resolve2) => setTimeout(resolve2, REGISTRY_PROPAGATION_WAIT_SECONDS * 1e3));
1542
1139
  } else {
1140
+ logger.info("Verifying image platform (linux/amd64)...");
1141
+ await getImageDigestAndName(imageRef);
1543
1142
  logger.info("Checking if image needs layering...");
1544
1143
  finalImageRef = await layerRemoteImageIfNeeded(
1545
1144
  {
@@ -1698,6 +1297,13 @@ function extractRegistryNameNoDocker(imageRef) {
1698
1297
  import { encodeFunctionData, encodeAbiParameters, decodeErrorResult } from "viem";
1699
1298
 
1700
1299
  // src/client/common/types/index.ts
1300
+ var EMPTY_CONTAINER_POLICY = {
1301
+ args: [],
1302
+ cmdOverride: [],
1303
+ env: [],
1304
+ envOverride: [],
1305
+ restartPolicy: ""
1306
+ };
1701
1307
  var noopLogger = {
1702
1308
  debug: () => {
1703
1309
  },
@@ -2938,32 +2544,1577 @@ async function executeBatch(options, logger = noopLogger) {
2938
2544
  } else {
2939
2545
  revertReason = callError.message || "Unknown reason";
2940
2546
  }
2941
- }
2942
- throw new Error(`Transaction reverted: ${hash}. Reason: ${revertReason}`);
2547
+ }
2548
+ throw new Error(`Transaction reverted: ${hash}. Reason: ${revertReason}`);
2549
+ }
2550
+ return hash;
2551
+ }
2552
+
2553
+ // src/client/common/contract/caller.ts
2554
+ import { encodeFunctionData as encodeFunctionData2, decodeErrorResult as decodeErrorResult2, bytesToHex } from "viem";
2555
+
2556
+ // src/client/common/utils/helpers.ts
2557
+ import { extractChain, createPublicClient, createWalletClient, http, fallback } from "viem";
2558
+ import { sepolia as sepolia2 } from "viem/chains";
2559
+ import { privateKeyToAccount } from "viem/accounts";
2560
+ function getChainFromID(chainID, fallback2 = sepolia2) {
2561
+ const id = Number(chainID);
2562
+ return extractChain({ chains: SUPPORTED_CHAINS, id }) || fallback2;
2563
+ }
2564
+ function addHexPrefix(value) {
2565
+ return value.startsWith("0x") ? value : `0x${value}`;
2566
+ }
2567
+ function stripHexPrefix(value) {
2568
+ return value.startsWith("0x") ? value.slice(2) : value;
2569
+ }
2570
+
2571
+ // src/client/common/abis/AppController.json
2572
+ var AppController_default = [
2573
+ {
2574
+ type: "constructor",
2575
+ inputs: [
2576
+ {
2577
+ name: "_version",
2578
+ type: "string",
2579
+ internalType: "string"
2580
+ },
2581
+ {
2582
+ name: "_permissionController",
2583
+ type: "address",
2584
+ internalType: "contractIPermissionController"
2585
+ },
2586
+ {
2587
+ name: "_releaseManager",
2588
+ type: "address",
2589
+ internalType: "contractIReleaseManager"
2590
+ },
2591
+ {
2592
+ name: "_computeAVSRegistrar",
2593
+ type: "address",
2594
+ internalType: "contractIComputeAVSRegistrar"
2595
+ },
2596
+ {
2597
+ name: "_computeOperator",
2598
+ type: "address",
2599
+ internalType: "contractIComputeOperator"
2600
+ },
2601
+ {
2602
+ name: "_appBeacon",
2603
+ type: "address",
2604
+ internalType: "contractIBeacon"
2605
+ }
2606
+ ],
2607
+ stateMutability: "nonpayable"
2608
+ },
2609
+ {
2610
+ type: "function",
2611
+ name: "API_PERMISSION_TYPEHASH",
2612
+ inputs: [],
2613
+ outputs: [
2614
+ {
2615
+ name: "",
2616
+ type: "bytes32",
2617
+ internalType: "bytes32"
2618
+ }
2619
+ ],
2620
+ stateMutability: "view"
2621
+ },
2622
+ {
2623
+ type: "function",
2624
+ name: "appBeacon",
2625
+ inputs: [],
2626
+ outputs: [
2627
+ {
2628
+ name: "",
2629
+ type: "address",
2630
+ internalType: "contractIBeacon"
2631
+ }
2632
+ ],
2633
+ stateMutability: "view"
2634
+ },
2635
+ {
2636
+ type: "function",
2637
+ name: "calculateApiPermissionDigestHash",
2638
+ inputs: [
2639
+ {
2640
+ name: "permission",
2641
+ type: "bytes4",
2642
+ internalType: "bytes4"
2643
+ },
2644
+ {
2645
+ name: "expiry",
2646
+ type: "uint256",
2647
+ internalType: "uint256"
2648
+ }
2649
+ ],
2650
+ outputs: [
2651
+ {
2652
+ name: "",
2653
+ type: "bytes32",
2654
+ internalType: "bytes32"
2655
+ }
2656
+ ],
2657
+ stateMutability: "view"
2658
+ },
2659
+ {
2660
+ type: "function",
2661
+ name: "calculateAppId",
2662
+ inputs: [
2663
+ {
2664
+ name: "deployer",
2665
+ type: "address",
2666
+ internalType: "address"
2667
+ },
2668
+ {
2669
+ name: "salt",
2670
+ type: "bytes32",
2671
+ internalType: "bytes32"
2672
+ }
2673
+ ],
2674
+ outputs: [
2675
+ {
2676
+ name: "",
2677
+ type: "address",
2678
+ internalType: "contractIApp"
2679
+ }
2680
+ ],
2681
+ stateMutability: "view"
2682
+ },
2683
+ {
2684
+ type: "function",
2685
+ name: "computeAVSRegistrar",
2686
+ inputs: [],
2687
+ outputs: [
2688
+ {
2689
+ name: "",
2690
+ type: "address",
2691
+ internalType: "contractIComputeAVSRegistrar"
2692
+ }
2693
+ ],
2694
+ stateMutability: "view"
2695
+ },
2696
+ {
2697
+ type: "function",
2698
+ name: "computeOperator",
2699
+ inputs: [],
2700
+ outputs: [
2701
+ {
2702
+ name: "",
2703
+ type: "address",
2704
+ internalType: "contractIComputeOperator"
2705
+ }
2706
+ ],
2707
+ stateMutability: "view"
2708
+ },
2709
+ {
2710
+ type: "function",
2711
+ name: "confirmUpgrade",
2712
+ inputs: [
2713
+ {
2714
+ name: "app",
2715
+ type: "address",
2716
+ internalType: "contractIApp"
2717
+ }
2718
+ ],
2719
+ outputs: [],
2720
+ stateMutability: "nonpayable"
2721
+ },
2722
+ {
2723
+ type: "function",
2724
+ name: "createApp",
2725
+ inputs: [
2726
+ {
2727
+ name: "salt",
2728
+ type: "bytes32",
2729
+ internalType: "bytes32"
2730
+ },
2731
+ {
2732
+ name: "release",
2733
+ type: "tuple",
2734
+ internalType: "structIAppController.Release",
2735
+ components: [
2736
+ {
2737
+ name: "rmsRelease",
2738
+ type: "tuple",
2739
+ internalType: "structIReleaseManagerTypes.Release",
2740
+ components: [
2741
+ {
2742
+ name: "artifacts",
2743
+ type: "tuple[]",
2744
+ internalType: "structIReleaseManagerTypes.Artifact[]",
2745
+ components: [
2746
+ {
2747
+ name: "digest",
2748
+ type: "bytes32",
2749
+ internalType: "bytes32"
2750
+ },
2751
+ {
2752
+ name: "registry",
2753
+ type: "string",
2754
+ internalType: "string"
2755
+ }
2756
+ ]
2757
+ },
2758
+ {
2759
+ name: "upgradeByTime",
2760
+ type: "uint32",
2761
+ internalType: "uint32"
2762
+ }
2763
+ ]
2764
+ },
2765
+ {
2766
+ name: "publicEnv",
2767
+ type: "bytes",
2768
+ internalType: "bytes"
2769
+ },
2770
+ {
2771
+ name: "encryptedEnv",
2772
+ type: "bytes",
2773
+ internalType: "bytes"
2774
+ },
2775
+ {
2776
+ name: "containerPolicy",
2777
+ type: "tuple",
2778
+ internalType: "structIAppController.ContainerPolicy",
2779
+ components: [
2780
+ {
2781
+ name: "args",
2782
+ type: "string[]",
2783
+ internalType: "string[]"
2784
+ },
2785
+ {
2786
+ name: "cmdOverride",
2787
+ type: "string[]",
2788
+ internalType: "string[]"
2789
+ },
2790
+ {
2791
+ name: "env",
2792
+ type: "tuple[]",
2793
+ internalType: "structIAppController.EnvVar[]",
2794
+ components: [
2795
+ {
2796
+ name: "key",
2797
+ type: "string",
2798
+ internalType: "string"
2799
+ },
2800
+ {
2801
+ name: "value",
2802
+ type: "string",
2803
+ internalType: "string"
2804
+ }
2805
+ ]
2806
+ },
2807
+ {
2808
+ name: "envOverride",
2809
+ type: "tuple[]",
2810
+ internalType: "structIAppController.EnvVar[]",
2811
+ components: [
2812
+ {
2813
+ name: "key",
2814
+ type: "string",
2815
+ internalType: "string"
2816
+ },
2817
+ {
2818
+ name: "value",
2819
+ type: "string",
2820
+ internalType: "string"
2821
+ }
2822
+ ]
2823
+ },
2824
+ {
2825
+ name: "restartPolicy",
2826
+ type: "string",
2827
+ internalType: "string"
2828
+ }
2829
+ ]
2830
+ }
2831
+ ]
2832
+ }
2833
+ ],
2834
+ outputs: [
2835
+ {
2836
+ name: "app",
2837
+ type: "address",
2838
+ internalType: "contractIApp"
2839
+ }
2840
+ ],
2841
+ stateMutability: "nonpayable"
2842
+ },
2843
+ {
2844
+ type: "function",
2845
+ name: "createAppWithIsolatedBilling",
2846
+ inputs: [
2847
+ {
2848
+ name: "salt",
2849
+ type: "bytes32",
2850
+ internalType: "bytes32"
2851
+ },
2852
+ {
2853
+ name: "release",
2854
+ type: "tuple",
2855
+ internalType: "structIAppController.Release",
2856
+ components: [
2857
+ {
2858
+ name: "rmsRelease",
2859
+ type: "tuple",
2860
+ internalType: "structIReleaseManagerTypes.Release",
2861
+ components: [
2862
+ {
2863
+ name: "artifacts",
2864
+ type: "tuple[]",
2865
+ internalType: "structIReleaseManagerTypes.Artifact[]",
2866
+ components: [
2867
+ {
2868
+ name: "digest",
2869
+ type: "bytes32",
2870
+ internalType: "bytes32"
2871
+ },
2872
+ {
2873
+ name: "registry",
2874
+ type: "string",
2875
+ internalType: "string"
2876
+ }
2877
+ ]
2878
+ },
2879
+ {
2880
+ name: "upgradeByTime",
2881
+ type: "uint32",
2882
+ internalType: "uint32"
2883
+ }
2884
+ ]
2885
+ },
2886
+ {
2887
+ name: "publicEnv",
2888
+ type: "bytes",
2889
+ internalType: "bytes"
2890
+ },
2891
+ {
2892
+ name: "encryptedEnv",
2893
+ type: "bytes",
2894
+ internalType: "bytes"
2895
+ },
2896
+ {
2897
+ name: "containerPolicy",
2898
+ type: "tuple",
2899
+ internalType: "structIAppController.ContainerPolicy",
2900
+ components: [
2901
+ {
2902
+ name: "args",
2903
+ type: "string[]",
2904
+ internalType: "string[]"
2905
+ },
2906
+ {
2907
+ name: "cmdOverride",
2908
+ type: "string[]",
2909
+ internalType: "string[]"
2910
+ },
2911
+ {
2912
+ name: "env",
2913
+ type: "tuple[]",
2914
+ internalType: "structIAppController.EnvVar[]",
2915
+ components: [
2916
+ {
2917
+ name: "key",
2918
+ type: "string",
2919
+ internalType: "string"
2920
+ },
2921
+ {
2922
+ name: "value",
2923
+ type: "string",
2924
+ internalType: "string"
2925
+ }
2926
+ ]
2927
+ },
2928
+ {
2929
+ name: "envOverride",
2930
+ type: "tuple[]",
2931
+ internalType: "structIAppController.EnvVar[]",
2932
+ components: [
2933
+ {
2934
+ name: "key",
2935
+ type: "string",
2936
+ internalType: "string"
2937
+ },
2938
+ {
2939
+ name: "value",
2940
+ type: "string",
2941
+ internalType: "string"
2942
+ }
2943
+ ]
2944
+ },
2945
+ {
2946
+ name: "restartPolicy",
2947
+ type: "string",
2948
+ internalType: "string"
2949
+ }
2950
+ ]
2951
+ }
2952
+ ]
2953
+ }
2954
+ ],
2955
+ outputs: [
2956
+ {
2957
+ name: "app",
2958
+ type: "address",
2959
+ internalType: "contractIApp"
2960
+ }
2961
+ ],
2962
+ stateMutability: "nonpayable"
2963
+ },
2964
+ {
2965
+ type: "function",
2966
+ name: "createEmptyApp",
2967
+ inputs: [
2968
+ {
2969
+ name: "salt",
2970
+ type: "bytes32",
2971
+ internalType: "bytes32"
2972
+ }
2973
+ ],
2974
+ outputs: [
2975
+ {
2976
+ name: "app",
2977
+ type: "address",
2978
+ internalType: "contractIApp"
2979
+ }
2980
+ ],
2981
+ stateMutability: "nonpayable"
2982
+ },
2983
+ {
2984
+ type: "function",
2985
+ name: "createEmptyAppWithIsolatedBilling",
2986
+ inputs: [
2987
+ {
2988
+ name: "salt",
2989
+ type: "bytes32",
2990
+ internalType: "bytes32"
2991
+ }
2992
+ ],
2993
+ outputs: [
2994
+ {
2995
+ name: "app",
2996
+ type: "address",
2997
+ internalType: "contractIApp"
2998
+ }
2999
+ ],
3000
+ stateMutability: "nonpayable"
3001
+ },
3002
+ {
3003
+ type: "function",
3004
+ name: "domainSeparator",
3005
+ inputs: [],
3006
+ outputs: [
3007
+ {
3008
+ name: "",
3009
+ type: "bytes32",
3010
+ internalType: "bytes32"
3011
+ }
3012
+ ],
3013
+ stateMutability: "view"
3014
+ },
3015
+ {
3016
+ type: "function",
3017
+ name: "getActiveAppCount",
3018
+ inputs: [
3019
+ {
3020
+ name: "user",
3021
+ type: "address",
3022
+ internalType: "address"
3023
+ }
3024
+ ],
3025
+ outputs: [
3026
+ {
3027
+ name: "",
3028
+ type: "uint32",
3029
+ internalType: "uint32"
3030
+ }
3031
+ ],
3032
+ stateMutability: "view"
3033
+ },
3034
+ {
3035
+ type: "function",
3036
+ name: "getAppCreator",
3037
+ inputs: [
3038
+ {
3039
+ name: "app",
3040
+ type: "address",
3041
+ internalType: "contractIApp"
3042
+ }
3043
+ ],
3044
+ outputs: [
3045
+ {
3046
+ name: "",
3047
+ type: "address",
3048
+ internalType: "address"
3049
+ }
3050
+ ],
3051
+ stateMutability: "view"
3052
+ },
3053
+ {
3054
+ type: "function",
3055
+ name: "getAppLatestReleaseBlockNumber",
3056
+ inputs: [
3057
+ {
3058
+ name: "app",
3059
+ type: "address",
3060
+ internalType: "contractIApp"
3061
+ }
3062
+ ],
3063
+ outputs: [
3064
+ {
3065
+ name: "",
3066
+ type: "uint32",
3067
+ internalType: "uint32"
3068
+ }
3069
+ ],
3070
+ stateMutability: "view"
3071
+ },
3072
+ {
3073
+ type: "function",
3074
+ name: "getAppOperatorSetId",
3075
+ inputs: [
3076
+ {
3077
+ name: "app",
3078
+ type: "address",
3079
+ internalType: "contractIApp"
3080
+ }
3081
+ ],
3082
+ outputs: [
3083
+ {
3084
+ name: "",
3085
+ type: "uint32",
3086
+ internalType: "uint32"
3087
+ }
3088
+ ],
3089
+ stateMutability: "view"
3090
+ },
3091
+ {
3092
+ type: "function",
3093
+ name: "getAppPendingReleaseBlockNumber",
3094
+ inputs: [
3095
+ {
3096
+ name: "app",
3097
+ type: "address",
3098
+ internalType: "contractIApp"
3099
+ }
3100
+ ],
3101
+ outputs: [
3102
+ {
3103
+ name: "",
3104
+ type: "uint32",
3105
+ internalType: "uint32"
3106
+ }
3107
+ ],
3108
+ stateMutability: "view"
3109
+ },
3110
+ {
3111
+ type: "function",
3112
+ name: "getAppStatus",
3113
+ inputs: [
3114
+ {
3115
+ name: "app",
3116
+ type: "address",
3117
+ internalType: "contractIApp"
3118
+ }
3119
+ ],
3120
+ outputs: [
3121
+ {
3122
+ name: "",
3123
+ type: "uint8",
3124
+ internalType: "enumIAppController.AppStatus"
3125
+ }
3126
+ ],
3127
+ stateMutability: "view"
3128
+ },
3129
+ {
3130
+ type: "function",
3131
+ name: "getApps",
3132
+ inputs: [
3133
+ {
3134
+ name: "offset",
3135
+ type: "uint256",
3136
+ internalType: "uint256"
3137
+ },
3138
+ {
3139
+ name: "limit",
3140
+ type: "uint256",
3141
+ internalType: "uint256"
3142
+ }
3143
+ ],
3144
+ outputs: [
3145
+ {
3146
+ name: "apps",
3147
+ type: "address[]",
3148
+ internalType: "contractIApp[]"
3149
+ },
3150
+ {
3151
+ name: "appConfigsMem",
3152
+ type: "tuple[]",
3153
+ internalType: "structIAppController.AppConfig[]",
3154
+ components: [
3155
+ {
3156
+ name: "creator",
3157
+ type: "address",
3158
+ internalType: "address"
3159
+ },
3160
+ {
3161
+ name: "operatorSetId",
3162
+ type: "uint32",
3163
+ internalType: "uint32"
3164
+ },
3165
+ {
3166
+ name: "latestReleaseBlockNumber",
3167
+ type: "uint32",
3168
+ internalType: "uint32"
3169
+ },
3170
+ {
3171
+ name: "pendingReleaseBlockNumber",
3172
+ type: "uint32",
3173
+ internalType: "uint32"
3174
+ },
3175
+ {
3176
+ name: "status",
3177
+ type: "uint8",
3178
+ internalType: "enumIAppController.AppStatus"
3179
+ }
3180
+ ]
3181
+ }
3182
+ ],
3183
+ stateMutability: "view"
3184
+ },
3185
+ {
3186
+ type: "function",
3187
+ name: "getAppsByBillingAccount",
3188
+ inputs: [
3189
+ {
3190
+ name: "account",
3191
+ type: "address",
3192
+ internalType: "address"
3193
+ },
3194
+ {
3195
+ name: "offset",
3196
+ type: "uint256",
3197
+ internalType: "uint256"
3198
+ },
3199
+ {
3200
+ name: "limit",
3201
+ type: "uint256",
3202
+ internalType: "uint256"
3203
+ }
3204
+ ],
3205
+ outputs: [
3206
+ {
3207
+ name: "apps",
3208
+ type: "address[]",
3209
+ internalType: "contractIApp[]"
3210
+ },
3211
+ {
3212
+ name: "appConfigsMem",
3213
+ type: "tuple[]",
3214
+ internalType: "structIAppController.AppConfig[]",
3215
+ components: [
3216
+ {
3217
+ name: "creator",
3218
+ type: "address",
3219
+ internalType: "address"
3220
+ },
3221
+ {
3222
+ name: "operatorSetId",
3223
+ type: "uint32",
3224
+ internalType: "uint32"
3225
+ },
3226
+ {
3227
+ name: "latestReleaseBlockNumber",
3228
+ type: "uint32",
3229
+ internalType: "uint32"
3230
+ },
3231
+ {
3232
+ name: "pendingReleaseBlockNumber",
3233
+ type: "uint32",
3234
+ internalType: "uint32"
3235
+ },
3236
+ {
3237
+ name: "status",
3238
+ type: "uint8",
3239
+ internalType: "enumIAppController.AppStatus"
3240
+ }
3241
+ ]
3242
+ }
3243
+ ],
3244
+ stateMutability: "view"
3245
+ },
3246
+ {
3247
+ type: "function",
3248
+ name: "getAppsByCreator",
3249
+ inputs: [
3250
+ {
3251
+ name: "creator",
3252
+ type: "address",
3253
+ internalType: "address"
3254
+ },
3255
+ {
3256
+ name: "offset",
3257
+ type: "uint256",
3258
+ internalType: "uint256"
3259
+ },
3260
+ {
3261
+ name: "limit",
3262
+ type: "uint256",
3263
+ internalType: "uint256"
3264
+ }
3265
+ ],
3266
+ outputs: [
3267
+ {
3268
+ name: "apps",
3269
+ type: "address[]",
3270
+ internalType: "contractIApp[]"
3271
+ },
3272
+ {
3273
+ name: "appConfigsMem",
3274
+ type: "tuple[]",
3275
+ internalType: "structIAppController.AppConfig[]",
3276
+ components: [
3277
+ {
3278
+ name: "creator",
3279
+ type: "address",
3280
+ internalType: "address"
3281
+ },
3282
+ {
3283
+ name: "operatorSetId",
3284
+ type: "uint32",
3285
+ internalType: "uint32"
3286
+ },
3287
+ {
3288
+ name: "latestReleaseBlockNumber",
3289
+ type: "uint32",
3290
+ internalType: "uint32"
3291
+ },
3292
+ {
3293
+ name: "pendingReleaseBlockNumber",
3294
+ type: "uint32",
3295
+ internalType: "uint32"
3296
+ },
3297
+ {
3298
+ name: "status",
3299
+ type: "uint8",
3300
+ internalType: "enumIAppController.AppStatus"
3301
+ }
3302
+ ]
3303
+ }
3304
+ ],
3305
+ stateMutability: "view"
3306
+ },
3307
+ {
3308
+ type: "function",
3309
+ name: "getAppsByDeveloper",
3310
+ inputs: [
3311
+ {
3312
+ name: "developer",
3313
+ type: "address",
3314
+ internalType: "address"
3315
+ },
3316
+ {
3317
+ name: "offset",
3318
+ type: "uint256",
3319
+ internalType: "uint256"
3320
+ },
3321
+ {
3322
+ name: "limit",
3323
+ type: "uint256",
3324
+ internalType: "uint256"
3325
+ }
3326
+ ],
3327
+ outputs: [
3328
+ {
3329
+ name: "apps",
3330
+ type: "address[]",
3331
+ internalType: "contractIApp[]"
3332
+ },
3333
+ {
3334
+ name: "appConfigsMem",
3335
+ type: "tuple[]",
3336
+ internalType: "structIAppController.AppConfig[]",
3337
+ components: [
3338
+ {
3339
+ name: "creator",
3340
+ type: "address",
3341
+ internalType: "address"
3342
+ },
3343
+ {
3344
+ name: "operatorSetId",
3345
+ type: "uint32",
3346
+ internalType: "uint32"
3347
+ },
3348
+ {
3349
+ name: "latestReleaseBlockNumber",
3350
+ type: "uint32",
3351
+ internalType: "uint32"
3352
+ },
3353
+ {
3354
+ name: "pendingReleaseBlockNumber",
3355
+ type: "uint32",
3356
+ internalType: "uint32"
3357
+ },
3358
+ {
3359
+ name: "status",
3360
+ type: "uint8",
3361
+ internalType: "enumIAppController.AppStatus"
3362
+ }
3363
+ ]
3364
+ }
3365
+ ],
3366
+ stateMutability: "view"
3367
+ },
3368
+ {
3369
+ type: "function",
3370
+ name: "getBillingAccount",
3371
+ inputs: [
3372
+ {
3373
+ name: "app",
3374
+ type: "address",
3375
+ internalType: "contractIApp"
3376
+ }
3377
+ ],
3378
+ outputs: [
3379
+ {
3380
+ name: "",
3381
+ type: "address",
3382
+ internalType: "address"
3383
+ }
3384
+ ],
3385
+ stateMutability: "view"
3386
+ },
3387
+ {
3388
+ type: "function",
3389
+ name: "getBillingType",
3390
+ inputs: [
3391
+ {
3392
+ name: "app",
3393
+ type: "address",
3394
+ internalType: "contractIApp"
3395
+ }
3396
+ ],
3397
+ outputs: [
3398
+ {
3399
+ name: "",
3400
+ type: "uint8",
3401
+ internalType: "enumIAppController.BillingType"
3402
+ }
3403
+ ],
3404
+ stateMutability: "view"
3405
+ },
3406
+ {
3407
+ type: "function",
3408
+ name: "getMaxActiveAppsPerUser",
3409
+ inputs: [
3410
+ {
3411
+ name: "user",
3412
+ type: "address",
3413
+ internalType: "address"
3414
+ }
3415
+ ],
3416
+ outputs: [
3417
+ {
3418
+ name: "",
3419
+ type: "uint32",
3420
+ internalType: "uint32"
3421
+ }
3422
+ ],
3423
+ stateMutability: "view"
3424
+ },
3425
+ {
3426
+ type: "function",
3427
+ name: "globalActiveAppCount",
3428
+ inputs: [],
3429
+ outputs: [
3430
+ {
3431
+ name: "",
3432
+ type: "uint32",
3433
+ internalType: "uint32"
3434
+ }
3435
+ ],
3436
+ stateMutability: "view"
3437
+ },
3438
+ {
3439
+ type: "function",
3440
+ name: "initialize",
3441
+ inputs: [
3442
+ {
3443
+ name: "admin",
3444
+ type: "address",
3445
+ internalType: "address"
3446
+ }
3447
+ ],
3448
+ outputs: [],
3449
+ stateMutability: "nonpayable"
3450
+ },
3451
+ {
3452
+ type: "function",
3453
+ name: "maxGlobalActiveApps",
3454
+ inputs: [],
3455
+ outputs: [
3456
+ {
3457
+ name: "",
3458
+ type: "uint32",
3459
+ internalType: "uint32"
3460
+ }
3461
+ ],
3462
+ stateMutability: "view"
3463
+ },
3464
+ {
3465
+ type: "function",
3466
+ name: "permissionController",
3467
+ inputs: [],
3468
+ outputs: [
3469
+ {
3470
+ name: "",
3471
+ type: "address",
3472
+ internalType: "contractIPermissionController"
3473
+ }
3474
+ ],
3475
+ stateMutability: "view"
3476
+ },
3477
+ {
3478
+ type: "function",
3479
+ name: "releaseManager",
3480
+ inputs: [],
3481
+ outputs: [
3482
+ {
3483
+ name: "",
3484
+ type: "address",
3485
+ internalType: "contractIReleaseManager"
3486
+ }
3487
+ ],
3488
+ stateMutability: "view"
3489
+ },
3490
+ {
3491
+ type: "function",
3492
+ name: "setMaxActiveAppsPerUser",
3493
+ inputs: [
3494
+ {
3495
+ name: "user",
3496
+ type: "address",
3497
+ internalType: "address"
3498
+ },
3499
+ {
3500
+ name: "limit",
3501
+ type: "uint32",
3502
+ internalType: "uint32"
3503
+ }
3504
+ ],
3505
+ outputs: [],
3506
+ stateMutability: "nonpayable"
3507
+ },
3508
+ {
3509
+ type: "function",
3510
+ name: "setMaxGlobalActiveApps",
3511
+ inputs: [
3512
+ {
3513
+ name: "limit",
3514
+ type: "uint32",
3515
+ internalType: "uint32"
3516
+ }
3517
+ ],
3518
+ outputs: [],
3519
+ stateMutability: "nonpayable"
3520
+ },
3521
+ {
3522
+ type: "function",
3523
+ name: "startApp",
3524
+ inputs: [
3525
+ {
3526
+ name: "app",
3527
+ type: "address",
3528
+ internalType: "contractIApp"
3529
+ }
3530
+ ],
3531
+ outputs: [],
3532
+ stateMutability: "nonpayable"
3533
+ },
3534
+ {
3535
+ type: "function",
3536
+ name: "stopApp",
3537
+ inputs: [
3538
+ {
3539
+ name: "app",
3540
+ type: "address",
3541
+ internalType: "contractIApp"
3542
+ }
3543
+ ],
3544
+ outputs: [],
3545
+ stateMutability: "nonpayable"
3546
+ },
3547
+ {
3548
+ type: "function",
3549
+ name: "suspend",
3550
+ inputs: [
3551
+ {
3552
+ name: "account",
3553
+ type: "address",
3554
+ internalType: "address"
3555
+ },
3556
+ {
3557
+ name: "apps",
3558
+ type: "address[]",
3559
+ internalType: "contractIApp[]"
3560
+ }
3561
+ ],
3562
+ outputs: [],
3563
+ stateMutability: "nonpayable"
3564
+ },
3565
+ {
3566
+ type: "function",
3567
+ name: "terminateApp",
3568
+ inputs: [
3569
+ {
3570
+ name: "app",
3571
+ type: "address",
3572
+ internalType: "contractIApp"
3573
+ }
3574
+ ],
3575
+ outputs: [],
3576
+ stateMutability: "nonpayable"
3577
+ },
3578
+ {
3579
+ type: "function",
3580
+ name: "terminateAppByAdmin",
3581
+ inputs: [
3582
+ {
3583
+ name: "app",
3584
+ type: "address",
3585
+ internalType: "contractIApp"
3586
+ }
3587
+ ],
3588
+ outputs: [],
3589
+ stateMutability: "nonpayable"
3590
+ },
3591
+ {
3592
+ type: "function",
3593
+ name: "updateAppMetadataURI",
3594
+ inputs: [
3595
+ {
3596
+ name: "app",
3597
+ type: "address",
3598
+ internalType: "contractIApp"
3599
+ },
3600
+ {
3601
+ name: "metadataURI",
3602
+ type: "string",
3603
+ internalType: "string"
3604
+ }
3605
+ ],
3606
+ outputs: [],
3607
+ stateMutability: "nonpayable"
3608
+ },
3609
+ {
3610
+ type: "function",
3611
+ name: "upgradeApp",
3612
+ inputs: [
3613
+ {
3614
+ name: "app",
3615
+ type: "address",
3616
+ internalType: "contractIApp"
3617
+ },
3618
+ {
3619
+ name: "release",
3620
+ type: "tuple",
3621
+ internalType: "structIAppController.Release",
3622
+ components: [
3623
+ {
3624
+ name: "rmsRelease",
3625
+ type: "tuple",
3626
+ internalType: "structIReleaseManagerTypes.Release",
3627
+ components: [
3628
+ {
3629
+ name: "artifacts",
3630
+ type: "tuple[]",
3631
+ internalType: "structIReleaseManagerTypes.Artifact[]",
3632
+ components: [
3633
+ {
3634
+ name: "digest",
3635
+ type: "bytes32",
3636
+ internalType: "bytes32"
3637
+ },
3638
+ {
3639
+ name: "registry",
3640
+ type: "string",
3641
+ internalType: "string"
3642
+ }
3643
+ ]
3644
+ },
3645
+ {
3646
+ name: "upgradeByTime",
3647
+ type: "uint32",
3648
+ internalType: "uint32"
3649
+ }
3650
+ ]
3651
+ },
3652
+ {
3653
+ name: "publicEnv",
3654
+ type: "bytes",
3655
+ internalType: "bytes"
3656
+ },
3657
+ {
3658
+ name: "encryptedEnv",
3659
+ type: "bytes",
3660
+ internalType: "bytes"
3661
+ },
3662
+ {
3663
+ name: "containerPolicy",
3664
+ type: "tuple",
3665
+ internalType: "structIAppController.ContainerPolicy",
3666
+ components: [
3667
+ {
3668
+ name: "args",
3669
+ type: "string[]",
3670
+ internalType: "string[]"
3671
+ },
3672
+ {
3673
+ name: "cmdOverride",
3674
+ type: "string[]",
3675
+ internalType: "string[]"
3676
+ },
3677
+ {
3678
+ name: "env",
3679
+ type: "tuple[]",
3680
+ internalType: "structIAppController.EnvVar[]",
3681
+ components: [
3682
+ {
3683
+ name: "key",
3684
+ type: "string",
3685
+ internalType: "string"
3686
+ },
3687
+ {
3688
+ name: "value",
3689
+ type: "string",
3690
+ internalType: "string"
3691
+ }
3692
+ ]
3693
+ },
3694
+ {
3695
+ name: "envOverride",
3696
+ type: "tuple[]",
3697
+ internalType: "structIAppController.EnvVar[]",
3698
+ components: [
3699
+ {
3700
+ name: "key",
3701
+ type: "string",
3702
+ internalType: "string"
3703
+ },
3704
+ {
3705
+ name: "value",
3706
+ type: "string",
3707
+ internalType: "string"
3708
+ }
3709
+ ]
3710
+ },
3711
+ {
3712
+ name: "restartPolicy",
3713
+ type: "string",
3714
+ internalType: "string"
3715
+ }
3716
+ ]
3717
+ }
3718
+ ]
3719
+ }
3720
+ ],
3721
+ outputs: [
3722
+ {
3723
+ name: "",
3724
+ type: "uint256",
3725
+ internalType: "uint256"
3726
+ }
3727
+ ],
3728
+ stateMutability: "nonpayable"
3729
+ },
3730
+ {
3731
+ type: "function",
3732
+ name: "version",
3733
+ inputs: [],
3734
+ outputs: [
3735
+ {
3736
+ name: "",
3737
+ type: "string",
3738
+ internalType: "string"
3739
+ }
3740
+ ],
3741
+ stateMutability: "view"
3742
+ },
3743
+ {
3744
+ type: "event",
3745
+ name: "AppCreated",
3746
+ inputs: [
3747
+ {
3748
+ name: "creator",
3749
+ type: "address",
3750
+ indexed: true,
3751
+ internalType: "address"
3752
+ },
3753
+ {
3754
+ name: "app",
3755
+ type: "address",
3756
+ indexed: true,
3757
+ internalType: "contractIApp"
3758
+ },
3759
+ {
3760
+ name: "operatorSetId",
3761
+ type: "uint32",
3762
+ indexed: false,
3763
+ internalType: "uint32"
3764
+ }
3765
+ ],
3766
+ anonymous: false
3767
+ },
3768
+ {
3769
+ type: "event",
3770
+ name: "AppMetadataURIUpdated",
3771
+ inputs: [
3772
+ {
3773
+ name: "app",
3774
+ type: "address",
3775
+ indexed: true,
3776
+ internalType: "contractIApp"
3777
+ },
3778
+ {
3779
+ name: "metadataURI",
3780
+ type: "string",
3781
+ indexed: false,
3782
+ internalType: "string"
3783
+ }
3784
+ ],
3785
+ anonymous: false
3786
+ },
3787
+ {
3788
+ type: "event",
3789
+ name: "AppStarted",
3790
+ inputs: [
3791
+ {
3792
+ name: "app",
3793
+ type: "address",
3794
+ indexed: true,
3795
+ internalType: "contractIApp"
3796
+ }
3797
+ ],
3798
+ anonymous: false
3799
+ },
3800
+ {
3801
+ type: "event",
3802
+ name: "AppStopped",
3803
+ inputs: [
3804
+ {
3805
+ name: "app",
3806
+ type: "address",
3807
+ indexed: true,
3808
+ internalType: "contractIApp"
3809
+ }
3810
+ ],
3811
+ anonymous: false
3812
+ },
3813
+ {
3814
+ type: "event",
3815
+ name: "AppSuspended",
3816
+ inputs: [
3817
+ {
3818
+ name: "app",
3819
+ type: "address",
3820
+ indexed: true,
3821
+ internalType: "contractIApp"
3822
+ }
3823
+ ],
3824
+ anonymous: false
3825
+ },
3826
+ {
3827
+ type: "event",
3828
+ name: "AppTerminated",
3829
+ inputs: [
3830
+ {
3831
+ name: "app",
3832
+ type: "address",
3833
+ indexed: true,
3834
+ internalType: "contractIApp"
3835
+ }
3836
+ ],
3837
+ anonymous: false
3838
+ },
3839
+ {
3840
+ type: "event",
3841
+ name: "AppTerminatedByAdmin",
3842
+ inputs: [
3843
+ {
3844
+ name: "app",
3845
+ type: "address",
3846
+ indexed: true,
3847
+ internalType: "contractIApp"
3848
+ }
3849
+ ],
3850
+ anonymous: false
3851
+ },
3852
+ {
3853
+ type: "event",
3854
+ name: "AppUpgraded",
3855
+ inputs: [
3856
+ {
3857
+ name: "app",
3858
+ type: "address",
3859
+ indexed: true,
3860
+ internalType: "contractIApp"
3861
+ },
3862
+ {
3863
+ name: "rmsReleaseId",
3864
+ type: "uint256",
3865
+ indexed: false,
3866
+ internalType: "uint256"
3867
+ },
3868
+ {
3869
+ name: "release",
3870
+ type: "tuple",
3871
+ indexed: false,
3872
+ internalType: "structIAppController.Release",
3873
+ components: [
3874
+ {
3875
+ name: "rmsRelease",
3876
+ type: "tuple",
3877
+ internalType: "structIReleaseManagerTypes.Release",
3878
+ components: [
3879
+ {
3880
+ name: "artifacts",
3881
+ type: "tuple[]",
3882
+ internalType: "structIReleaseManagerTypes.Artifact[]",
3883
+ components: [
3884
+ {
3885
+ name: "digest",
3886
+ type: "bytes32",
3887
+ internalType: "bytes32"
3888
+ },
3889
+ {
3890
+ name: "registry",
3891
+ type: "string",
3892
+ internalType: "string"
3893
+ }
3894
+ ]
3895
+ },
3896
+ {
3897
+ name: "upgradeByTime",
3898
+ type: "uint32",
3899
+ internalType: "uint32"
3900
+ }
3901
+ ]
3902
+ },
3903
+ {
3904
+ name: "publicEnv",
3905
+ type: "bytes",
3906
+ internalType: "bytes"
3907
+ },
3908
+ {
3909
+ name: "encryptedEnv",
3910
+ type: "bytes",
3911
+ internalType: "bytes"
3912
+ },
3913
+ {
3914
+ name: "containerPolicy",
3915
+ type: "tuple",
3916
+ internalType: "structIAppController.ContainerPolicy",
3917
+ components: [
3918
+ {
3919
+ name: "args",
3920
+ type: "string[]",
3921
+ internalType: "string[]"
3922
+ },
3923
+ {
3924
+ name: "cmdOverride",
3925
+ type: "string[]",
3926
+ internalType: "string[]"
3927
+ },
3928
+ {
3929
+ name: "env",
3930
+ type: "tuple[]",
3931
+ internalType: "structIAppController.EnvVar[]",
3932
+ components: [
3933
+ {
3934
+ name: "key",
3935
+ type: "string",
3936
+ internalType: "string"
3937
+ },
3938
+ {
3939
+ name: "value",
3940
+ type: "string",
3941
+ internalType: "string"
3942
+ }
3943
+ ]
3944
+ },
3945
+ {
3946
+ name: "envOverride",
3947
+ type: "tuple[]",
3948
+ internalType: "structIAppController.EnvVar[]",
3949
+ components: [
3950
+ {
3951
+ name: "key",
3952
+ type: "string",
3953
+ internalType: "string"
3954
+ },
3955
+ {
3956
+ name: "value",
3957
+ type: "string",
3958
+ internalType: "string"
3959
+ }
3960
+ ]
3961
+ },
3962
+ {
3963
+ name: "restartPolicy",
3964
+ type: "string",
3965
+ internalType: "string"
3966
+ }
3967
+ ]
3968
+ }
3969
+ ]
3970
+ }
3971
+ ],
3972
+ anonymous: false
3973
+ },
3974
+ {
3975
+ type: "event",
3976
+ name: "GlobalMaxActiveAppsSet",
3977
+ inputs: [
3978
+ {
3979
+ name: "limit",
3980
+ type: "uint32",
3981
+ indexed: false,
3982
+ internalType: "uint32"
3983
+ }
3984
+ ],
3985
+ anonymous: false
3986
+ },
3987
+ {
3988
+ type: "event",
3989
+ name: "Initialized",
3990
+ inputs: [
3991
+ {
3992
+ name: "version",
3993
+ type: "uint8",
3994
+ indexed: false,
3995
+ internalType: "uint8"
3996
+ }
3997
+ ],
3998
+ anonymous: false
3999
+ },
4000
+ {
4001
+ type: "event",
4002
+ name: "MaxActiveAppsSet",
4003
+ inputs: [
4004
+ {
4005
+ name: "user",
4006
+ type: "address",
4007
+ indexed: true,
4008
+ internalType: "address"
4009
+ },
4010
+ {
4011
+ name: "limit",
4012
+ type: "uint32",
4013
+ indexed: false,
4014
+ internalType: "uint32"
4015
+ }
4016
+ ],
4017
+ anonymous: false
4018
+ },
4019
+ {
4020
+ type: "event",
4021
+ name: "UpgradeConfirmed",
4022
+ inputs: [
4023
+ {
4024
+ name: "app",
4025
+ type: "address",
4026
+ indexed: true,
4027
+ internalType: "contractIApp"
4028
+ },
4029
+ {
4030
+ name: "pendingReleaseBlockNumber",
4031
+ type: "uint32",
4032
+ indexed: false,
4033
+ internalType: "uint32"
4034
+ }
4035
+ ],
4036
+ anonymous: false
4037
+ },
4038
+ {
4039
+ type: "error",
4040
+ name: "AccountHasActiveApps",
4041
+ inputs: []
4042
+ },
4043
+ {
4044
+ type: "error",
4045
+ name: "AppAlreadyExists",
4046
+ inputs: []
4047
+ },
4048
+ {
4049
+ type: "error",
4050
+ name: "AppDoesNotExist",
4051
+ inputs: []
4052
+ },
4053
+ {
4054
+ type: "error",
4055
+ name: "GlobalMaxActiveAppsExceeded",
4056
+ inputs: []
4057
+ },
4058
+ {
4059
+ type: "error",
4060
+ name: "InvalidAppStatus",
4061
+ inputs: []
4062
+ },
4063
+ {
4064
+ type: "error",
4065
+ name: "InvalidPermissions",
4066
+ inputs: []
4067
+ },
4068
+ {
4069
+ type: "error",
4070
+ name: "InvalidReleaseMetadataURI",
4071
+ inputs: []
4072
+ },
4073
+ {
4074
+ type: "error",
4075
+ name: "InvalidShortString",
4076
+ inputs: []
4077
+ },
4078
+ {
4079
+ type: "error",
4080
+ name: "InvalidSignature",
4081
+ inputs: []
4082
+ },
4083
+ {
4084
+ type: "error",
4085
+ name: "MaxActiveAppsExceeded",
4086
+ inputs: []
4087
+ },
4088
+ {
4089
+ type: "error",
4090
+ name: "MoreThanOneArtifact",
4091
+ inputs: []
4092
+ },
4093
+ {
4094
+ type: "error",
4095
+ name: "NoPendingUpgrade",
4096
+ inputs: []
4097
+ },
4098
+ {
4099
+ type: "error",
4100
+ name: "SignatureExpired",
4101
+ inputs: []
4102
+ },
4103
+ {
4104
+ type: "error",
4105
+ name: "StringTooLong",
4106
+ inputs: [
4107
+ {
4108
+ name: "str",
4109
+ type: "string",
4110
+ internalType: "string"
4111
+ }
4112
+ ]
2943
4113
  }
2944
- return hash;
2945
- }
2946
-
2947
- // src/client/common/contract/caller.ts
2948
- import { encodeFunctionData as encodeFunctionData2, decodeErrorResult as decodeErrorResult2, bytesToHex } from "viem";
2949
-
2950
- // src/client/common/utils/helpers.ts
2951
- import { extractChain, createPublicClient, createWalletClient, http, fallback } from "viem";
2952
- import { sepolia as sepolia2 } from "viem/chains";
2953
- import { privateKeyToAccount } from "viem/accounts";
2954
- function getChainFromID(chainID, fallback2 = sepolia2) {
2955
- const id = Number(chainID);
2956
- return extractChain({ chains: SUPPORTED_CHAINS, id }) || fallback2;
2957
- }
2958
- function addHexPrefix(value) {
2959
- return value.startsWith("0x") ? value : `0x${value}`;
2960
- }
2961
- function stripHexPrefix(value) {
2962
- return value.startsWith("0x") ? value.slice(2) : value;
2963
- }
4114
+ ];
2964
4115
 
2965
- // src/client/common/abis/AppController.json
2966
- var AppController_default = [
4116
+ // src/client/common/abis/AppController.v1_4.json
4117
+ var AppController_v1_4_default = [
2967
4118
  {
2968
4119
  type: "constructor",
2969
4120
  inputs: [
@@ -4647,6 +5798,38 @@ var PermissionController_default = [
4647
5798
  ];
4648
5799
 
4649
5800
  // src/client/common/contract/caller.ts
5801
+ function appControllerAbiFor(environmentConfig) {
5802
+ return environmentConfig.releaseAbiVersion === "v1.4" ? AppController_v1_4_default : AppController_default;
5803
+ }
5804
+ function supportsContainerPolicy(environmentConfig) {
5805
+ return environmentConfig.releaseAbiVersion !== "v1.4";
5806
+ }
5807
+ function containerPolicyForViem(policy = EMPTY_CONTAINER_POLICY) {
5808
+ return {
5809
+ args: policy.args,
5810
+ cmdOverride: policy.cmdOverride,
5811
+ env: policy.env.map((e) => ({ key: e.key, value: e.value })),
5812
+ envOverride: policy.envOverride.map((e) => ({ key: e.key, value: e.value })),
5813
+ restartPolicy: policy.restartPolicy
5814
+ };
5815
+ }
5816
+ function releaseForViem(release, environmentConfig) {
5817
+ const base = {
5818
+ rmsRelease: {
5819
+ artifacts: release.rmsRelease.artifacts.map((artifact) => ({
5820
+ digest: `0x${bytesToHex(artifact.digest).slice(2).padStart(64, "0")}`,
5821
+ registry: artifact.registry
5822
+ })),
5823
+ upgradeByTime: release.rmsRelease.upgradeByTime
5824
+ },
5825
+ publicEnv: bytesToHex(release.publicEnv),
5826
+ encryptedEnv: bytesToHex(release.encryptedEnv)
5827
+ };
5828
+ if (!supportsContainerPolicy(environmentConfig)) {
5829
+ return base;
5830
+ }
5831
+ return { ...base, containerPolicy: containerPolicyForViem(release.containerPolicy) };
5832
+ }
4650
5833
  function formatETH(wei) {
4651
5834
  const eth = Number(wei) / 1e18;
4652
5835
  const costStr = eth.toFixed(6);
@@ -4663,7 +5846,7 @@ async function calculateAppID(options) {
4663
5846
  const saltHex = `0x${paddedSaltHex}`;
4664
5847
  const appID = await publicClient.readContract({
4665
5848
  address: environmentConfig.appControllerAddress,
4666
- abi: AppController_default,
5849
+ abi: appControllerAbiFor(environmentConfig),
4667
5850
  functionName: "calculateAppId",
4668
5851
  args: [ownerAddress, saltHex]
4669
5852
  });
@@ -4687,22 +5870,12 @@ async function prepareDeployBatch(options, logger = noopLogger) {
4687
5870
  const saltHexString = bytesToHex(salt).slice(2);
4688
5871
  const paddedSaltHex = saltHexString.padStart(64, "0");
4689
5872
  const saltHex = `0x${paddedSaltHex}`;
4690
- const releaseForViem = {
4691
- rmsRelease: {
4692
- artifacts: release.rmsRelease.artifacts.map((artifact) => ({
4693
- digest: `0x${bytesToHex(artifact.digest).slice(2).padStart(64, "0")}`,
4694
- registry: artifact.registry
4695
- })),
4696
- upgradeByTime: release.rmsRelease.upgradeByTime
4697
- },
4698
- publicEnv: bytesToHex(release.publicEnv),
4699
- encryptedEnv: bytesToHex(release.encryptedEnv)
4700
- };
5873
+ const release_ = releaseForViem(release, environmentConfig);
4701
5874
  const functionName = options.billTo === "app" ? "createAppWithIsolatedBilling" : "createApp";
4702
5875
  const createData = encodeFunctionData2({
4703
- abi: AppController_default,
5876
+ abi: appControllerAbiFor(environmentConfig),
4704
5877
  functionName,
4705
- args: [saltHex, releaseForViem]
5878
+ args: [saltHex, release_]
4706
5879
  });
4707
5880
  const acceptAdminData = encodeFunctionData2({
4708
5881
  abi: PermissionController_default,
@@ -4790,21 +5963,11 @@ async function prepareUpgradeBatch(options) {
4790
5963
  publicLogs,
4791
5964
  needsPermissionChange
4792
5965
  } = options;
4793
- const releaseForViem = {
4794
- rmsRelease: {
4795
- artifacts: release.rmsRelease.artifacts.map((artifact) => ({
4796
- digest: `0x${bytesToHex(artifact.digest).slice(2).padStart(64, "0")}`,
4797
- registry: artifact.registry
4798
- })),
4799
- upgradeByTime: release.rmsRelease.upgradeByTime
4800
- },
4801
- publicEnv: bytesToHex(release.publicEnv),
4802
- encryptedEnv: bytesToHex(release.encryptedEnv)
4803
- };
5966
+ const release_ = releaseForViem(release, environmentConfig);
4804
5967
  const upgradeData = encodeFunctionData2({
4805
- abi: AppController_default,
5968
+ abi: appControllerAbiFor(environmentConfig),
4806
5969
  functionName: "upgradeApp",
4807
- args: [appID, releaseForViem]
5970
+ args: [appID, release_]
4808
5971
  });
4809
5972
  const executions = [
4810
5973
  {
@@ -4938,7 +6101,7 @@ ${pendingMessage}`);
4938
6101
  if (callError.data) {
4939
6102
  try {
4940
6103
  const decoded = decodeErrorResult2({
4941
- abi: AppController_default,
6104
+ abi: appControllerAbiFor(environmentConfig),
4942
6105
  data: callError.data
4943
6106
  });
4944
6107
  const formattedError = formatAppControllerError(decoded);
@@ -4991,7 +6154,7 @@ function formatAppControllerError(decoded) {
4991
6154
  async function getActiveAppCount(publicClient, environmentConfig, user) {
4992
6155
  const count = await publicClient.readContract({
4993
6156
  address: environmentConfig.appControllerAddress,
4994
- abi: AppController_default,
6157
+ abi: appControllerAbiFor(environmentConfig),
4995
6158
  functionName: "getActiveAppCount",
4996
6159
  args: [user]
4997
6160
  });
@@ -5000,7 +6163,7 @@ async function getActiveAppCount(publicClient, environmentConfig, user) {
5000
6163
  async function getMaxActiveAppsPerUser(publicClient, environmentConfig, user) {
5001
6164
  const quota = await publicClient.readContract({
5002
6165
  address: environmentConfig.appControllerAddress,
5003
- abi: AppController_default,
6166
+ abi: appControllerAbiFor(environmentConfig),
5004
6167
  functionName: "getMaxActiveAppsPerUser",
5005
6168
  args: [user]
5006
6169
  });
@@ -5009,7 +6172,7 @@ async function getMaxActiveAppsPerUser(publicClient, environmentConfig, user) {
5009
6172
  async function getBillingType(publicClient, environmentConfig, app) {
5010
6173
  const result = await publicClient.readContract({
5011
6174
  address: environmentConfig.appControllerAddress,
5012
- abi: AppController_default,
6175
+ abi: appControllerAbiFor(environmentConfig),
5013
6176
  functionName: "getBillingType",
5014
6177
  args: [app]
5015
6178
  });
@@ -5018,7 +6181,7 @@ async function getBillingType(publicClient, environmentConfig, app) {
5018
6181
  async function getAppsByBillingAccount(publicClient, environmentConfig, account, offset, limit) {
5019
6182
  const result = await publicClient.readContract({
5020
6183
  address: environmentConfig.appControllerAddress,
5021
- abi: AppController_default,
6184
+ abi: appControllerAbiFor(environmentConfig),
5022
6185
  functionName: "getAppsByBillingAccount",
5023
6186
  args: [account, offset, limit]
5024
6187
  });
@@ -5072,6 +6235,35 @@ async function undelegate(options, logger = noopLogger) {
5072
6235
  return hash;
5073
6236
  }
5074
6237
 
6238
+ // src/client/common/gas/insufficientGas.ts
6239
+ import { formatEther } from "viem";
6240
+ var InsufficientGasError = class extends Error {
6241
+ constructor(args) {
6242
+ const requiredEth = formatEther(args.requiredWei);
6243
+ const availableEth = formatEther(args.availableWei);
6244
+ super(
6245
+ `Insufficient ETH for gas: wallet ${args.address} has ${availableEth} ETH but this transaction needs ~${requiredEth} ETH.
6246
+ Compute credits do not pay on-chain gas \u2014 fund the wallet with ETH and retry.`
6247
+ );
6248
+ this.name = "InsufficientGasError";
6249
+ this.address = args.address;
6250
+ this.requiredWei = args.requiredWei;
6251
+ this.availableWei = args.availableWei;
6252
+ this.requiredEth = requiredEth;
6253
+ this.availableEth = availableEth;
6254
+ }
6255
+ };
6256
+ async function assertSufficientGas(args) {
6257
+ const availableWei = await args.publicClient.getBalance({ address: args.address });
6258
+ if (availableWei < args.gasEstimate.maxCostWei) {
6259
+ throw new InsufficientGasError({
6260
+ address: args.address,
6261
+ requiredWei: args.gasEstimate.maxCostWei,
6262
+ availableWei
6263
+ });
6264
+ }
6265
+ }
6266
+
5075
6267
  // src/client/common/utils/userapi.ts
5076
6268
  import axios2 from "axios";
5077
6269
 
@@ -5098,43 +6290,13 @@ async function calculatePermissionSignature(options) {
5098
6290
  });
5099
6291
  return { signature, digest };
5100
6292
  }
5101
- var generateBillingSigData = (product, expiry) => {
5102
- return {
5103
- domain: {
5104
- name: "EigenCloud Billing API",
5105
- version: "1"
5106
- },
5107
- types: {
5108
- BillingAuth: [
5109
- { name: "product", type: "string" },
5110
- { name: "expiry", type: "uint256" }
5111
- ]
5112
- },
5113
- primaryType: "BillingAuth",
5114
- message: {
5115
- product,
5116
- expiry
5117
- }
5118
- };
5119
- };
5120
- async function calculateBillingAuthSignature(options) {
5121
- const { walletClient, product, expiry } = options;
5122
- const account = walletClient.account;
5123
- if (!account) {
5124
- throw new Error("WalletClient must have an account attached");
5125
- }
5126
- const signature = await walletClient.signTypedData({
5127
- account,
5128
- ...generateBillingSigData(product, expiry)
5129
- });
5130
- return { signature, expiry };
5131
- }
5132
6293
 
5133
6294
  // src/client/common/utils/retry.ts
5134
6295
  import axios from "axios";
5135
6296
  var MAX_RETRIES = 5;
5136
6297
  var INITIAL_BACKOFF_MS = 1e3;
5137
6298
  var MAX_BACKOFF_MS = 3e4;
6299
+ var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 502, 503, 504]);
5138
6300
  function sleep(ms) {
5139
6301
  return new Promise((resolve2) => setTimeout(resolve2, ms));
5140
6302
  }
@@ -5154,7 +6316,7 @@ async function requestWithRetry(config) {
5154
6316
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
5155
6317
  const res = await axios({ ...config, validateStatus: () => true });
5156
6318
  lastResponse = res;
5157
- if (res.status !== 429) {
6319
+ if (!RETRYABLE_STATUSES.has(res.status)) {
5158
6320
  return res;
5159
6321
  }
5160
6322
  if (attempt < MAX_RETRIES) {
@@ -5183,7 +6345,7 @@ var CanViewAppLogsPermission = "0x2fd3f2fe";
5183
6345
  var CanViewSensitiveAppInfoPermission = "0x0e67b22f";
5184
6346
  var CanUpdateAppProfilePermission = "0x036fef61";
5185
6347
  function getDefaultClientId() {
5186
- const version = true ? "1.0.0-devep7" : "0.0.0";
6348
+ const version = true ? "1.0.0" : "0.0.0";
5187
6349
  return `ecloud-sdk/v${version}`;
5188
6350
  }
5189
6351
  var UserApiClient = class {
@@ -5218,7 +6380,6 @@ var UserApiClient = class {
5218
6380
  status: app.app_status,
5219
6381
  ip: app.ip,
5220
6382
  machineType: app.machine_type,
5221
- hostname: app.hostname,
5222
6383
  profile: app.profile,
5223
6384
  metrics: app.metrics,
5224
6385
  evmAddresses,
@@ -5284,28 +6445,6 @@ var UserApiClient = class {
5284
6445
  status: app.app_status || app.App_Status || ""
5285
6446
  }));
5286
6447
  }
5287
- /**
5288
- * Get deployments for an app from the gRPC-gateway endpoint.
5289
- * Returns deployment records including upgrade_phase for tracking upgrade progress.
5290
- *
5291
- * Endpoint: GET /v1/apps/:appAddress/deployments
5292
- */
5293
- async getDeployments(appAddress) {
5294
- const endpoint = `${this.config.userApiServerURL}/v1/apps/${appAddress}/deployments`;
5295
- const response = await this.makeEIP712AuthenticatedRequest(endpoint);
5296
- const result = await response.json();
5297
- const deployments = result.deployments || [];
5298
- return deployments.map((dep) => ({
5299
- id: dep.id || "",
5300
- externalId: dep.external_id || dep.externalId || "",
5301
- endpoint: dep.endpoint || "",
5302
- releaseId: dep.release_id || dep.releaseId || "",
5303
- upgradePhase: dep.upgrade_phase || dep.upgradePhase || "",
5304
- replacesDeploymentId: dep.replaces_deployment_id || dep.replacesDeploymentId || "",
5305
- createdAt: dep.created_at || dep.createdAt || "",
5306
- updatedAt: dep.updated_at || dep.updatedAt || ""
5307
- }));
5308
- }
5309
6448
  /**
5310
6449
  * Upload app profile information with optional image
5311
6450
  *
@@ -5439,48 +6578,6 @@ Please check:
5439
6578
  "X-eigenx-expiry": expiry.toString()
5440
6579
  };
5441
6580
  }
5442
- /**
5443
- * Make an EIP-712 authenticated request to the gRPC-gateway endpoints.
5444
- * Uses the billing/compute auth pattern: Authorization + X-Account + X-Expiry headers.
5445
- */
5446
- async makeEIP712AuthenticatedRequest(url) {
5447
- const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
5448
- const { signature } = await calculateBillingAuthSignature({
5449
- walletClient: this.walletClient,
5450
- product: "compute",
5451
- expiry
5452
- });
5453
- const headers = {
5454
- Authorization: `Bearer ${signature}`,
5455
- "X-Account": this.address,
5456
- "X-Expiry": expiry.toString(),
5457
- "x-client-id": this.clientId
5458
- };
5459
- try {
5460
- const response = await requestWithRetry({
5461
- method: "GET",
5462
- url,
5463
- headers,
5464
- maxRedirects: 0,
5465
- withCredentials: true
5466
- });
5467
- const status = response.status;
5468
- if (status < 200 || status >= 300) {
5469
- const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
5470
- throw new Error(`gRPC-gateway request failed: ${status} - ${body}`);
5471
- }
5472
- return {
5473
- json: async () => response.data,
5474
- text: async () => typeof response.data === "string" ? response.data : JSON.stringify(response.data)
5475
- };
5476
- } catch (error) {
5477
- if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
5478
- const cause = error.cause?.message || error.cause || error.message;
5479
- throw new Error(`Failed to connect to API at ${url}: ${cause}`);
5480
- }
5481
- throw error;
5482
- }
5483
- }
5484
6581
  // ==========================================================================
5485
6582
  // SIWE Session Management
5486
6583
  // ==========================================================================
@@ -5586,8 +6683,36 @@ function transformAppRelease(raw) {
5586
6683
 
5587
6684
  // src/client/common/contract/watcher.ts
5588
6685
  var WATCH_POLL_INTERVAL_SECONDS = 5;
6686
+ var WATCH_HEARTBEAT_INTERVAL_SECONDS = 30;
6687
+ var WATCH_DEFAULT_TIMEOUT_SECONDS = 10 * 60;
5589
6688
  var APP_STATUS_RUNNING = "Running";
5590
6689
  var APP_STATUS_FAILED = "Failed";
6690
+ var WatchTimeoutError = class extends Error {
6691
+ constructor(args) {
6692
+ super(
6693
+ args.message ?? `Timed out after ${args.elapsedSeconds}s waiting for app ${args.appId} (last status: ${args.lastStatus ?? "unknown"})`
6694
+ );
6695
+ this.name = "WatchTimeoutError";
6696
+ this.appId = args.appId;
6697
+ this.elapsedSeconds = args.elapsedSeconds;
6698
+ this.lastStatus = args.lastStatus;
6699
+ this.timeoutSeconds = args.timeoutSeconds;
6700
+ }
6701
+ };
6702
+ function resolveWatchTimeoutSeconds(explicit) {
6703
+ if (typeof explicit === "number" && Number.isFinite(explicit) && explicit > 0) {
6704
+ return Math.floor(explicit);
6705
+ }
6706
+ const raw = process.env.ECLOUD_WATCH_TIMEOUT_SECONDS;
6707
+ if (raw === void 0 || raw === "") {
6708
+ return WATCH_DEFAULT_TIMEOUT_SECONDS;
6709
+ }
6710
+ const parsed = Number(raw);
6711
+ if (!Number.isFinite(parsed) || parsed <= 0) {
6712
+ return WATCH_DEFAULT_TIMEOUT_SECONDS;
6713
+ }
6714
+ return Math.floor(parsed);
6715
+ }
5591
6716
  async function watchUntilRunning(options, logger) {
5592
6717
  const { walletClient, publicClient, environmentConfig, appId } = options;
5593
6718
  const userApiClient = new UserApiClient(environmentConfig, walletClient, publicClient);
@@ -5618,8 +6743,20 @@ async function watchUntilRunning(options, logger) {
5618
6743
  return false;
5619
6744
  };
5620
6745
  const startTime = Date.now();
6746
+ const timeoutSeconds = resolveWatchTimeoutSeconds(options.timeoutSeconds);
5621
6747
  let lastLoggedStatus;
6748
+ let lastHeartbeatAt = startTime;
5622
6749
  while (true) {
6750
+ const elapsedMs = Date.now() - startTime;
6751
+ const elapsed = Math.round(elapsedMs / 1e3);
6752
+ if (elapsed >= timeoutSeconds) {
6753
+ throw new WatchTimeoutError({
6754
+ appId,
6755
+ elapsedSeconds: elapsed,
6756
+ lastStatus: lastLoggedStatus,
6757
+ timeoutSeconds
6758
+ });
6759
+ }
5623
6760
  try {
5624
6761
  const info = await userApiClient.getInfos([appId], 1);
5625
6762
  if (info.length === 0) {
@@ -5629,62 +6766,35 @@ async function watchUntilRunning(options, logger) {
5629
6766
  const appInfo = info[0];
5630
6767
  const currentStatus = appInfo.status;
5631
6768
  const currentIP = appInfo.ip || "";
5632
- const elapsed = Math.round((Date.now() - startTime) / 1e3);
5633
6769
  if (currentStatus !== lastLoggedStatus) {
5634
6770
  logger.info(`Status: ${currentStatus} (${elapsed}s)`);
5635
6771
  lastLoggedStatus = currentStatus;
6772
+ lastHeartbeatAt = Date.now();
6773
+ } else if (Date.now() - lastHeartbeatAt >= WATCH_HEARTBEAT_INTERVAL_SECONDS * 1e3) {
6774
+ logger.info(`Status: ${currentStatus} (${elapsed}s)`);
6775
+ lastHeartbeatAt = Date.now();
5636
6776
  }
5637
6777
  if (stopCondition(currentStatus, currentIP)) {
5638
6778
  return currentIP || void 0;
5639
6779
  }
5640
6780
  await sleep2(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5641
6781
  } catch (error) {
6782
+ if (error instanceof WatchTimeoutError) {
6783
+ throw error;
6784
+ }
5642
6785
  logger.warn(`Failed to fetch app info: ${error.message}`);
5643
6786
  await sleep2(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5644
6787
  }
5645
6788
  }
5646
6789
  }
5647
6790
  var APP_STATUS_STOPPED = "Stopped";
5648
- var UPGRADE_PHASE_LABELS = {
5649
- provisioning: "Provisioning new instance",
5650
- health_check: "Running health checks",
5651
- draining: "Switching traffic & draining old instance",
5652
- complete: "Complete",
5653
- rolling_back: "Rolling back",
5654
- rollback_done: "Rollback complete",
5655
- db_handoff: "Database handoff",
5656
- // Prewarm-detach phases
5657
- awaiting_userdata: "Waiting for instance readiness",
5658
- draining_old: "Draining old instance",
5659
- detached: "Detaching storage",
5660
- attached_to_new: "Attaching storage to new instance",
5661
- finalizing: "Finalizing new instance",
5662
- flipping: "Switching traffic",
5663
- teardown_old: "Cleaning up old instance"
5664
- };
5665
- async function fetchUpgradePhase(userApiClient, appId) {
5666
- try {
5667
- const deployments = await userApiClient.getDeployments(appId);
5668
- if (deployments.length === 0) return void 0;
5669
- const sorted = [...deployments].sort((a, b) => {
5670
- const ta = a.createdAt ? new Date(a.createdAt).getTime() : 0;
5671
- const tb = b.createdAt ? new Date(b.createdAt).getTime() : 0;
5672
- return tb - ta;
5673
- });
5674
- return sorted[0].upgradePhase || void 0;
5675
- } catch {
5676
- return void 0;
5677
- }
5678
- }
5679
6791
  async function watchUntilUpgradeComplete(options, logger) {
5680
6792
  const { walletClient, publicClient, environmentConfig, appId } = options;
6793
+ const timeoutSeconds = resolveWatchTimeoutSeconds(options.timeoutSeconds);
5681
6794
  const userApiClient = new UserApiClient(environmentConfig, walletClient, publicClient);
5682
6795
  let initialStatus;
5683
6796
  let initialIP;
5684
6797
  let hasChanged = false;
5685
- const startTime = Date.now();
5686
- let lastLoggedStatus;
5687
- let lastLoggedPhase;
5688
6798
  const stopCondition = (status, ip) => {
5689
6799
  if (!initialStatus) {
5690
6800
  initialStatus = status;
@@ -5718,37 +6828,44 @@ async function watchUntilUpgradeComplete(options, logger) {
5718
6828
  }
5719
6829
  return false;
5720
6830
  };
6831
+ const startTime = Date.now();
6832
+ const deadline = startTime + timeoutSeconds * 1e3;
6833
+ let lastLoggedStatus;
6834
+ let lastObservedStatus;
5721
6835
  while (true) {
6836
+ if (Date.now() >= deadline) {
6837
+ const elapsedSeconds = Math.round((Date.now() - startTime) / 1e3);
6838
+ throw new WatchTimeoutError({
6839
+ appId,
6840
+ lastStatus: lastObservedStatus,
6841
+ elapsedSeconds,
6842
+ timeoutSeconds
6843
+ });
6844
+ }
5722
6845
  try {
5723
- const [infoResult, upgradePhase] = await Promise.all([
5724
- userApiClient.getInfos([appId], 1),
5725
- fetchUpgradePhase(userApiClient, appId)
5726
- ]);
5727
- if (infoResult.length === 0) {
6846
+ const info = await userApiClient.getInfos([appId], 1);
6847
+ if (info.length === 0) {
5728
6848
  await sleep2(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5729
6849
  continue;
5730
6850
  }
5731
- const appInfo = infoResult[0];
6851
+ const appInfo = info[0];
5732
6852
  const currentStatus = appInfo.status;
5733
6853
  const currentIP = appInfo.ip || "";
6854
+ lastObservedStatus = currentStatus;
5734
6855
  const elapsed = Math.round((Date.now() - startTime) / 1e3);
5735
- if (upgradePhase && upgradePhase !== lastLoggedPhase) {
5736
- const label = UPGRADE_PHASE_LABELS[upgradePhase] || upgradePhase;
5737
- logger.info(`Phase: ${label} (${elapsed}s)`);
5738
- lastLoggedPhase = upgradePhase;
5739
- }
5740
- if (!upgradePhase && currentStatus !== lastLoggedStatus) {
6856
+ if (currentStatus !== lastLoggedStatus) {
5741
6857
  logger.info(`Status: ${currentStatus} (${elapsed}s)`);
5742
6858
  lastLoggedStatus = currentStatus;
5743
6859
  }
5744
6860
  if (stopCondition(currentStatus, currentIP)) {
5745
- const totalElapsed = Math.round((Date.now() - startTime) / 1e3);
5746
- logger.info(`Upgrade completed in ${totalElapsed}s`);
5747
6861
  return;
5748
6862
  }
5749
6863
  await sleep2(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5750
6864
  } catch (error) {
5751
- if (error.message?.includes("entered")) {
6865
+ if (error instanceof WatchTimeoutError) {
6866
+ throw error;
6867
+ }
6868
+ if (typeof error?.message === "string" && error.message.includes("Failed")) {
5752
6869
  throw error;
5753
6870
  }
5754
6871
  logger.warn(`Failed to fetch app info: ${error.message}`);
@@ -5859,27 +6976,28 @@ var ChainAddresses = {
5859
6976
  PermissionController: "0x44632dfBdCb6D3E21EF613B0ca8A6A0c618F5a37"
5860
6977
  }
5861
6978
  };
5862
- var PLATFORM_ENV_TESTNET_SEPOLIA = "testnet-sepolia";
5863
- var PLATFORM_ENV_MAINNET_ETHEREUM = "mainnet-ethereum";
5864
- var DEFAULT_APP_BASE_DOMAIN = "eigencloud.xyz";
5865
6979
  var ENVIRONMENTS = {
5866
6980
  "sepolia-dev": {
5867
6981
  name: "sepolia",
5868
6982
  build: "dev",
5869
6983
  appControllerAddress: "0xa86DC1C47cb2518327fB4f9A1627F51966c83B92",
6984
+ releaseAbiVersion: "v1.5",
6985
+ // AppController upgraded to v1.5.x (containerPolicy)
5870
6986
  permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
5871
6987
  erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
5872
6988
  kmsServerURL: "http://10.128.0.57:8080",
5873
6989
  userApiServerURL: "https://userapi-compute-sepolia-dev.eigencloud.xyz",
5874
6990
  defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com",
5875
6991
  usdcCreditsAddress: "0xbdA3897c3A428763B59015C64AB766c288C97376",
5876
- platformEnv: PLATFORM_ENV_TESTNET_SEPOLIA,
5877
- appBaseDomain: DEFAULT_APP_BASE_DOMAIN
6992
+ baseUsdcCreditsAddress: "0x7673a47463F80c6a3553Db9E54c8cDcd5313d0ac",
6993
+ baseRPCURL: "https://base-sepolia-rpc.publicnode.com"
5878
6994
  },
5879
6995
  sepolia: {
5880
6996
  name: "sepolia",
5881
6997
  build: "prod",
5882
6998
  appControllerAddress: "0x0dd810a6ffba6a9820a10d97b659f07d8d23d4E2",
6999
+ releaseAbiVersion: "v1.4",
7000
+ // prod still on AppController v1.4.0 (3-field Release)
5883
7001
  permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
5884
7002
  erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
5885
7003
  kmsServerURL: "http://10.128.15.203:8080",
@@ -5887,21 +7005,21 @@ var ENVIRONMENTS = {
5887
7005
  defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com",
5888
7006
  billingRPCURL: "https://ethereum-rpc.publicnode.com",
5889
7007
  usdcCreditsAddress: "0xed9c88640ca9149Bd9f7ee6620074af10F2E145d",
5890
- platformEnv: PLATFORM_ENV_TESTNET_SEPOLIA,
5891
- appBaseDomain: DEFAULT_APP_BASE_DOMAIN
7008
+ baseUsdcCreditsAddress: "0x7673a47463F80c6a3553Db9E54c8cDcd5313d0ac",
7009
+ baseRPCURL: "https://base-sepolia-rpc.publicnode.com"
5892
7010
  },
5893
7011
  "mainnet-alpha": {
5894
7012
  name: "mainnet-alpha",
5895
7013
  build: "prod",
5896
7014
  appControllerAddress: "0xc38d35Fc995e75342A21CBd6D770305b142Fbe67",
7015
+ releaseAbiVersion: "v1.4",
7016
+ // prod still on AppController v1.4.0 (3-field Release)
5897
7017
  permissionControllerAddress: ChainAddresses[MAINNET_CHAIN_ID].PermissionController,
5898
7018
  erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
5899
7019
  kmsServerURL: "http://10.128.0.2:8080",
5900
7020
  userApiServerURL: "https://userapi-compute.eigencloud.xyz",
5901
7021
  defaultRPCURL: "https://ethereum-rpc.publicnode.com",
5902
- usdcCreditsAddress: "0xed9c88640ca9149Bd9f7ee6620074af10F2E145d",
5903
- platformEnv: PLATFORM_ENV_MAINNET_ETHEREUM,
5904
- appBaseDomain: DEFAULT_APP_BASE_DOMAIN
7022
+ usdcCreditsAddress: "0xed9c88640ca9149Bd9f7ee6620074af10F2E145d"
5905
7023
  }
5906
7024
  };
5907
7025
  var CHAIN_ID_TO_ENVIRONMENT = {
@@ -5945,7 +7063,7 @@ function getEnvironmentConfig(environment, chainID) {
5945
7063
  };
5946
7064
  }
5947
7065
  function getBuildType() {
5948
- const buildTimeType = true ? "dev"?.toLowerCase() : void 0;
7066
+ const buildTimeType = true ? "prod"?.toLowerCase() : void 0;
5949
7067
  const runtimeType = process.env.BUILD_TYPE?.toLowerCase();
5950
7068
  const buildType = buildTimeType || runtimeType;
5951
7069
  if (buildType === "dev") {
@@ -6296,6 +7414,11 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
6296
7414
  executions: batch.executions,
6297
7415
  authorizationList
6298
7416
  });
7417
+ await assertSufficientGas({
7418
+ publicClient: batch.publicClient,
7419
+ address: batch.walletClient.account.address,
7420
+ gasEstimate
7421
+ });
6299
7422
  const data = {
6300
7423
  appId: batch.appId,
6301
7424
  salt: batch.salt,
@@ -6554,6 +7677,11 @@ async function prepareDeploy(options, logger = defaultLogger) {
6554
7677
  executions: batch.executions,
6555
7678
  authorizationList
6556
7679
  });
7680
+ await assertSufficientGas({
7681
+ publicClient: batch.publicClient,
7682
+ address: batch.walletClient.account.address,
7683
+ gasEstimate
7684
+ });
6557
7685
  const data = {
6558
7686
  appId: batch.appId,
6559
7687
  salt: batch.salt,
@@ -6590,7 +7718,7 @@ async function executeDeploy(options) {
6590
7718
  }
6591
7719
  );
6592
7720
  }
6593
- async function watchDeployment(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry) {
7721
+ async function watchDeployment(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry, opts) {
6594
7722
  return withSDKTelemetry(
6595
7723
  {
6596
7724
  functionName: "watchDeployment",
@@ -6606,7 +7734,8 @@ async function watchDeployment(appId, walletClient, publicClient, environmentCon
6606
7734
  walletClient,
6607
7735
  publicClient,
6608
7736
  environmentConfig,
6609
- appId
7737
+ appId,
7738
+ timeoutSeconds: opts?.timeoutSeconds
6610
7739
  },
6611
7740
  logger
6612
7741
  );
@@ -6702,6 +7831,11 @@ async function prepareUpgradeFromVerifiableBuild(options, logger = defaultLogger
6702
7831
  executions: batch.executions,
6703
7832
  authorizationList
6704
7833
  });
7834
+ await assertSufficientGas({
7835
+ publicClient: batch.publicClient,
7836
+ address: batch.walletClient.account.address,
7837
+ gasEstimate
7838
+ });
6705
7839
  const data = {
6706
7840
  appId: batch.appId,
6707
7841
  executions: batch.executions,
@@ -6891,6 +8025,11 @@ async function prepareUpgrade(options, logger = defaultLogger) {
6891
8025
  executions: batch.executions,
6892
8026
  authorizationList
6893
8027
  });
8028
+ await assertSufficientGas({
8029
+ publicClient: batch.publicClient,
8030
+ address: batch.walletClient.account.address,
8031
+ gasEstimate
8032
+ });
6894
8033
  const data = {
6895
8034
  appId: batch.appId,
6896
8035
  executions: batch.executions,
@@ -6925,7 +8064,7 @@ async function executeUpgrade(options) {
6925
8064
  }
6926
8065
  );
6927
8066
  }
6928
- async function watchUpgrade(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry) {
8067
+ async function watchUpgrade(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry, opts) {
6929
8068
  return withSDKTelemetry(
6930
8069
  {
6931
8070
  functionName: "watchUpgrade",
@@ -6941,7 +8080,8 @@ async function watchUpgrade(appId, walletClient, publicClient, environmentConfig
6941
8080
  walletClient,
6942
8081
  publicClient,
6943
8082
  environmentConfig,
6944
- appId
8083
+ appId,
8084
+ timeoutSeconds: opts?.timeoutSeconds
6945
8085
  },
6946
8086
  logger
6947
8087
  );
@@ -7633,7 +8773,7 @@ function createAppModule(ctx) {
7633
8773
  }
7634
8774
  const account = walletClient.account;
7635
8775
  const environment = getEnvironmentConfig(ctx.environment);
7636
- const logger = getLogger(ctx.verbose);
8776
+ const logger = ctx.logger ?? getLogger(ctx.verbose);
7637
8777
  return {
7638
8778
  async create(opts) {
7639
8779
  return createApp(opts, logger);
@@ -7745,14 +8885,15 @@ function createAppModule(ctx) {
7745
8885
  imageRef: result.imageRef
7746
8886
  };
7747
8887
  },
7748
- async watchDeployment(appId) {
8888
+ async watchDeployment(appId, opts) {
7749
8889
  return watchDeployment(
7750
8890
  appId,
7751
8891
  walletClient,
7752
8892
  publicClient,
7753
8893
  environment,
7754
8894
  logger,
7755
- skipTelemetry
8895
+ skipTelemetry,
8896
+ opts
7756
8897
  );
7757
8898
  },
7758
8899
  // Granular upgrade control
@@ -7810,8 +8951,16 @@ function createAppModule(ctx) {
7810
8951
  imageRef: result.imageRef
7811
8952
  };
7812
8953
  },
7813
- async watchUpgrade(appId) {
7814
- return watchUpgrade(appId, walletClient, publicClient, environment, logger, skipTelemetry);
8954
+ async watchUpgrade(appId, opts) {
8955
+ return watchUpgrade(
8956
+ appId,
8957
+ walletClient,
8958
+ publicClient,
8959
+ environment,
8960
+ logger,
8961
+ skipTelemetry,
8962
+ opts
8963
+ );
7815
8964
  },
7816
8965
  // Profile management
7817
8966
  async setProfile(appId, profile) {