@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.cjs CHANGED
@@ -167,7 +167,7 @@ __export(compute_exports, {
167
167
  module.exports = __toCommonJS(compute_exports);
168
168
 
169
169
  // src/client/modules/compute/app/index.ts
170
- var import_viem6 = require("viem");
170
+ var import_viem7 = require("viem");
171
171
 
172
172
  // src/client/common/docker/build.ts
173
173
  var child_process = __toESM(require("child_process"), 1);
@@ -175,7 +175,7 @@ var import_util = require("util");
175
175
 
176
176
  // src/client/common/constants.ts
177
177
  var import_chains = require("viem/chains");
178
- var SUPPORTED_CHAINS = [import_chains.mainnet, import_chains.sepolia];
178
+ var SUPPORTED_CHAINS = [import_chains.mainnet, import_chains.sepolia, import_chains.baseSepolia];
179
179
  var DOCKER_PLATFORM = "linux/amd64";
180
180
  var REGISTRY_PROPAGATION_WAIT_SECONDS = 3;
181
181
  var LAYERED_DOCKERFILE_NAME = "Dockerfile.eigencompute";
@@ -183,7 +183,6 @@ var ENV_SOURCE_SCRIPT_NAME = "compute-source-env.sh";
183
183
  var KMS_CLIENT_BINARY_NAME = "kms-client";
184
184
  var KMS_SIGNING_KEY_NAME = "kms-signing-public-key.pem";
185
185
  var TLS_KEYGEN_BINARY_NAME = "tls-keygen";
186
- var DRAIN_WATCHER_BINARY_NAME = "ecloud-drain-watcher";
187
186
  var CADDYFILE_NAME = "Caddyfile";
188
187
  var LAYERED_BUILD_DIR_PREFIX = "ecloud-layered-build";
189
188
 
@@ -470,12 +469,14 @@ USER root
470
469
  {{/if}}
471
470
 
472
471
  # Copy core TEE components
472
+ # CA bundle for kms-client / tls-keygen to validate HTTPS calls to
473
+ # eigencloud.xyz endpoints. Bundled at a non-standard path and consumed
474
+ # only via SSL_CERT_FILE in compute-source-env.sh, so the user's
475
+ # /etc/ssl/ is never touched.
476
+ COPY --from=alpine:3.20.10 /etc/ssl/certs/ca-certificates.crt /usr/local/share/eigenx-ca-certs.crt
473
477
  COPY compute-source-env.sh /usr/local/bin/
474
478
  COPY kms-client /usr/local/bin/
475
479
  COPY kms-signing-public-key.pem /usr/local/bin/
476
- {{#if includeDrainWatcher}}
477
- COPY ecloud-drain-watcher /usr/local/bin/
478
- {{/if}}
479
480
 
480
481
  {{#if includeTLS}}
481
482
  # Copy Caddy from official image
@@ -489,8 +490,7 @@ COPY Caddyfile /etc/caddy/
489
490
  {{#if originalUser}}
490
491
  # Make binaries executable (755 for executables, 644 for keys)
491
492
  RUN chmod 755 /usr/local/bin/compute-source-env.sh \\
492
- && chmod 755 /usr/local/bin/kms-client{{#if includeDrainWatcher}} \\
493
- && chmod 755 /usr/local/bin/ecloud-drain-watcher{{/if}}{{#if includeTLS}} \\
493
+ && chmod 755 /usr/local/bin/kms-client{{#if includeTLS}} \\
494
494
  && chmod 755 /usr/local/bin/tls-keygen \\
495
495
  && chmod 755 /usr/local/bin/caddy{{/if}} \\
496
496
  && chmod 644 /usr/local/bin/kms-signing-public-key.pem
@@ -500,8 +500,7 @@ ENV __ECLOUD_ORIGINAL_USER={{originalUser}}
500
500
  {{else}}
501
501
  # Make binaries executable (preserve existing permissions, just add execute)
502
502
  RUN chmod +x /usr/local/bin/compute-source-env.sh \\
503
- && chmod +x /usr/local/bin/kms-client{{#if includeDrainWatcher}} \\
504
- && chmod +x /usr/local/bin/ecloud-drain-watcher{{/if}}{{#if includeTLS}} \\
503
+ && chmod +x /usr/local/bin/kms-client{{#if includeTLS}} \\
505
504
  && chmod +x /usr/local/bin/tls-keygen{{/if}}
506
505
  {{/if}}
507
506
 
@@ -514,36 +513,8 @@ LABEL tee.launch_policy.log_redirect={{logRedirect}}
514
513
  LABEL tee.launch_policy.monitoring_memory_allow={{resourceUsageAllow}}
515
514
  {{/if}}
516
515
 
517
- # Allow-list the envvars the ecloud-platform sets via GCE \`tee-env-*\`
518
- # metadata. Without this label, Confidential Space's launcher rejects
519
- # any \`tee-env-*\` override at container-start with
520
- # "env var {...} is not allowed to be overridden on this image" and
521
- # exits with code 1 \u2014 which terminates the VM before the entrypoint
522
- # ever runs. User-supplied env vars flow through KMS (not tee-env-*)
523
- # and don't need to be listed here.
524
- #
525
- # Entries:
526
- # - ECLOUD_PD_EXPECTED set on PD-backed apps so the entrypoint
527
- # (compute-source-env.sh) knows to wait for
528
- # the persistent disk before exec'ing the
529
- # user workload.
530
- # - ECLOUD_PLATFORM_HOST the platform-routed hostname
531
- # (<addr>.<env>.eigencloud.xyz) so the
532
- # entrypoint's setup_tls can issue an ACME
533
- # cert for it. Injected by the CLI into
534
- # publicEnv at deploy/upgrade time and
535
- # propagated by ecloud-platform's
536
- # compute.go as a tee-env-* metadata key.
537
- #
538
- # The CS launcher parses this label as a comma-separated list
539
- # (go-tpm-tools/launcher/spec/launch_policy.go:185 \u2014 strings.Split
540
- # on ","). Quotes are not required; keep the value bare for
541
- # consistency with compute-tee's and eigenx-kms's existing labels.
542
- LABEL tee.launch_policy.allow_env_override=ECLOUD_PD_EXPECTED,ECLOUD_PLATFORM_HOST
543
-
544
516
  LABEL eigenx_cli_version={{ecloudCLIVersion}}
545
517
  LABEL eigenx_vm_image=eigen
546
- LABEL eigenx_container_contract=v1
547
518
 
548
519
  {{#if includeTLS}}
549
520
  # Expose both HTTP and HTTPS ports for Caddy
@@ -565,54 +536,11 @@ var import_handlebars2 = __toESM(require("handlebars"), 1);
565
536
 
566
537
  // src/client/common/templates/compute-source-env.sh.tmpl
567
538
  var compute_source_env_sh_default = `#!/bin/sh
568
- # EigenCompute container entrypoint script
569
- # This script handles KMS secret fetching, TLS setup, and privilege dropping
570
- # before executing the user's application.
571
- #
572
- # Handlebars template variables (replaced at build time by the CLI):
573
- # kmsServerURL - URL of the KMS server
574
- # userAPIURL - URL of the user API (ecloud-platform)
575
- # The KMS signing public key is copied into the image as
576
- # /usr/local/bin/kms-signing-public-key.pem at layer-build time by the CLI.
577
- #
578
- # ecloud-platform divergence from compute-tee:
579
- # This script emits ECLOUD_READY / ECLOUD_FAIL / ECLOUD_AWAITING_USERDATA /
580
- # ECLOUD_DETACHED markers to stdout at key lifecycle points. The GCP
581
- # provisioner's serial-console watcher in ecloud-platform
582
- # (pkg/services/infraService/providers/gcp/compute.go) parses those
583
- # markers to gate "VM ready" and to coordinate the prewarm-detach
584
- # upgrade flow. Without the markers, the platform's waitForStartupReady
585
- # times out at ~10 minutes per deploy, rollback fires, and the VM is
586
- # deleted \u2014 seen in dev on 2026-05-04 with an older copy of this
587
- # template that lacked the markers.
588
- #
589
- # Prewarm-detach contract:
590
- # - If ECLOUD_PD_EXPECTED=1 and /mnt/disks/userdata is not present at boot,
591
- # emit ECLOUD_AWAITING_USERDATA and wait until the disk is attached.
592
- # - On SIGTERM (drain-requested), forward to child, wait for exit, sync
593
- # + unmount /mnt/disks/userdata, emit ECLOUD_DETACHED, exit.
594
- # - ECLOUD_READY is emitted once runtime is bootstrapped (same as before).
595
- # - ECLOUD_FAIL is emitted on any unrecoverable setup error.
596
- # Keep the markers on any line that resolves a lifecycle outcome.
597
- #
598
- # This file is kept in lockstep with
599
- # ecloud-platform/pkg/services/buildService/assets/compute-source-env.sh.tmpl
600
- # \u2014 if you change one, change the other. Differences vs the platform copy
601
- # are intentionally minimal:
602
- # - Handlebars placeholders use the CLI's naming (kmsServerURL,
603
- # userAPIURL) rather than the platform's (KMS_SERVER_URL,
604
- # USER_API_URL). (See top of file for real placeholder syntax \u2014
605
- # not repeated here so Handlebars doesn't expand it in this comment.)
606
- # - KMS signing key is read from a file the CLI copies into the image,
607
- # not heredoc-embedded in the script, because the CLI's image
608
- # layering writes it as a separate file (kms-signing-public-key.pem).
609
- # - TLS binary is \`tls-keygen\` (CLI-bundled) not \`tls-client\`.
610
-
611
539
  echo "compute-source-env.sh: Running setup script..."
612
540
 
613
541
  # Fetch and source environment variables from KMS
614
542
  echo "Fetching secrets from KMS..."
615
- if /usr/local/bin/kms-client \\
543
+ if SSL_CERT_FILE=/usr/local/share/eigenx-ca-certs.crt /usr/local/bin/kms-client \\
616
544
  --kms-server-url "{{kmsServerURL}}" \\
617
545
  --kms-signing-key-file /usr/local/bin/kms-signing-public-key.pem \\
618
546
  --userapi-url "{{userAPIURL}}" \\
@@ -623,187 +551,93 @@ if /usr/local/bin/kms-client \\
623
551
  else
624
552
  echo "compute-source-env.sh: ERROR - Failed to fetch environment variables from KMS"
625
553
  echo "compute-source-env.sh: Exiting - cannot start user workload without KMS secrets"
626
- echo "ECLOUD_FAIL kms_bootstrap"
627
554
  exit 1
628
555
  fi
629
556
 
630
- # issue_cert_for runs tls-keygen for a single hostname and copies the
631
- # produced fullchain/privkey into $1's output directory ($2). Returns
632
- # 0 on success, non-zero on any failure (caller decides whether that's
633
- # fatal). tls-keygen writes to /run/tls/{fullchain,privkey}.pem by
634
- # default; we move those to a per-host subdirectory so the two-site
635
- # Caddyfile can select the right cert by path.
636
- issue_cert_for() {
637
- local host="$1"
638
- local cert_dir="$2"
639
- local mnemonic="$3"
640
- local challenge="$4"
641
- local staging_flag="$5"
642
-
643
- echo "compute-source-env.sh: Obtaining TLS certificate for $host (challenge=$challenge)..."
644
- # Remove any stale default outputs from a prior tls-keygen run
645
- rm -f /run/tls/fullchain.pem /run/tls/privkey.pem
646
- if ! MNEMONIC="$mnemonic" DOMAIN="$host" API_URL="{{userAPIURL}}" /usr/local/bin/tls-keygen \\
647
- -challenge "$challenge" \\
648
- $staging_flag; then
649
- return 1
650
- fi
651
- mkdir -p "$cert_dir"
652
- mv /run/tls/fullchain.pem "$cert_dir/fullchain.pem"
653
- mv /run/tls/privkey.pem "$cert_dir/privkey.pem"
654
- echo "compute-source-env.sh: Cert for $host written to $cert_dir"
655
- }
656
-
657
- # Setup TLS: issues certs for
658
- # - ECLOUD_PLATFORM_HOST (platform-routed <addr>.<env>.eigencloud.xyz),
659
- # when the CLI/platform has set it
660
- # - DOMAIN (user-supplied custom domain), when set and non-localhost
661
- #
662
- # No client-side DNS precheck. Earlier versions tried to gate ACME on
663
- # "does this hostname resolve to my external IP" but that's wrong for
664
- # the platform-routing model (DNS points at the shared nginx NLB, not
665
- # the VM) and was preventing cert issuance on the production path.
666
- # tls-client (eigencompute-containers/tls-client) does its own DNS
667
- # poll before calling ACME and surfaces a clear error when challenges
668
- # can't reach the VM, which is the right place for that check \u2014
669
- # attempting it here from inside the VM cannot tell platform-routed
670
- # from compute-tee-routed apps.
557
+ # Setup TLS if tls-keygen is present (which means TLS was configured at build time)
671
558
  setup_tls() {
672
559
  # If tls-keygen isn't present, TLS wasn't configured during build
673
560
  if [ ! -x /usr/local/bin/tls-keygen ]; then
674
561
  echo "compute-source-env.sh: TLS not configured (no tls-keygen binary)"
675
562
  return 0
676
563
  fi
677
-
678
- local platform_host="\${ECLOUD_PLATFORM_HOST:-}"
679
- local user_domain="\${DOMAIN:-}"
564
+
565
+ local domain="\${DOMAIN:-}"
680
566
  local mnemonic="\${MNEMONIC:-}"
681
-
682
- # Normalize "localhost" DOMAIN to unset \u2014 compute-tee apps often
683
- # set DOMAIN=localhost as a sentinel for "no custom domain".
684
- if [ "$user_domain" = "localhost" ]; then
685
- user_domain=""
686
- fi
687
-
688
- # If the user set DOMAIN to the same value as the platform
689
- # hostname, collapse them. Without this we'd burn a second ACME
690
- # issuance for the exact same name (each VM boot counts against
691
- # Let's Encrypt's 5-certs-per-domain-per-week limit), and Caddy
692
- # would then refuse to start because two site blocks bound to
693
- # the same address is a config error. Also unset DOMAIN so the
694
- # DOMAIN site block in the Caddyfile falls back to its
695
- # "localhost.user.invalid" placeholder and stays dormant.
696
- if [ -n "$platform_host" ] && [ "$user_domain" = "$platform_host" ]; then
697
- echo "compute-source-env.sh: DOMAIN matches ECLOUD_PLATFORM_HOST; skipping duplicate cert"
698
- user_domain=""
699
- unset DOMAIN
700
- fi
701
-
702
- if [ -z "$platform_host" ] && [ -z "$user_domain" ]; then
703
- echo "compute-source-env.sh: TLS skipped (neither ECLOUD_PLATFORM_HOST nor DOMAIN set)"
704
- return 0
567
+
568
+ # Since tls-keygen is present, TLS is expected - validate requirements
569
+ if [ -z "$domain" ] || [ "$domain" = "localhost" ]; then
570
+ echo "compute-source-env.sh: ERROR - TLS binary present but DOMAIN not configured or is localhost"
571
+ echo "compute-source-env.sh: Set DOMAIN environment variable to a valid domain"
572
+ exit 1
705
573
  fi
706
-
574
+
707
575
  if [ -z "$mnemonic" ]; then
708
- echo "compute-source-env.sh: ERROR - TLS requested but MNEMONIC not available"
709
- echo "ECLOUD_FAIL tls_mnemonic_missing"
576
+ echo "compute-source-env.sh: ERROR - TLS binary present but MNEMONIC not available"
577
+ echo "compute-source-env.sh: Cannot obtain TLS certificate without mnemonic"
710
578
  exit 1
711
579
  fi
712
-
580
+
713
581
  if [ ! -x /usr/local/bin/caddy ]; then
714
- echo "compute-source-env.sh: ERROR - TLS requested but Caddy not found"
715
- echo "ECLOUD_FAIL tls_caddy_missing"
582
+ echo "compute-source-env.sh: ERROR - TLS binary present but Caddy not found"
716
583
  exit 1
717
584
  fi
718
-
585
+
586
+ echo "compute-source-env.sh: Setting up TLS for domain: $domain"
587
+
588
+ # Obtain TLS certificate using ACME
589
+ # Default to http-01, but allow override via ACME_CHALLENGE env var
719
590
  local challenge="\${ACME_CHALLENGE:-http-01}"
591
+
592
+ # Check if we should use staging (for testing)
720
593
  local staging_flag=""
721
594
  if [ "\${ACME_STAGING:-false}" = "true" ]; then
722
595
  staging_flag="-staging"
723
- echo "compute-source-env.sh: Using Let's Encrypt STAGING environment"
724
- fi
725
-
726
- local certs_issued=0
727
-
728
- if [ -n "$platform_host" ]; then
729
- if issue_cert_for "$platform_host" "/run/tls/platform" "$mnemonic" "$challenge" "$staging_flag"; then
730
- certs_issued=$((certs_issued + 1))
731
- else
732
- echo "compute-source-env.sh: ERROR - failed to issue cert for platform host $platform_host"
733
- echo "ECLOUD_FAIL tls_setup"
734
- exit 1
735
- fi
736
- fi
737
-
738
- if [ -n "$user_domain" ]; then
739
- if issue_cert_for "$user_domain" "/run/tls/domain" "$mnemonic" "$challenge" "$staging_flag"; then
740
- certs_issued=$((certs_issued + 1))
741
- else
742
- echo "compute-source-env.sh: ERROR - failed to issue cert for user domain $user_domain"
743
- echo "ECLOUD_FAIL tls_setup"
744
- exit 1
745
- fi
596
+ echo "compute-source-env.sh: Using Let's Encrypt STAGING environment (certificates won't be trusted)"
746
597
  fi
747
-
748
- if [ "$certs_issued" -eq 0 ]; then
749
- echo "compute-source-env.sh: no certs issued, skipping Caddy (app will serve plaintext until DNS is wired)"
750
- return 0
598
+
599
+ echo "compute-source-env.sh: Obtaining TLS certificate using $challenge challenge..."
600
+ # Pass the API URL for certificate persistence
601
+ if ! SSL_CERT_FILE=/usr/local/share/eigenx-ca-certs.crt \\
602
+ MNEMONIC="$mnemonic" DOMAIN="$domain" API_URL="{{userAPIURL}}" /usr/local/bin/tls-keygen \\
603
+ -challenge "$challenge" \\
604
+ $staging_flag; then
605
+ echo "compute-source-env.sh: ERROR - Failed to obtain TLS certificate"
606
+ echo "compute-source-env.sh: Certificate issuance failed for $domain"
607
+ exit 1
751
608
  fi
752
-
753
- # Caddy's validate step checks that every \`tls <cert> <key>\` file
754
- # exists, even on site blocks bound to dormant placeholder
755
- # hostnames. The default Caddyfile declares both a platform site
756
- # and a user-domain site; when only one is configured, the other
757
- # block's cert paths are never populated and validate fails with
758
- # "Invalid Caddyfile". Point the unused block at the issued
759
- # block's cert files so validate passes \u2014 the dormant block can't
760
- # receive real traffic (its hostname falls back to
761
- # localhost.{platform,user}.invalid, which Caddy routes by SNI and
762
- # never matches public traffic), so the symlink is never actually
763
- # presented. Skipped when a user-supplied Caddyfile is in use,
764
- # since we don't know what cert paths it references.
765
- if [ -d /run/tls/platform ] && [ ! -e /run/tls/domain/fullchain.pem ]; then
766
- mkdir -p /run/tls/domain
767
- ln -sf /run/tls/platform/fullchain.pem /run/tls/domain/fullchain.pem
768
- ln -sf /run/tls/platform/privkey.pem /run/tls/domain/privkey.pem
769
- elif [ -d /run/tls/domain ] && [ ! -e /run/tls/platform/fullchain.pem ]; then
770
- mkdir -p /run/tls/platform
771
- ln -sf /run/tls/domain/fullchain.pem /run/tls/platform/fullchain.pem
772
- ln -sf /run/tls/domain/privkey.pem /run/tls/platform/privkey.pem
609
+
610
+ echo "compute-source-env.sh: TLS certificate obtained successfully"
611
+
612
+ # Validate Caddyfile before starting
613
+ if ! /usr/local/bin/caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile 2>/dev/null; then
614
+ echo "compute-source-env.sh: ERROR - Invalid Caddyfile"
615
+ echo "compute-source-env.sh: TLS was requested (DOMAIN=$domain) but setup failed"
616
+ exit 1
773
617
  fi
774
-
775
- # Validate Caddyfile before starting. Don't redirect stderr \u2014 when
776
- # validate fails, Caddy's diagnostic is the only signal that lands
777
- # in ReadinessError.SerialTail, so silencing it leaves operators
778
- # staring at a bare "tls_invalid_caddyfile" with no detail.
779
- if [ -f /etc/caddy/Caddyfile ]; then
780
- if ! /usr/local/bin/caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile; then
781
- echo "compute-source-env.sh: ERROR - Invalid Caddyfile"
782
- echo "ECLOUD_FAIL tls_invalid_caddyfile"
618
+
619
+ # Start Caddy in background
620
+ echo "compute-source-env.sh: Starting Caddy reverse proxy..."
621
+
622
+ # Check if Caddy logs should be enabled
623
+ if [ "\${ENABLE_CADDY_LOGS:-false}" = "true" ]; then
624
+ if ! /usr/local/bin/caddy start --config /etc/caddy/Caddyfile --adapter caddyfile 2>&1; then
625
+ echo "compute-source-env.sh: ERROR - Failed to start Caddy"
626
+ echo "compute-source-env.sh: TLS was requested (DOMAIN=$domain) but setup failed"
783
627
  exit 1
784
628
  fi
785
-
786
- echo "compute-source-env.sh: Starting Caddy reverse proxy..."
787
- if [ "\${ENABLE_CADDY_LOGS:-false}" = "true" ]; then
788
- if ! /usr/local/bin/caddy start --config /etc/caddy/Caddyfile --adapter caddyfile 2>&1; then
789
- echo "compute-source-env.sh: ERROR - Failed to start Caddy"
790
- echo "ECLOUD_FAIL tls_caddy_start"
791
- exit 1
792
- fi
793
- else
794
- if ! /usr/local/bin/caddy start --config /etc/caddy/Caddyfile --adapter caddyfile >/dev/null 2>&1; then
795
- echo "compute-source-env.sh: ERROR - Failed to start Caddy"
796
- echo "ECLOUD_FAIL tls_caddy_start"
797
- exit 1
798
- fi
799
- fi
800
-
801
- sleep 2
802
- echo "compute-source-env.sh: Caddy started successfully"
803
629
  else
804
- echo "compute-source-env.sh: No Caddyfile found, skipping Caddy"
630
+ # Redirect Caddy output to /dev/null to silence logs
631
+ if ! /usr/local/bin/caddy start --config /etc/caddy/Caddyfile --adapter caddyfile >/dev/null 2>&1; then
632
+ echo "compute-source-env.sh: ERROR - Failed to start Caddy"
633
+ echo "compute-source-env.sh: TLS was requested (DOMAIN=$domain) but setup failed"
634
+ exit 1
635
+ fi
805
636
  fi
806
-
637
+
638
+ # Give Caddy a moment to fully initialize
639
+ sleep 2
640
+ echo "compute-source-env.sh: Caddy started successfully"
807
641
  return 0
808
642
  }
809
643
 
@@ -814,233 +648,15 @@ setup_tls
814
648
  export KMS_SERVER_URL="{{kmsServerURL}}"
815
649
  export KMS_PUBLIC_KEY="$(cat /usr/local/bin/kms-signing-public-key.pem)"
816
650
 
817
- # \u2500\u2500 Prewarm-detach: wait for PD if expected \u2500\u2500
818
- # Orchestrator sets ECLOUD_PD_EXPECTED=1 on apps using StorageBackend=pd.
819
- # When the prewarm path is used, the new VM boots WITHOUT the disk; we
820
- # signal awaiting-userdata and poll until the disk is attached.
821
- USERDATA_MOUNT="/mnt/disks/userdata"
822
- USERDATA_DEV="/dev/disk/by-id/google-persistent_storage_1"
823
-
824
- wait_for_userdata() {
825
- if [ "\${ECLOUD_PD_EXPECTED:-0}" != "1" ]; then
826
- return 0
827
- fi
828
- if mountpoint -q "$USERDATA_MOUNT" 2>/dev/null; then
829
- echo "compute-source-env.sh: userdata already mounted at $USERDATA_MOUNT"
830
- return 0
831
- fi
832
- # Refuse to proceed if the tools we need for safe first-attach
833
- # detection are missing. Without blkid we cannot tell an empty new
834
- # disk from an already-formatted one \u2014 running mkfs.ext4 on the
835
- # latter would destroy data.
836
- if ! command -v blkid >/dev/null 2>&1; then
837
- echo "ECLOUD_FAIL pd_tools_missing"
838
- exit 1
839
- fi
840
- echo "ECLOUD_AWAITING_USERDATA"
841
- echo "compute-source-env.sh: waiting for PD at $USERDATA_DEV..."
842
- # Poll for up to 10 minutes (120 * 5s). The orchestrator's overall
843
- # attach timeout is shorter; the ceiling here just bounds the wait
844
- # for manual / diagnostic scenarios.
845
- local i=0
846
- local mount_failures=0
847
- while [ "$i" -lt 120 ]; do
848
- if [ -e "$USERDATA_DEV" ]; then
849
- mkdir -p "$USERDATA_MOUNT"
850
- if mount -o noatime "$USERDATA_DEV" "$USERDATA_MOUNT" 2>/dev/null; then
851
- echo "compute-source-env.sh: PD mounted at $USERDATA_MOUNT"
852
- return 0
853
- fi
854
- # Disk present but mount failed. Check whether it has a
855
- # recognized filesystem. \`blkid -s TYPE -o value\` prints the
856
- # FS type (empty if none). We only mkfs when there is
857
- # demonstrably NO filesystem \u2014 never on the basis of blkid
858
- # returning non-zero alone, which could mean "blkid missing"
859
- # or "device busy".
860
- local fstype
861
- fstype=$(blkid -s TYPE -o value "$USERDATA_DEV" 2>/dev/null)
862
- if [ -z "$fstype" ]; then
863
- echo "compute-source-env.sh: formatting $USERDATA_DEV (first attach)"
864
- mkfs.ext4 -F -L eclouddata "$USERDATA_DEV" >/dev/null 2>&1 || {
865
- echo "ECLOUD_FAIL pd_mkfs_failed"
866
- exit 1
867
- }
868
- mount -o noatime "$USERDATA_DEV" "$USERDATA_MOUNT" || {
869
- echo "ECLOUD_FAIL pd_mount_after_format_failed"
870
- exit 1
871
- }
872
- return 0
873
- fi
874
- # Disk has a filesystem but mount still failed. Give it a
875
- # few retries to cover transient cases (device busy, udev
876
- # still settling), but don't pretend this is an attach
877
- # timeout if it persists.
878
- mount_failures=$((mount_failures + 1))
879
- if [ "$mount_failures" -ge 6 ]; then
880
- echo "ECLOUD_FAIL pd_mount_failed"
881
- exit 1
882
- fi
883
- else
884
- # Device disappeared (e.g. udev re-enumeration between
885
- # attach and mount). Reset the consecutive-failure counter
886
- # so only true back-to-back mount failures trip
887
- # pd_mount_failed; a device blip should not steal retries.
888
- mount_failures=0
889
- fi
890
- i=$((i + 1))
891
- sleep 5
892
- done
893
- echo "ECLOUD_FAIL pd_attach_timeout"
894
- exit 1
895
- }
896
-
897
- wait_for_userdata
898
-
899
- # \u2500\u2500 Prewarm-detach: install SIGTERM handler for graceful drain \u2500\u2500
900
- # Orchestrator signals drain by setting the instance metadata key
901
- # ECLOUD_DRAIN_REQUESTED=1, which a host-level agent translates into
902
- # SIGTERM on PID 1. On SIGTERM we:
903
- # 1. Forward to the child (wakes the user's app for graceful exit)
904
- # 2. Wait for child exit
905
- # 3. Sync + unmount the PD
906
- # 4. Emit ECLOUD_DETACHED so the orchestrator can proceed to detach
907
- CHILD_PID=""
908
- _DRAIN_IN_PROGRESS=0
909
-
910
- drain_handler() {
911
- # Guard against re-entry if SIGTERM arrives twice (e.g. both the
912
- # drain_watcher and an external signal fire in quick succession).
913
- if [ "$_DRAIN_IN_PROGRESS" = "1" ]; then
914
- return 0
915
- fi
916
- _DRAIN_IN_PROGRESS=1
917
- echo "compute-source-env.sh: received drain signal, forwarding to child pgid=$CHILD_PID"
918
- if [ -n "$CHILD_PID" ]; then
919
- # Send to the process group so intermediate wrappers (su, sh -c,
920
- # etc.) don't swallow the signal. The leading \`-\` targets the
921
- # pgid, which equals the direct child's pid for a shell-backgrounded
922
- # process. Fall back to the pid alone if pgid signaling fails
923
- # (e.g. kernel older than 3.9 or PID namespace edge cases).
924
- kill -TERM -"$CHILD_PID" 2>/dev/null || kill -TERM "$CHILD_PID" 2>/dev/null || true
925
- # Give the app up to 30s to exit cleanly.
926
- local i=0
927
- while [ "$i" -lt 30 ] && kill -0 "$CHILD_PID" 2>/dev/null; do
928
- i=$((i + 1))
929
- sleep 1
930
- done
931
- if kill -0 "$CHILD_PID" 2>/dev/null; then
932
- echo "compute-source-env.sh: child did not exit in 30s, sending SIGKILL"
933
- kill -KILL -"$CHILD_PID" 2>/dev/null || kill -KILL "$CHILD_PID" 2>/dev/null || true
934
- # Reap the process so its in-flight I/O is flushed to the
935
- # filesystem before we sync + unmount. SIGKILL schedules
936
- # death; wait guarantees it's complete.
937
- wait "$CHILD_PID" 2>/dev/null || true
938
- fi
939
- fi
940
- if [ "\${ECLOUD_PD_EXPECTED:-0}" = "1" ] && mountpoint -q "$USERDATA_MOUNT" 2>/dev/null; then
941
- sync
942
- if umount "$USERDATA_MOUNT" 2>/dev/null; then
943
- echo "compute-source-env.sh: unmounted $USERDATA_MOUNT cleanly"
944
- else
945
- # Force lazy unmount as last resort \u2014 orchestrator still needs
946
- # the DETACHED signal to proceed.
947
- umount -l "$USERDATA_MOUNT" 2>/dev/null || true
948
- echo "compute-source-env.sh: WARNING - used lazy unmount on $USERDATA_MOUNT"
949
- fi
950
- # ECLOUD_DETACHED is strictly a PD-lifecycle signal. Only emit
951
- # it when we actually had a PD mount in play, so serial-log
952
- # parsers and alerting for non-PD apps don't see spurious
953
- # lifecycle markers on routine container SIGTERM.
954
- echo "ECLOUD_DETACHED"
955
- fi
956
- # Always exit 0: drain is a managed shutdown and the orchestrator
957
- # waits on ECLOUD_DETACHED, not the container exit code. Forwarding
958
- # the child's exit status here would make a crash-during-drain look
959
- # like a drain failure to whatever reads the container exit code.
960
- exit 0
961
- }
962
- trap drain_handler TERM
963
-
964
- # \u2500\u2500 Prewarm-detach: background drain watcher \u2500\u2500
965
- # Container metadata delivery in Confidential Space is limited, so we
966
- # poll the instance metadata server for ECLOUD_DRAIN_REQUESTED and
967
- # raise SIGTERM on ourselves when it flips to "1".
968
- #
969
- # Try wget first (present in most Alpine bases), fall back to curl.
970
- # If neither is present, drain watcher is disabled \u2014 the orchestrator
971
- # will hit its drain timeout and fail the upgrade explicitly, which is
972
- # the correct behavior (we cannot silently ignore a drain request).
973
- _fetch_drain_flag() {
974
- local url="http://metadata.google.internal/computeMetadata/v1/instance/attributes/ECLOUD_DRAIN_REQUESTED"
975
- if command -v wget >/dev/null 2>&1; then
976
- wget -q --tries=1 --timeout=2 --header='Metadata-Flavor: Google' -O - "$url" 2>/dev/null
977
- elif command -v curl >/dev/null 2>&1; then
978
- curl -sf --max-time 2 -H 'Metadata-Flavor: Google' "$url" 2>/dev/null
979
- else
980
- return 2
981
- fi
982
- }
983
-
984
- drain_watcher() {
985
- # Preflight: confirm we have an HTTP client
986
- if ! _fetch_drain_flag >/dev/null 2>&1; then
987
- # Either no http client available OR metadata server not
988
- # responding yet. If no client, give up and log; otherwise the
989
- # loop below will retry.
990
- if ! command -v wget >/dev/null 2>&1 && ! command -v curl >/dev/null 2>&1; then
991
- echo "compute-source-env.sh: WARNING - no wget/curl; drain_watcher disabled"
992
- return 0
993
- fi
994
- fi
995
- while true; do
996
- local v
997
- v=$(_fetch_drain_flag || true)
998
- if [ "$v" = "1" ]; then
999
- echo "compute-source-env.sh: drain_watcher saw ECLOUD_DRAIN_REQUESTED=1, signaling PID 1"
1000
- # The CS launcher runs this script directly as PID 1, so
1001
- # kill -TERM 1 delivers SIGTERM to the shell that installed
1002
- # the drain_handler trap. If the launch mechanism ever
1003
- # wraps this script in another process, this assumption
1004
- # breaks and drain will silently no-op \u2014 audit here.
1005
- kill -TERM 1 2>/dev/null || true
1006
- return 0
1007
- fi
1008
- sleep 2
1009
- done
1010
- }
1011
-
1012
- if [ "\${ECLOUD_PD_EXPECTED:-0}" = "1" ]; then
1013
- # Assumption: the orchestrator only flips ECLOUD_DRAIN_REQUESTED=1
1014
- # after observing ECLOUD_AWAITING_USERDATA (old VM) or
1015
- # ECLOUD_READY (new VM), so CHILD_PID is always set by the time
1016
- # drain_handler fires. If drain somehow arrived in the tiny window
1017
- # between this watcher spawn and CHILD_PID assignment below,
1018
- # drain_handler would skip the child-kill branch and still emit
1019
- # ECLOUD_DETACHED \u2014 harmless because there's nothing to drain yet.
1020
- if [ -x /usr/local/bin/ecloud-drain-watcher ]; then
1021
- /usr/local/bin/ecloud-drain-watcher &
1022
- else
1023
- drain_watcher &
1024
- fi
1025
- fi
1026
-
1027
651
  echo "compute-source-env.sh: Environment sourced."
1028
- echo "ECLOUD_READY runtime_bootstrapped"
1029
652
 
1030
653
  # Drop privileges to original user for the application command
1031
654
  if [ -n "$__ECLOUD_ORIGINAL_USER" ] && [ "$(id -u)" = "0" ]; then
1032
655
  echo "compute-source-env.sh: Dropping privileges to user: $__ECLOUD_ORIGINAL_USER"
1033
- # Must background the child so our trap can fire; exec replaces PID 1.
1034
- su -s /bin/sh "$__ECLOUD_ORIGINAL_USER" -c 'exec "$@"' -- sh "$@" &
1035
- CHILD_PID=$!
1036
- wait "$CHILD_PID"
1037
- exit $?
656
+ exec su -s /bin/sh "$__ECLOUD_ORIGINAL_USER" -c 'exec "$@"' -- sh "$@"
1038
657
  fi
1039
658
 
1040
- "$@" &
1041
- CHILD_PID=$!
1042
- wait "$CHILD_PID"
1043
- exit $?
659
+ exec "$@"
1044
660
  `;
1045
661
 
1046
662
  // src/client/common/templates/scriptTemplate.ts
@@ -1049,14 +665,6 @@ function processScriptTemplate(data) {
1049
665
  return template(data);
1050
666
  }
1051
667
 
1052
- // src/client/common/templates/Caddyfile.default.tmpl
1053
- 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';
1054
-
1055
- // src/client/common/templates/caddyfileTemplate.ts
1056
- function getDefaultCaddyfile() {
1057
- return Caddyfile_default_default;
1058
- }
1059
-
1060
668
  // keys/mainnet-alpha/prod/kms-encryption-public-key.pem
1061
669
  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-----";
1062
670
 
@@ -1232,9 +840,15 @@ async function layerLocalImage(options, logger) {
1232
840
  const imageConfig = await extractImageConfig(docker, sourceImageRef);
1233
841
  const originalCmd = imageConfig.cmd.length > 0 ? imageConfig.cmd : imageConfig.entrypoint;
1234
842
  const originalUser = imageConfig.user;
1235
- const includeTLS = true;
1236
- const drainWatcherSource = findBinary("ecloud-drain-watcher-linux-amd64");
1237
- const includeDrainWatcher = fs.existsSync(drainWatcherSource);
843
+ let includeTLS = false;
844
+ if (envFilePath && fs.existsSync(envFilePath)) {
845
+ const envContent = fs.readFileSync(envFilePath, "utf-8");
846
+ const domainMatch = envContent.match(/^DOMAIN=(.+)$/m);
847
+ if (domainMatch && domainMatch[1] && domainMatch[1] !== "localhost") {
848
+ includeTLS = true;
849
+ logger.debug(`Found DOMAIN=${domainMatch[1]} in ${envFilePath}, including TLS components`);
850
+ }
851
+ }
1238
852
  const layeredDockerfileContent = processDockerfileTemplate({
1239
853
  baseImage: sourceImageRef,
1240
854
  originalCmd: JSON.stringify(originalCmd),
@@ -1242,9 +856,8 @@ async function layerLocalImage(options, logger) {
1242
856
  logRedirect,
1243
857
  resourceUsageAllow,
1244
858
  includeTLS,
1245
- ecloudCLIVersion: "0.1.0",
859
+ ecloudCLIVersion: "0.1.0"
1246
860
  // TODO: Get from package.json
1247
- includeDrainWatcher
1248
861
  });
1249
862
  const scriptContent = processScriptTemplate({
1250
863
  kmsServerURL: environmentConfig.kmsServerURL,
@@ -1254,8 +867,7 @@ async function layerLocalImage(options, logger) {
1254
867
  environmentConfig,
1255
868
  layeredDockerfileContent,
1256
869
  scriptContent,
1257
- includeTLS,
1258
- includeDrainWatcher ? drainWatcherSource : void 0
870
+ includeTLS
1259
871
  // logger
1260
872
  );
1261
873
  try {
@@ -1270,7 +882,7 @@ async function layerLocalImage(options, logger) {
1270
882
  fs.rmSync(tempDir, { recursive: true, force: true });
1271
883
  }
1272
884
  }
1273
- async function setupLayeredBuildDirectory(environmentConfig, layeredDockerfileContent, scriptContent, includeTLS, drainWatcherSource) {
885
+ async function setupLayeredBuildDirectory(environmentConfig, layeredDockerfileContent, scriptContent, includeTLS) {
1274
886
  const tempDir = fs.mkdtempSync(path2.join(os.tmpdir(), LAYERED_BUILD_DIR_PREFIX));
1275
887
  try {
1276
888
  const layeredDockerfilePath = path2.join(tempDir, LAYERED_DOCKERFILE_NAME);
@@ -1294,11 +906,6 @@ async function setupLayeredBuildDirectory(environmentConfig, layeredDockerfileCo
1294
906
  }
1295
907
  fs.copyFileSync(kmsClientSource, kmsClientPath);
1296
908
  fs.chmodSync(kmsClientPath, 493);
1297
- if (drainWatcherSource && fs.existsSync(drainWatcherSource)) {
1298
- const drainWatcherPath = path2.join(tempDir, DRAIN_WATCHER_BINARY_NAME);
1299
- fs.copyFileSync(drainWatcherSource, drainWatcherPath);
1300
- fs.chmodSync(drainWatcherPath, 493);
1301
- }
1302
909
  if (includeTLS) {
1303
910
  const tlsKeygenPath = path2.join(tempDir, TLS_KEYGEN_BINARY_NAME);
1304
911
  const tlsKeygenSource = findBinary("tls-keygen-linux-amd64");
@@ -1309,13 +916,15 @@ async function setupLayeredBuildDirectory(environmentConfig, layeredDockerfileCo
1309
916
  }
1310
917
  fs.copyFileSync(tlsKeygenSource, tlsKeygenPath);
1311
918
  fs.chmodSync(tlsKeygenPath, 493);
1312
- const userCaddyfilePath = path2.join(process.cwd(), CADDYFILE_NAME);
1313
- const destCaddyfilePath = path2.join(tempDir, CADDYFILE_NAME);
1314
- if (fs.existsSync(userCaddyfilePath)) {
1315
- fs.copyFileSync(userCaddyfilePath, destCaddyfilePath);
1316
- fs.chmodSync(destCaddyfilePath, 420);
919
+ const caddyfilePath = path2.join(process.cwd(), CADDYFILE_NAME);
920
+ if (fs.existsSync(caddyfilePath)) {
921
+ const caddyfileContent = fs.readFileSync(caddyfilePath);
922
+ const destCaddyfilePath = path2.join(tempDir, CADDYFILE_NAME);
923
+ fs.writeFileSync(destCaddyfilePath, caddyfileContent, { mode: 420 });
1317
924
  } else {
1318
- fs.writeFileSync(destCaddyfilePath, getDefaultCaddyfile(), { mode: 420 });
925
+ throw new Error(
926
+ "TLS is enabled (DOMAIN is set) but Caddyfile not found. Run configure TLS to set up TLS configuration"
927
+ );
1319
928
  }
1320
929
  }
1321
930
  return tempDir;
@@ -1383,16 +992,7 @@ async function extractDigestFromSinglePlatform(manifest, imageRef) {
1383
992
  architecture: inspectData[0].Architecture
1384
993
  } : null;
1385
994
  if (!config) {
1386
- if (manifest.config?.digest) {
1387
- const digest = hexStringToBytes32(manifest.config.digest);
1388
- const registry = extractRegistryName(imageRef);
1389
- return {
1390
- digest,
1391
- registry,
1392
- platform: DOCKER_PLATFORM
1393
- };
1394
- }
1395
- throw new Error(`Could not determine platform for ${imageRef}`);
995
+ throw createPlatformErrorMessage(imageRef, ["unknown (could not determine architecture)"]);
1396
996
  }
1397
997
  const platform2 = `${config.os}/${config.architecture}`;
1398
998
  if (platform2 === DOCKER_PLATFORM) {
@@ -1464,14 +1064,11 @@ Image: ${imageRef}
1464
1064
  Found platform(s): ${platforms.join(", ")}
1465
1065
  Required platform: ${DOCKER_PLATFORM}
1466
1066
 
1467
- To fix this issue:
1468
- 1. Manual fix:
1469
- a. Rebuild your image with the correct platform:
1470
- docker build --platform ${DOCKER_PLATFORM} -t ${imageRef} .
1471
- b. Push the rebuilt image to your remote registry:
1472
- docker push ${imageRef}
1473
-
1474
- 2. Or use the SDK to build with the correct platform automatically.`;
1067
+ To fix, either:
1068
+ 1. Rebuild the image for ${DOCKER_PLATFORM} and push it:
1069
+ docker buildx build --platform ${DOCKER_PLATFORM} -t ${imageRef} --push .
1070
+ 2. Or use a verifiable build (--verifiable --repo <repo> --commit <sha>), which
1071
+ builds server-side and needs no local Docker.`;
1475
1072
  return new Error(errorMsg);
1476
1073
  }
1477
1074
 
@@ -1576,6 +1173,8 @@ async function prepareRelease(options, logger) {
1576
1173
  logger.info(`Waiting ${REGISTRY_PROPAGATION_WAIT_SECONDS} seconds for registry propagation...`);
1577
1174
  await new Promise((resolve2) => setTimeout(resolve2, REGISTRY_PROPAGATION_WAIT_SECONDS * 1e3));
1578
1175
  } else {
1176
+ logger.info("Verifying image platform (linux/amd64)...");
1177
+ await getImageDigestAndName(imageRef);
1579
1178
  logger.info("Checking if image needs layering...");
1580
1179
  finalImageRef = await layerRemoteImageIfNeeded(
1581
1180
  {
@@ -1734,6 +1333,13 @@ function extractRegistryNameNoDocker(imageRef) {
1734
1333
  var import_viem = require("viem");
1735
1334
 
1736
1335
  // src/client/common/types/index.ts
1336
+ var EMPTY_CONTAINER_POLICY = {
1337
+ args: [],
1338
+ cmdOverride: [],
1339
+ env: [],
1340
+ envOverride: [],
1341
+ restartPolicy: ""
1342
+ };
1737
1343
  var noopLogger = {
1738
1344
  debug: () => {
1739
1345
  },
@@ -2974,32 +2580,1577 @@ async function executeBatch(options, logger = noopLogger) {
2974
2580
  } else {
2975
2581
  revertReason = callError.message || "Unknown reason";
2976
2582
  }
2977
- }
2978
- throw new Error(`Transaction reverted: ${hash}. Reason: ${revertReason}`);
2583
+ }
2584
+ throw new Error(`Transaction reverted: ${hash}. Reason: ${revertReason}`);
2585
+ }
2586
+ return hash;
2587
+ }
2588
+
2589
+ // src/client/common/contract/caller.ts
2590
+ var import_viem3 = require("viem");
2591
+
2592
+ // src/client/common/utils/helpers.ts
2593
+ var import_viem2 = require("viem");
2594
+ var import_chains2 = require("viem/chains");
2595
+ var import_accounts = require("viem/accounts");
2596
+ function getChainFromID(chainID, fallback2 = import_chains2.sepolia) {
2597
+ const id = Number(chainID);
2598
+ return (0, import_viem2.extractChain)({ chains: SUPPORTED_CHAINS, id }) || fallback2;
2599
+ }
2600
+ function addHexPrefix(value) {
2601
+ return value.startsWith("0x") ? value : `0x${value}`;
2602
+ }
2603
+ function stripHexPrefix(value) {
2604
+ return value.startsWith("0x") ? value.slice(2) : value;
2605
+ }
2606
+
2607
+ // src/client/common/abis/AppController.json
2608
+ var AppController_default = [
2609
+ {
2610
+ type: "constructor",
2611
+ inputs: [
2612
+ {
2613
+ name: "_version",
2614
+ type: "string",
2615
+ internalType: "string"
2616
+ },
2617
+ {
2618
+ name: "_permissionController",
2619
+ type: "address",
2620
+ internalType: "contractIPermissionController"
2621
+ },
2622
+ {
2623
+ name: "_releaseManager",
2624
+ type: "address",
2625
+ internalType: "contractIReleaseManager"
2626
+ },
2627
+ {
2628
+ name: "_computeAVSRegistrar",
2629
+ type: "address",
2630
+ internalType: "contractIComputeAVSRegistrar"
2631
+ },
2632
+ {
2633
+ name: "_computeOperator",
2634
+ type: "address",
2635
+ internalType: "contractIComputeOperator"
2636
+ },
2637
+ {
2638
+ name: "_appBeacon",
2639
+ type: "address",
2640
+ internalType: "contractIBeacon"
2641
+ }
2642
+ ],
2643
+ stateMutability: "nonpayable"
2644
+ },
2645
+ {
2646
+ type: "function",
2647
+ name: "API_PERMISSION_TYPEHASH",
2648
+ inputs: [],
2649
+ outputs: [
2650
+ {
2651
+ name: "",
2652
+ type: "bytes32",
2653
+ internalType: "bytes32"
2654
+ }
2655
+ ],
2656
+ stateMutability: "view"
2657
+ },
2658
+ {
2659
+ type: "function",
2660
+ name: "appBeacon",
2661
+ inputs: [],
2662
+ outputs: [
2663
+ {
2664
+ name: "",
2665
+ type: "address",
2666
+ internalType: "contractIBeacon"
2667
+ }
2668
+ ],
2669
+ stateMutability: "view"
2670
+ },
2671
+ {
2672
+ type: "function",
2673
+ name: "calculateApiPermissionDigestHash",
2674
+ inputs: [
2675
+ {
2676
+ name: "permission",
2677
+ type: "bytes4",
2678
+ internalType: "bytes4"
2679
+ },
2680
+ {
2681
+ name: "expiry",
2682
+ type: "uint256",
2683
+ internalType: "uint256"
2684
+ }
2685
+ ],
2686
+ outputs: [
2687
+ {
2688
+ name: "",
2689
+ type: "bytes32",
2690
+ internalType: "bytes32"
2691
+ }
2692
+ ],
2693
+ stateMutability: "view"
2694
+ },
2695
+ {
2696
+ type: "function",
2697
+ name: "calculateAppId",
2698
+ inputs: [
2699
+ {
2700
+ name: "deployer",
2701
+ type: "address",
2702
+ internalType: "address"
2703
+ },
2704
+ {
2705
+ name: "salt",
2706
+ type: "bytes32",
2707
+ internalType: "bytes32"
2708
+ }
2709
+ ],
2710
+ outputs: [
2711
+ {
2712
+ name: "",
2713
+ type: "address",
2714
+ internalType: "contractIApp"
2715
+ }
2716
+ ],
2717
+ stateMutability: "view"
2718
+ },
2719
+ {
2720
+ type: "function",
2721
+ name: "computeAVSRegistrar",
2722
+ inputs: [],
2723
+ outputs: [
2724
+ {
2725
+ name: "",
2726
+ type: "address",
2727
+ internalType: "contractIComputeAVSRegistrar"
2728
+ }
2729
+ ],
2730
+ stateMutability: "view"
2731
+ },
2732
+ {
2733
+ type: "function",
2734
+ name: "computeOperator",
2735
+ inputs: [],
2736
+ outputs: [
2737
+ {
2738
+ name: "",
2739
+ type: "address",
2740
+ internalType: "contractIComputeOperator"
2741
+ }
2742
+ ],
2743
+ stateMutability: "view"
2744
+ },
2745
+ {
2746
+ type: "function",
2747
+ name: "confirmUpgrade",
2748
+ inputs: [
2749
+ {
2750
+ name: "app",
2751
+ type: "address",
2752
+ internalType: "contractIApp"
2753
+ }
2754
+ ],
2755
+ outputs: [],
2756
+ stateMutability: "nonpayable"
2757
+ },
2758
+ {
2759
+ type: "function",
2760
+ name: "createApp",
2761
+ inputs: [
2762
+ {
2763
+ name: "salt",
2764
+ type: "bytes32",
2765
+ internalType: "bytes32"
2766
+ },
2767
+ {
2768
+ name: "release",
2769
+ type: "tuple",
2770
+ internalType: "structIAppController.Release",
2771
+ components: [
2772
+ {
2773
+ name: "rmsRelease",
2774
+ type: "tuple",
2775
+ internalType: "structIReleaseManagerTypes.Release",
2776
+ components: [
2777
+ {
2778
+ name: "artifacts",
2779
+ type: "tuple[]",
2780
+ internalType: "structIReleaseManagerTypes.Artifact[]",
2781
+ components: [
2782
+ {
2783
+ name: "digest",
2784
+ type: "bytes32",
2785
+ internalType: "bytes32"
2786
+ },
2787
+ {
2788
+ name: "registry",
2789
+ type: "string",
2790
+ internalType: "string"
2791
+ }
2792
+ ]
2793
+ },
2794
+ {
2795
+ name: "upgradeByTime",
2796
+ type: "uint32",
2797
+ internalType: "uint32"
2798
+ }
2799
+ ]
2800
+ },
2801
+ {
2802
+ name: "publicEnv",
2803
+ type: "bytes",
2804
+ internalType: "bytes"
2805
+ },
2806
+ {
2807
+ name: "encryptedEnv",
2808
+ type: "bytes",
2809
+ internalType: "bytes"
2810
+ },
2811
+ {
2812
+ name: "containerPolicy",
2813
+ type: "tuple",
2814
+ internalType: "structIAppController.ContainerPolicy",
2815
+ components: [
2816
+ {
2817
+ name: "args",
2818
+ type: "string[]",
2819
+ internalType: "string[]"
2820
+ },
2821
+ {
2822
+ name: "cmdOverride",
2823
+ type: "string[]",
2824
+ internalType: "string[]"
2825
+ },
2826
+ {
2827
+ name: "env",
2828
+ type: "tuple[]",
2829
+ internalType: "structIAppController.EnvVar[]",
2830
+ components: [
2831
+ {
2832
+ name: "key",
2833
+ type: "string",
2834
+ internalType: "string"
2835
+ },
2836
+ {
2837
+ name: "value",
2838
+ type: "string",
2839
+ internalType: "string"
2840
+ }
2841
+ ]
2842
+ },
2843
+ {
2844
+ name: "envOverride",
2845
+ type: "tuple[]",
2846
+ internalType: "structIAppController.EnvVar[]",
2847
+ components: [
2848
+ {
2849
+ name: "key",
2850
+ type: "string",
2851
+ internalType: "string"
2852
+ },
2853
+ {
2854
+ name: "value",
2855
+ type: "string",
2856
+ internalType: "string"
2857
+ }
2858
+ ]
2859
+ },
2860
+ {
2861
+ name: "restartPolicy",
2862
+ type: "string",
2863
+ internalType: "string"
2864
+ }
2865
+ ]
2866
+ }
2867
+ ]
2868
+ }
2869
+ ],
2870
+ outputs: [
2871
+ {
2872
+ name: "app",
2873
+ type: "address",
2874
+ internalType: "contractIApp"
2875
+ }
2876
+ ],
2877
+ stateMutability: "nonpayable"
2878
+ },
2879
+ {
2880
+ type: "function",
2881
+ name: "createAppWithIsolatedBilling",
2882
+ inputs: [
2883
+ {
2884
+ name: "salt",
2885
+ type: "bytes32",
2886
+ internalType: "bytes32"
2887
+ },
2888
+ {
2889
+ name: "release",
2890
+ type: "tuple",
2891
+ internalType: "structIAppController.Release",
2892
+ components: [
2893
+ {
2894
+ name: "rmsRelease",
2895
+ type: "tuple",
2896
+ internalType: "structIReleaseManagerTypes.Release",
2897
+ components: [
2898
+ {
2899
+ name: "artifacts",
2900
+ type: "tuple[]",
2901
+ internalType: "structIReleaseManagerTypes.Artifact[]",
2902
+ components: [
2903
+ {
2904
+ name: "digest",
2905
+ type: "bytes32",
2906
+ internalType: "bytes32"
2907
+ },
2908
+ {
2909
+ name: "registry",
2910
+ type: "string",
2911
+ internalType: "string"
2912
+ }
2913
+ ]
2914
+ },
2915
+ {
2916
+ name: "upgradeByTime",
2917
+ type: "uint32",
2918
+ internalType: "uint32"
2919
+ }
2920
+ ]
2921
+ },
2922
+ {
2923
+ name: "publicEnv",
2924
+ type: "bytes",
2925
+ internalType: "bytes"
2926
+ },
2927
+ {
2928
+ name: "encryptedEnv",
2929
+ type: "bytes",
2930
+ internalType: "bytes"
2931
+ },
2932
+ {
2933
+ name: "containerPolicy",
2934
+ type: "tuple",
2935
+ internalType: "structIAppController.ContainerPolicy",
2936
+ components: [
2937
+ {
2938
+ name: "args",
2939
+ type: "string[]",
2940
+ internalType: "string[]"
2941
+ },
2942
+ {
2943
+ name: "cmdOverride",
2944
+ type: "string[]",
2945
+ internalType: "string[]"
2946
+ },
2947
+ {
2948
+ name: "env",
2949
+ type: "tuple[]",
2950
+ internalType: "structIAppController.EnvVar[]",
2951
+ components: [
2952
+ {
2953
+ name: "key",
2954
+ type: "string",
2955
+ internalType: "string"
2956
+ },
2957
+ {
2958
+ name: "value",
2959
+ type: "string",
2960
+ internalType: "string"
2961
+ }
2962
+ ]
2963
+ },
2964
+ {
2965
+ name: "envOverride",
2966
+ type: "tuple[]",
2967
+ internalType: "structIAppController.EnvVar[]",
2968
+ components: [
2969
+ {
2970
+ name: "key",
2971
+ type: "string",
2972
+ internalType: "string"
2973
+ },
2974
+ {
2975
+ name: "value",
2976
+ type: "string",
2977
+ internalType: "string"
2978
+ }
2979
+ ]
2980
+ },
2981
+ {
2982
+ name: "restartPolicy",
2983
+ type: "string",
2984
+ internalType: "string"
2985
+ }
2986
+ ]
2987
+ }
2988
+ ]
2989
+ }
2990
+ ],
2991
+ outputs: [
2992
+ {
2993
+ name: "app",
2994
+ type: "address",
2995
+ internalType: "contractIApp"
2996
+ }
2997
+ ],
2998
+ stateMutability: "nonpayable"
2999
+ },
3000
+ {
3001
+ type: "function",
3002
+ name: "createEmptyApp",
3003
+ inputs: [
3004
+ {
3005
+ name: "salt",
3006
+ type: "bytes32",
3007
+ internalType: "bytes32"
3008
+ }
3009
+ ],
3010
+ outputs: [
3011
+ {
3012
+ name: "app",
3013
+ type: "address",
3014
+ internalType: "contractIApp"
3015
+ }
3016
+ ],
3017
+ stateMutability: "nonpayable"
3018
+ },
3019
+ {
3020
+ type: "function",
3021
+ name: "createEmptyAppWithIsolatedBilling",
3022
+ inputs: [
3023
+ {
3024
+ name: "salt",
3025
+ type: "bytes32",
3026
+ internalType: "bytes32"
3027
+ }
3028
+ ],
3029
+ outputs: [
3030
+ {
3031
+ name: "app",
3032
+ type: "address",
3033
+ internalType: "contractIApp"
3034
+ }
3035
+ ],
3036
+ stateMutability: "nonpayable"
3037
+ },
3038
+ {
3039
+ type: "function",
3040
+ name: "domainSeparator",
3041
+ inputs: [],
3042
+ outputs: [
3043
+ {
3044
+ name: "",
3045
+ type: "bytes32",
3046
+ internalType: "bytes32"
3047
+ }
3048
+ ],
3049
+ stateMutability: "view"
3050
+ },
3051
+ {
3052
+ type: "function",
3053
+ name: "getActiveAppCount",
3054
+ inputs: [
3055
+ {
3056
+ name: "user",
3057
+ type: "address",
3058
+ internalType: "address"
3059
+ }
3060
+ ],
3061
+ outputs: [
3062
+ {
3063
+ name: "",
3064
+ type: "uint32",
3065
+ internalType: "uint32"
3066
+ }
3067
+ ],
3068
+ stateMutability: "view"
3069
+ },
3070
+ {
3071
+ type: "function",
3072
+ name: "getAppCreator",
3073
+ inputs: [
3074
+ {
3075
+ name: "app",
3076
+ type: "address",
3077
+ internalType: "contractIApp"
3078
+ }
3079
+ ],
3080
+ outputs: [
3081
+ {
3082
+ name: "",
3083
+ type: "address",
3084
+ internalType: "address"
3085
+ }
3086
+ ],
3087
+ stateMutability: "view"
3088
+ },
3089
+ {
3090
+ type: "function",
3091
+ name: "getAppLatestReleaseBlockNumber",
3092
+ inputs: [
3093
+ {
3094
+ name: "app",
3095
+ type: "address",
3096
+ internalType: "contractIApp"
3097
+ }
3098
+ ],
3099
+ outputs: [
3100
+ {
3101
+ name: "",
3102
+ type: "uint32",
3103
+ internalType: "uint32"
3104
+ }
3105
+ ],
3106
+ stateMutability: "view"
3107
+ },
3108
+ {
3109
+ type: "function",
3110
+ name: "getAppOperatorSetId",
3111
+ inputs: [
3112
+ {
3113
+ name: "app",
3114
+ type: "address",
3115
+ internalType: "contractIApp"
3116
+ }
3117
+ ],
3118
+ outputs: [
3119
+ {
3120
+ name: "",
3121
+ type: "uint32",
3122
+ internalType: "uint32"
3123
+ }
3124
+ ],
3125
+ stateMutability: "view"
3126
+ },
3127
+ {
3128
+ type: "function",
3129
+ name: "getAppPendingReleaseBlockNumber",
3130
+ inputs: [
3131
+ {
3132
+ name: "app",
3133
+ type: "address",
3134
+ internalType: "contractIApp"
3135
+ }
3136
+ ],
3137
+ outputs: [
3138
+ {
3139
+ name: "",
3140
+ type: "uint32",
3141
+ internalType: "uint32"
3142
+ }
3143
+ ],
3144
+ stateMutability: "view"
3145
+ },
3146
+ {
3147
+ type: "function",
3148
+ name: "getAppStatus",
3149
+ inputs: [
3150
+ {
3151
+ name: "app",
3152
+ type: "address",
3153
+ internalType: "contractIApp"
3154
+ }
3155
+ ],
3156
+ outputs: [
3157
+ {
3158
+ name: "",
3159
+ type: "uint8",
3160
+ internalType: "enumIAppController.AppStatus"
3161
+ }
3162
+ ],
3163
+ stateMutability: "view"
3164
+ },
3165
+ {
3166
+ type: "function",
3167
+ name: "getApps",
3168
+ inputs: [
3169
+ {
3170
+ name: "offset",
3171
+ type: "uint256",
3172
+ internalType: "uint256"
3173
+ },
3174
+ {
3175
+ name: "limit",
3176
+ type: "uint256",
3177
+ internalType: "uint256"
3178
+ }
3179
+ ],
3180
+ outputs: [
3181
+ {
3182
+ name: "apps",
3183
+ type: "address[]",
3184
+ internalType: "contractIApp[]"
3185
+ },
3186
+ {
3187
+ name: "appConfigsMem",
3188
+ type: "tuple[]",
3189
+ internalType: "structIAppController.AppConfig[]",
3190
+ components: [
3191
+ {
3192
+ name: "creator",
3193
+ type: "address",
3194
+ internalType: "address"
3195
+ },
3196
+ {
3197
+ name: "operatorSetId",
3198
+ type: "uint32",
3199
+ internalType: "uint32"
3200
+ },
3201
+ {
3202
+ name: "latestReleaseBlockNumber",
3203
+ type: "uint32",
3204
+ internalType: "uint32"
3205
+ },
3206
+ {
3207
+ name: "pendingReleaseBlockNumber",
3208
+ type: "uint32",
3209
+ internalType: "uint32"
3210
+ },
3211
+ {
3212
+ name: "status",
3213
+ type: "uint8",
3214
+ internalType: "enumIAppController.AppStatus"
3215
+ }
3216
+ ]
3217
+ }
3218
+ ],
3219
+ stateMutability: "view"
3220
+ },
3221
+ {
3222
+ type: "function",
3223
+ name: "getAppsByBillingAccount",
3224
+ inputs: [
3225
+ {
3226
+ name: "account",
3227
+ type: "address",
3228
+ internalType: "address"
3229
+ },
3230
+ {
3231
+ name: "offset",
3232
+ type: "uint256",
3233
+ internalType: "uint256"
3234
+ },
3235
+ {
3236
+ name: "limit",
3237
+ type: "uint256",
3238
+ internalType: "uint256"
3239
+ }
3240
+ ],
3241
+ outputs: [
3242
+ {
3243
+ name: "apps",
3244
+ type: "address[]",
3245
+ internalType: "contractIApp[]"
3246
+ },
3247
+ {
3248
+ name: "appConfigsMem",
3249
+ type: "tuple[]",
3250
+ internalType: "structIAppController.AppConfig[]",
3251
+ components: [
3252
+ {
3253
+ name: "creator",
3254
+ type: "address",
3255
+ internalType: "address"
3256
+ },
3257
+ {
3258
+ name: "operatorSetId",
3259
+ type: "uint32",
3260
+ internalType: "uint32"
3261
+ },
3262
+ {
3263
+ name: "latestReleaseBlockNumber",
3264
+ type: "uint32",
3265
+ internalType: "uint32"
3266
+ },
3267
+ {
3268
+ name: "pendingReleaseBlockNumber",
3269
+ type: "uint32",
3270
+ internalType: "uint32"
3271
+ },
3272
+ {
3273
+ name: "status",
3274
+ type: "uint8",
3275
+ internalType: "enumIAppController.AppStatus"
3276
+ }
3277
+ ]
3278
+ }
3279
+ ],
3280
+ stateMutability: "view"
3281
+ },
3282
+ {
3283
+ type: "function",
3284
+ name: "getAppsByCreator",
3285
+ inputs: [
3286
+ {
3287
+ name: "creator",
3288
+ type: "address",
3289
+ internalType: "address"
3290
+ },
3291
+ {
3292
+ name: "offset",
3293
+ type: "uint256",
3294
+ internalType: "uint256"
3295
+ },
3296
+ {
3297
+ name: "limit",
3298
+ type: "uint256",
3299
+ internalType: "uint256"
3300
+ }
3301
+ ],
3302
+ outputs: [
3303
+ {
3304
+ name: "apps",
3305
+ type: "address[]",
3306
+ internalType: "contractIApp[]"
3307
+ },
3308
+ {
3309
+ name: "appConfigsMem",
3310
+ type: "tuple[]",
3311
+ internalType: "structIAppController.AppConfig[]",
3312
+ components: [
3313
+ {
3314
+ name: "creator",
3315
+ type: "address",
3316
+ internalType: "address"
3317
+ },
3318
+ {
3319
+ name: "operatorSetId",
3320
+ type: "uint32",
3321
+ internalType: "uint32"
3322
+ },
3323
+ {
3324
+ name: "latestReleaseBlockNumber",
3325
+ type: "uint32",
3326
+ internalType: "uint32"
3327
+ },
3328
+ {
3329
+ name: "pendingReleaseBlockNumber",
3330
+ type: "uint32",
3331
+ internalType: "uint32"
3332
+ },
3333
+ {
3334
+ name: "status",
3335
+ type: "uint8",
3336
+ internalType: "enumIAppController.AppStatus"
3337
+ }
3338
+ ]
3339
+ }
3340
+ ],
3341
+ stateMutability: "view"
3342
+ },
3343
+ {
3344
+ type: "function",
3345
+ name: "getAppsByDeveloper",
3346
+ inputs: [
3347
+ {
3348
+ name: "developer",
3349
+ type: "address",
3350
+ internalType: "address"
3351
+ },
3352
+ {
3353
+ name: "offset",
3354
+ type: "uint256",
3355
+ internalType: "uint256"
3356
+ },
3357
+ {
3358
+ name: "limit",
3359
+ type: "uint256",
3360
+ internalType: "uint256"
3361
+ }
3362
+ ],
3363
+ outputs: [
3364
+ {
3365
+ name: "apps",
3366
+ type: "address[]",
3367
+ internalType: "contractIApp[]"
3368
+ },
3369
+ {
3370
+ name: "appConfigsMem",
3371
+ type: "tuple[]",
3372
+ internalType: "structIAppController.AppConfig[]",
3373
+ components: [
3374
+ {
3375
+ name: "creator",
3376
+ type: "address",
3377
+ internalType: "address"
3378
+ },
3379
+ {
3380
+ name: "operatorSetId",
3381
+ type: "uint32",
3382
+ internalType: "uint32"
3383
+ },
3384
+ {
3385
+ name: "latestReleaseBlockNumber",
3386
+ type: "uint32",
3387
+ internalType: "uint32"
3388
+ },
3389
+ {
3390
+ name: "pendingReleaseBlockNumber",
3391
+ type: "uint32",
3392
+ internalType: "uint32"
3393
+ },
3394
+ {
3395
+ name: "status",
3396
+ type: "uint8",
3397
+ internalType: "enumIAppController.AppStatus"
3398
+ }
3399
+ ]
3400
+ }
3401
+ ],
3402
+ stateMutability: "view"
3403
+ },
3404
+ {
3405
+ type: "function",
3406
+ name: "getBillingAccount",
3407
+ inputs: [
3408
+ {
3409
+ name: "app",
3410
+ type: "address",
3411
+ internalType: "contractIApp"
3412
+ }
3413
+ ],
3414
+ outputs: [
3415
+ {
3416
+ name: "",
3417
+ type: "address",
3418
+ internalType: "address"
3419
+ }
3420
+ ],
3421
+ stateMutability: "view"
3422
+ },
3423
+ {
3424
+ type: "function",
3425
+ name: "getBillingType",
3426
+ inputs: [
3427
+ {
3428
+ name: "app",
3429
+ type: "address",
3430
+ internalType: "contractIApp"
3431
+ }
3432
+ ],
3433
+ outputs: [
3434
+ {
3435
+ name: "",
3436
+ type: "uint8",
3437
+ internalType: "enumIAppController.BillingType"
3438
+ }
3439
+ ],
3440
+ stateMutability: "view"
3441
+ },
3442
+ {
3443
+ type: "function",
3444
+ name: "getMaxActiveAppsPerUser",
3445
+ inputs: [
3446
+ {
3447
+ name: "user",
3448
+ type: "address",
3449
+ internalType: "address"
3450
+ }
3451
+ ],
3452
+ outputs: [
3453
+ {
3454
+ name: "",
3455
+ type: "uint32",
3456
+ internalType: "uint32"
3457
+ }
3458
+ ],
3459
+ stateMutability: "view"
3460
+ },
3461
+ {
3462
+ type: "function",
3463
+ name: "globalActiveAppCount",
3464
+ inputs: [],
3465
+ outputs: [
3466
+ {
3467
+ name: "",
3468
+ type: "uint32",
3469
+ internalType: "uint32"
3470
+ }
3471
+ ],
3472
+ stateMutability: "view"
3473
+ },
3474
+ {
3475
+ type: "function",
3476
+ name: "initialize",
3477
+ inputs: [
3478
+ {
3479
+ name: "admin",
3480
+ type: "address",
3481
+ internalType: "address"
3482
+ }
3483
+ ],
3484
+ outputs: [],
3485
+ stateMutability: "nonpayable"
3486
+ },
3487
+ {
3488
+ type: "function",
3489
+ name: "maxGlobalActiveApps",
3490
+ inputs: [],
3491
+ outputs: [
3492
+ {
3493
+ name: "",
3494
+ type: "uint32",
3495
+ internalType: "uint32"
3496
+ }
3497
+ ],
3498
+ stateMutability: "view"
3499
+ },
3500
+ {
3501
+ type: "function",
3502
+ name: "permissionController",
3503
+ inputs: [],
3504
+ outputs: [
3505
+ {
3506
+ name: "",
3507
+ type: "address",
3508
+ internalType: "contractIPermissionController"
3509
+ }
3510
+ ],
3511
+ stateMutability: "view"
3512
+ },
3513
+ {
3514
+ type: "function",
3515
+ name: "releaseManager",
3516
+ inputs: [],
3517
+ outputs: [
3518
+ {
3519
+ name: "",
3520
+ type: "address",
3521
+ internalType: "contractIReleaseManager"
3522
+ }
3523
+ ],
3524
+ stateMutability: "view"
3525
+ },
3526
+ {
3527
+ type: "function",
3528
+ name: "setMaxActiveAppsPerUser",
3529
+ inputs: [
3530
+ {
3531
+ name: "user",
3532
+ type: "address",
3533
+ internalType: "address"
3534
+ },
3535
+ {
3536
+ name: "limit",
3537
+ type: "uint32",
3538
+ internalType: "uint32"
3539
+ }
3540
+ ],
3541
+ outputs: [],
3542
+ stateMutability: "nonpayable"
3543
+ },
3544
+ {
3545
+ type: "function",
3546
+ name: "setMaxGlobalActiveApps",
3547
+ inputs: [
3548
+ {
3549
+ name: "limit",
3550
+ type: "uint32",
3551
+ internalType: "uint32"
3552
+ }
3553
+ ],
3554
+ outputs: [],
3555
+ stateMutability: "nonpayable"
3556
+ },
3557
+ {
3558
+ type: "function",
3559
+ name: "startApp",
3560
+ inputs: [
3561
+ {
3562
+ name: "app",
3563
+ type: "address",
3564
+ internalType: "contractIApp"
3565
+ }
3566
+ ],
3567
+ outputs: [],
3568
+ stateMutability: "nonpayable"
3569
+ },
3570
+ {
3571
+ type: "function",
3572
+ name: "stopApp",
3573
+ inputs: [
3574
+ {
3575
+ name: "app",
3576
+ type: "address",
3577
+ internalType: "contractIApp"
3578
+ }
3579
+ ],
3580
+ outputs: [],
3581
+ stateMutability: "nonpayable"
3582
+ },
3583
+ {
3584
+ type: "function",
3585
+ name: "suspend",
3586
+ inputs: [
3587
+ {
3588
+ name: "account",
3589
+ type: "address",
3590
+ internalType: "address"
3591
+ },
3592
+ {
3593
+ name: "apps",
3594
+ type: "address[]",
3595
+ internalType: "contractIApp[]"
3596
+ }
3597
+ ],
3598
+ outputs: [],
3599
+ stateMutability: "nonpayable"
3600
+ },
3601
+ {
3602
+ type: "function",
3603
+ name: "terminateApp",
3604
+ inputs: [
3605
+ {
3606
+ name: "app",
3607
+ type: "address",
3608
+ internalType: "contractIApp"
3609
+ }
3610
+ ],
3611
+ outputs: [],
3612
+ stateMutability: "nonpayable"
3613
+ },
3614
+ {
3615
+ type: "function",
3616
+ name: "terminateAppByAdmin",
3617
+ inputs: [
3618
+ {
3619
+ name: "app",
3620
+ type: "address",
3621
+ internalType: "contractIApp"
3622
+ }
3623
+ ],
3624
+ outputs: [],
3625
+ stateMutability: "nonpayable"
3626
+ },
3627
+ {
3628
+ type: "function",
3629
+ name: "updateAppMetadataURI",
3630
+ inputs: [
3631
+ {
3632
+ name: "app",
3633
+ type: "address",
3634
+ internalType: "contractIApp"
3635
+ },
3636
+ {
3637
+ name: "metadataURI",
3638
+ type: "string",
3639
+ internalType: "string"
3640
+ }
3641
+ ],
3642
+ outputs: [],
3643
+ stateMutability: "nonpayable"
3644
+ },
3645
+ {
3646
+ type: "function",
3647
+ name: "upgradeApp",
3648
+ inputs: [
3649
+ {
3650
+ name: "app",
3651
+ type: "address",
3652
+ internalType: "contractIApp"
3653
+ },
3654
+ {
3655
+ name: "release",
3656
+ type: "tuple",
3657
+ internalType: "structIAppController.Release",
3658
+ components: [
3659
+ {
3660
+ name: "rmsRelease",
3661
+ type: "tuple",
3662
+ internalType: "structIReleaseManagerTypes.Release",
3663
+ components: [
3664
+ {
3665
+ name: "artifacts",
3666
+ type: "tuple[]",
3667
+ internalType: "structIReleaseManagerTypes.Artifact[]",
3668
+ components: [
3669
+ {
3670
+ name: "digest",
3671
+ type: "bytes32",
3672
+ internalType: "bytes32"
3673
+ },
3674
+ {
3675
+ name: "registry",
3676
+ type: "string",
3677
+ internalType: "string"
3678
+ }
3679
+ ]
3680
+ },
3681
+ {
3682
+ name: "upgradeByTime",
3683
+ type: "uint32",
3684
+ internalType: "uint32"
3685
+ }
3686
+ ]
3687
+ },
3688
+ {
3689
+ name: "publicEnv",
3690
+ type: "bytes",
3691
+ internalType: "bytes"
3692
+ },
3693
+ {
3694
+ name: "encryptedEnv",
3695
+ type: "bytes",
3696
+ internalType: "bytes"
3697
+ },
3698
+ {
3699
+ name: "containerPolicy",
3700
+ type: "tuple",
3701
+ internalType: "structIAppController.ContainerPolicy",
3702
+ components: [
3703
+ {
3704
+ name: "args",
3705
+ type: "string[]",
3706
+ internalType: "string[]"
3707
+ },
3708
+ {
3709
+ name: "cmdOverride",
3710
+ type: "string[]",
3711
+ internalType: "string[]"
3712
+ },
3713
+ {
3714
+ name: "env",
3715
+ type: "tuple[]",
3716
+ internalType: "structIAppController.EnvVar[]",
3717
+ components: [
3718
+ {
3719
+ name: "key",
3720
+ type: "string",
3721
+ internalType: "string"
3722
+ },
3723
+ {
3724
+ name: "value",
3725
+ type: "string",
3726
+ internalType: "string"
3727
+ }
3728
+ ]
3729
+ },
3730
+ {
3731
+ name: "envOverride",
3732
+ type: "tuple[]",
3733
+ internalType: "structIAppController.EnvVar[]",
3734
+ components: [
3735
+ {
3736
+ name: "key",
3737
+ type: "string",
3738
+ internalType: "string"
3739
+ },
3740
+ {
3741
+ name: "value",
3742
+ type: "string",
3743
+ internalType: "string"
3744
+ }
3745
+ ]
3746
+ },
3747
+ {
3748
+ name: "restartPolicy",
3749
+ type: "string",
3750
+ internalType: "string"
3751
+ }
3752
+ ]
3753
+ }
3754
+ ]
3755
+ }
3756
+ ],
3757
+ outputs: [
3758
+ {
3759
+ name: "",
3760
+ type: "uint256",
3761
+ internalType: "uint256"
3762
+ }
3763
+ ],
3764
+ stateMutability: "nonpayable"
3765
+ },
3766
+ {
3767
+ type: "function",
3768
+ name: "version",
3769
+ inputs: [],
3770
+ outputs: [
3771
+ {
3772
+ name: "",
3773
+ type: "string",
3774
+ internalType: "string"
3775
+ }
3776
+ ],
3777
+ stateMutability: "view"
3778
+ },
3779
+ {
3780
+ type: "event",
3781
+ name: "AppCreated",
3782
+ inputs: [
3783
+ {
3784
+ name: "creator",
3785
+ type: "address",
3786
+ indexed: true,
3787
+ internalType: "address"
3788
+ },
3789
+ {
3790
+ name: "app",
3791
+ type: "address",
3792
+ indexed: true,
3793
+ internalType: "contractIApp"
3794
+ },
3795
+ {
3796
+ name: "operatorSetId",
3797
+ type: "uint32",
3798
+ indexed: false,
3799
+ internalType: "uint32"
3800
+ }
3801
+ ],
3802
+ anonymous: false
3803
+ },
3804
+ {
3805
+ type: "event",
3806
+ name: "AppMetadataURIUpdated",
3807
+ inputs: [
3808
+ {
3809
+ name: "app",
3810
+ type: "address",
3811
+ indexed: true,
3812
+ internalType: "contractIApp"
3813
+ },
3814
+ {
3815
+ name: "metadataURI",
3816
+ type: "string",
3817
+ indexed: false,
3818
+ internalType: "string"
3819
+ }
3820
+ ],
3821
+ anonymous: false
3822
+ },
3823
+ {
3824
+ type: "event",
3825
+ name: "AppStarted",
3826
+ inputs: [
3827
+ {
3828
+ name: "app",
3829
+ type: "address",
3830
+ indexed: true,
3831
+ internalType: "contractIApp"
3832
+ }
3833
+ ],
3834
+ anonymous: false
3835
+ },
3836
+ {
3837
+ type: "event",
3838
+ name: "AppStopped",
3839
+ inputs: [
3840
+ {
3841
+ name: "app",
3842
+ type: "address",
3843
+ indexed: true,
3844
+ internalType: "contractIApp"
3845
+ }
3846
+ ],
3847
+ anonymous: false
3848
+ },
3849
+ {
3850
+ type: "event",
3851
+ name: "AppSuspended",
3852
+ inputs: [
3853
+ {
3854
+ name: "app",
3855
+ type: "address",
3856
+ indexed: true,
3857
+ internalType: "contractIApp"
3858
+ }
3859
+ ],
3860
+ anonymous: false
3861
+ },
3862
+ {
3863
+ type: "event",
3864
+ name: "AppTerminated",
3865
+ inputs: [
3866
+ {
3867
+ name: "app",
3868
+ type: "address",
3869
+ indexed: true,
3870
+ internalType: "contractIApp"
3871
+ }
3872
+ ],
3873
+ anonymous: false
3874
+ },
3875
+ {
3876
+ type: "event",
3877
+ name: "AppTerminatedByAdmin",
3878
+ inputs: [
3879
+ {
3880
+ name: "app",
3881
+ type: "address",
3882
+ indexed: true,
3883
+ internalType: "contractIApp"
3884
+ }
3885
+ ],
3886
+ anonymous: false
3887
+ },
3888
+ {
3889
+ type: "event",
3890
+ name: "AppUpgraded",
3891
+ inputs: [
3892
+ {
3893
+ name: "app",
3894
+ type: "address",
3895
+ indexed: true,
3896
+ internalType: "contractIApp"
3897
+ },
3898
+ {
3899
+ name: "rmsReleaseId",
3900
+ type: "uint256",
3901
+ indexed: false,
3902
+ internalType: "uint256"
3903
+ },
3904
+ {
3905
+ name: "release",
3906
+ type: "tuple",
3907
+ indexed: false,
3908
+ internalType: "structIAppController.Release",
3909
+ components: [
3910
+ {
3911
+ name: "rmsRelease",
3912
+ type: "tuple",
3913
+ internalType: "structIReleaseManagerTypes.Release",
3914
+ components: [
3915
+ {
3916
+ name: "artifacts",
3917
+ type: "tuple[]",
3918
+ internalType: "structIReleaseManagerTypes.Artifact[]",
3919
+ components: [
3920
+ {
3921
+ name: "digest",
3922
+ type: "bytes32",
3923
+ internalType: "bytes32"
3924
+ },
3925
+ {
3926
+ name: "registry",
3927
+ type: "string",
3928
+ internalType: "string"
3929
+ }
3930
+ ]
3931
+ },
3932
+ {
3933
+ name: "upgradeByTime",
3934
+ type: "uint32",
3935
+ internalType: "uint32"
3936
+ }
3937
+ ]
3938
+ },
3939
+ {
3940
+ name: "publicEnv",
3941
+ type: "bytes",
3942
+ internalType: "bytes"
3943
+ },
3944
+ {
3945
+ name: "encryptedEnv",
3946
+ type: "bytes",
3947
+ internalType: "bytes"
3948
+ },
3949
+ {
3950
+ name: "containerPolicy",
3951
+ type: "tuple",
3952
+ internalType: "structIAppController.ContainerPolicy",
3953
+ components: [
3954
+ {
3955
+ name: "args",
3956
+ type: "string[]",
3957
+ internalType: "string[]"
3958
+ },
3959
+ {
3960
+ name: "cmdOverride",
3961
+ type: "string[]",
3962
+ internalType: "string[]"
3963
+ },
3964
+ {
3965
+ name: "env",
3966
+ type: "tuple[]",
3967
+ internalType: "structIAppController.EnvVar[]",
3968
+ components: [
3969
+ {
3970
+ name: "key",
3971
+ type: "string",
3972
+ internalType: "string"
3973
+ },
3974
+ {
3975
+ name: "value",
3976
+ type: "string",
3977
+ internalType: "string"
3978
+ }
3979
+ ]
3980
+ },
3981
+ {
3982
+ name: "envOverride",
3983
+ type: "tuple[]",
3984
+ internalType: "structIAppController.EnvVar[]",
3985
+ components: [
3986
+ {
3987
+ name: "key",
3988
+ type: "string",
3989
+ internalType: "string"
3990
+ },
3991
+ {
3992
+ name: "value",
3993
+ type: "string",
3994
+ internalType: "string"
3995
+ }
3996
+ ]
3997
+ },
3998
+ {
3999
+ name: "restartPolicy",
4000
+ type: "string",
4001
+ internalType: "string"
4002
+ }
4003
+ ]
4004
+ }
4005
+ ]
4006
+ }
4007
+ ],
4008
+ anonymous: false
4009
+ },
4010
+ {
4011
+ type: "event",
4012
+ name: "GlobalMaxActiveAppsSet",
4013
+ inputs: [
4014
+ {
4015
+ name: "limit",
4016
+ type: "uint32",
4017
+ indexed: false,
4018
+ internalType: "uint32"
4019
+ }
4020
+ ],
4021
+ anonymous: false
4022
+ },
4023
+ {
4024
+ type: "event",
4025
+ name: "Initialized",
4026
+ inputs: [
4027
+ {
4028
+ name: "version",
4029
+ type: "uint8",
4030
+ indexed: false,
4031
+ internalType: "uint8"
4032
+ }
4033
+ ],
4034
+ anonymous: false
4035
+ },
4036
+ {
4037
+ type: "event",
4038
+ name: "MaxActiveAppsSet",
4039
+ inputs: [
4040
+ {
4041
+ name: "user",
4042
+ type: "address",
4043
+ indexed: true,
4044
+ internalType: "address"
4045
+ },
4046
+ {
4047
+ name: "limit",
4048
+ type: "uint32",
4049
+ indexed: false,
4050
+ internalType: "uint32"
4051
+ }
4052
+ ],
4053
+ anonymous: false
4054
+ },
4055
+ {
4056
+ type: "event",
4057
+ name: "UpgradeConfirmed",
4058
+ inputs: [
4059
+ {
4060
+ name: "app",
4061
+ type: "address",
4062
+ indexed: true,
4063
+ internalType: "contractIApp"
4064
+ },
4065
+ {
4066
+ name: "pendingReleaseBlockNumber",
4067
+ type: "uint32",
4068
+ indexed: false,
4069
+ internalType: "uint32"
4070
+ }
4071
+ ],
4072
+ anonymous: false
4073
+ },
4074
+ {
4075
+ type: "error",
4076
+ name: "AccountHasActiveApps",
4077
+ inputs: []
4078
+ },
4079
+ {
4080
+ type: "error",
4081
+ name: "AppAlreadyExists",
4082
+ inputs: []
4083
+ },
4084
+ {
4085
+ type: "error",
4086
+ name: "AppDoesNotExist",
4087
+ inputs: []
4088
+ },
4089
+ {
4090
+ type: "error",
4091
+ name: "GlobalMaxActiveAppsExceeded",
4092
+ inputs: []
4093
+ },
4094
+ {
4095
+ type: "error",
4096
+ name: "InvalidAppStatus",
4097
+ inputs: []
4098
+ },
4099
+ {
4100
+ type: "error",
4101
+ name: "InvalidPermissions",
4102
+ inputs: []
4103
+ },
4104
+ {
4105
+ type: "error",
4106
+ name: "InvalidReleaseMetadataURI",
4107
+ inputs: []
4108
+ },
4109
+ {
4110
+ type: "error",
4111
+ name: "InvalidShortString",
4112
+ inputs: []
4113
+ },
4114
+ {
4115
+ type: "error",
4116
+ name: "InvalidSignature",
4117
+ inputs: []
4118
+ },
4119
+ {
4120
+ type: "error",
4121
+ name: "MaxActiveAppsExceeded",
4122
+ inputs: []
4123
+ },
4124
+ {
4125
+ type: "error",
4126
+ name: "MoreThanOneArtifact",
4127
+ inputs: []
4128
+ },
4129
+ {
4130
+ type: "error",
4131
+ name: "NoPendingUpgrade",
4132
+ inputs: []
4133
+ },
4134
+ {
4135
+ type: "error",
4136
+ name: "SignatureExpired",
4137
+ inputs: []
4138
+ },
4139
+ {
4140
+ type: "error",
4141
+ name: "StringTooLong",
4142
+ inputs: [
4143
+ {
4144
+ name: "str",
4145
+ type: "string",
4146
+ internalType: "string"
4147
+ }
4148
+ ]
2979
4149
  }
2980
- return hash;
2981
- }
2982
-
2983
- // src/client/common/contract/caller.ts
2984
- var import_viem3 = require("viem");
2985
-
2986
- // src/client/common/utils/helpers.ts
2987
- var import_viem2 = require("viem");
2988
- var import_chains2 = require("viem/chains");
2989
- var import_accounts = require("viem/accounts");
2990
- function getChainFromID(chainID, fallback2 = import_chains2.sepolia) {
2991
- const id = Number(chainID);
2992
- return (0, import_viem2.extractChain)({ chains: SUPPORTED_CHAINS, id }) || fallback2;
2993
- }
2994
- function addHexPrefix(value) {
2995
- return value.startsWith("0x") ? value : `0x${value}`;
2996
- }
2997
- function stripHexPrefix(value) {
2998
- return value.startsWith("0x") ? value.slice(2) : value;
2999
- }
4150
+ ];
3000
4151
 
3001
- // src/client/common/abis/AppController.json
3002
- var AppController_default = [
4152
+ // src/client/common/abis/AppController.v1_4.json
4153
+ var AppController_v1_4_default = [
3003
4154
  {
3004
4155
  type: "constructor",
3005
4156
  inputs: [
@@ -4683,6 +5834,38 @@ var PermissionController_default = [
4683
5834
  ];
4684
5835
 
4685
5836
  // src/client/common/contract/caller.ts
5837
+ function appControllerAbiFor(environmentConfig) {
5838
+ return environmentConfig.releaseAbiVersion === "v1.4" ? AppController_v1_4_default : AppController_default;
5839
+ }
5840
+ function supportsContainerPolicy(environmentConfig) {
5841
+ return environmentConfig.releaseAbiVersion !== "v1.4";
5842
+ }
5843
+ function containerPolicyForViem(policy = EMPTY_CONTAINER_POLICY) {
5844
+ return {
5845
+ args: policy.args,
5846
+ cmdOverride: policy.cmdOverride,
5847
+ env: policy.env.map((e) => ({ key: e.key, value: e.value })),
5848
+ envOverride: policy.envOverride.map((e) => ({ key: e.key, value: e.value })),
5849
+ restartPolicy: policy.restartPolicy
5850
+ };
5851
+ }
5852
+ function releaseForViem(release, environmentConfig) {
5853
+ const base = {
5854
+ rmsRelease: {
5855
+ artifacts: release.rmsRelease.artifacts.map((artifact) => ({
5856
+ digest: `0x${(0, import_viem3.bytesToHex)(artifact.digest).slice(2).padStart(64, "0")}`,
5857
+ registry: artifact.registry
5858
+ })),
5859
+ upgradeByTime: release.rmsRelease.upgradeByTime
5860
+ },
5861
+ publicEnv: (0, import_viem3.bytesToHex)(release.publicEnv),
5862
+ encryptedEnv: (0, import_viem3.bytesToHex)(release.encryptedEnv)
5863
+ };
5864
+ if (!supportsContainerPolicy(environmentConfig)) {
5865
+ return base;
5866
+ }
5867
+ return { ...base, containerPolicy: containerPolicyForViem(release.containerPolicy) };
5868
+ }
4686
5869
  function formatETH(wei) {
4687
5870
  const eth = Number(wei) / 1e18;
4688
5871
  const costStr = eth.toFixed(6);
@@ -4699,7 +5882,7 @@ async function calculateAppID(options) {
4699
5882
  const saltHex = `0x${paddedSaltHex}`;
4700
5883
  const appID = await publicClient.readContract({
4701
5884
  address: environmentConfig.appControllerAddress,
4702
- abi: AppController_default,
5885
+ abi: appControllerAbiFor(environmentConfig),
4703
5886
  functionName: "calculateAppId",
4704
5887
  args: [ownerAddress, saltHex]
4705
5888
  });
@@ -4723,22 +5906,12 @@ async function prepareDeployBatch(options, logger = noopLogger) {
4723
5906
  const saltHexString = (0, import_viem3.bytesToHex)(salt).slice(2);
4724
5907
  const paddedSaltHex = saltHexString.padStart(64, "0");
4725
5908
  const saltHex = `0x${paddedSaltHex}`;
4726
- const releaseForViem = {
4727
- rmsRelease: {
4728
- artifacts: release.rmsRelease.artifacts.map((artifact) => ({
4729
- digest: `0x${(0, import_viem3.bytesToHex)(artifact.digest).slice(2).padStart(64, "0")}`,
4730
- registry: artifact.registry
4731
- })),
4732
- upgradeByTime: release.rmsRelease.upgradeByTime
4733
- },
4734
- publicEnv: (0, import_viem3.bytesToHex)(release.publicEnv),
4735
- encryptedEnv: (0, import_viem3.bytesToHex)(release.encryptedEnv)
4736
- };
5909
+ const release_ = releaseForViem(release, environmentConfig);
4737
5910
  const functionName = options.billTo === "app" ? "createAppWithIsolatedBilling" : "createApp";
4738
5911
  const createData = (0, import_viem3.encodeFunctionData)({
4739
- abi: AppController_default,
5912
+ abi: appControllerAbiFor(environmentConfig),
4740
5913
  functionName,
4741
- args: [saltHex, releaseForViem]
5914
+ args: [saltHex, release_]
4742
5915
  });
4743
5916
  const acceptAdminData = (0, import_viem3.encodeFunctionData)({
4744
5917
  abi: PermissionController_default,
@@ -4826,21 +5999,11 @@ async function prepareUpgradeBatch(options) {
4826
5999
  publicLogs,
4827
6000
  needsPermissionChange
4828
6001
  } = options;
4829
- const releaseForViem = {
4830
- rmsRelease: {
4831
- artifacts: release.rmsRelease.artifacts.map((artifact) => ({
4832
- digest: `0x${(0, import_viem3.bytesToHex)(artifact.digest).slice(2).padStart(64, "0")}`,
4833
- registry: artifact.registry
4834
- })),
4835
- upgradeByTime: release.rmsRelease.upgradeByTime
4836
- },
4837
- publicEnv: (0, import_viem3.bytesToHex)(release.publicEnv),
4838
- encryptedEnv: (0, import_viem3.bytesToHex)(release.encryptedEnv)
4839
- };
6002
+ const release_ = releaseForViem(release, environmentConfig);
4840
6003
  const upgradeData = (0, import_viem3.encodeFunctionData)({
4841
- abi: AppController_default,
6004
+ abi: appControllerAbiFor(environmentConfig),
4842
6005
  functionName: "upgradeApp",
4843
- args: [appID, releaseForViem]
6006
+ args: [appID, release_]
4844
6007
  });
4845
6008
  const executions = [
4846
6009
  {
@@ -4974,7 +6137,7 @@ ${pendingMessage}`);
4974
6137
  if (callError.data) {
4975
6138
  try {
4976
6139
  const decoded = (0, import_viem3.decodeErrorResult)({
4977
- abi: AppController_default,
6140
+ abi: appControllerAbiFor(environmentConfig),
4978
6141
  data: callError.data
4979
6142
  });
4980
6143
  const formattedError = formatAppControllerError(decoded);
@@ -5027,7 +6190,7 @@ function formatAppControllerError(decoded) {
5027
6190
  async function getActiveAppCount(publicClient, environmentConfig, user) {
5028
6191
  const count = await publicClient.readContract({
5029
6192
  address: environmentConfig.appControllerAddress,
5030
- abi: AppController_default,
6193
+ abi: appControllerAbiFor(environmentConfig),
5031
6194
  functionName: "getActiveAppCount",
5032
6195
  args: [user]
5033
6196
  });
@@ -5036,7 +6199,7 @@ async function getActiveAppCount(publicClient, environmentConfig, user) {
5036
6199
  async function getMaxActiveAppsPerUser(publicClient, environmentConfig, user) {
5037
6200
  const quota = await publicClient.readContract({
5038
6201
  address: environmentConfig.appControllerAddress,
5039
- abi: AppController_default,
6202
+ abi: appControllerAbiFor(environmentConfig),
5040
6203
  functionName: "getMaxActiveAppsPerUser",
5041
6204
  args: [user]
5042
6205
  });
@@ -5045,7 +6208,7 @@ async function getMaxActiveAppsPerUser(publicClient, environmentConfig, user) {
5045
6208
  async function getBillingType(publicClient, environmentConfig, app) {
5046
6209
  const result = await publicClient.readContract({
5047
6210
  address: environmentConfig.appControllerAddress,
5048
- abi: AppController_default,
6211
+ abi: appControllerAbiFor(environmentConfig),
5049
6212
  functionName: "getBillingType",
5050
6213
  args: [app]
5051
6214
  });
@@ -5054,7 +6217,7 @@ async function getBillingType(publicClient, environmentConfig, app) {
5054
6217
  async function getAppsByBillingAccount(publicClient, environmentConfig, account, offset, limit) {
5055
6218
  const result = await publicClient.readContract({
5056
6219
  address: environmentConfig.appControllerAddress,
5057
- abi: AppController_default,
6220
+ abi: appControllerAbiFor(environmentConfig),
5058
6221
  functionName: "getAppsByBillingAccount",
5059
6222
  args: [account, offset, limit]
5060
6223
  });
@@ -5108,12 +6271,41 @@ async function undelegate(options, logger = noopLogger) {
5108
6271
  return hash;
5109
6272
  }
5110
6273
 
6274
+ // src/client/common/gas/insufficientGas.ts
6275
+ var import_viem4 = require("viem");
6276
+ var InsufficientGasError = class extends Error {
6277
+ constructor(args) {
6278
+ const requiredEth = (0, import_viem4.formatEther)(args.requiredWei);
6279
+ const availableEth = (0, import_viem4.formatEther)(args.availableWei);
6280
+ super(
6281
+ `Insufficient ETH for gas: wallet ${args.address} has ${availableEth} ETH but this transaction needs ~${requiredEth} ETH.
6282
+ Compute credits do not pay on-chain gas \u2014 fund the wallet with ETH and retry.`
6283
+ );
6284
+ this.name = "InsufficientGasError";
6285
+ this.address = args.address;
6286
+ this.requiredWei = args.requiredWei;
6287
+ this.availableWei = args.availableWei;
6288
+ this.requiredEth = requiredEth;
6289
+ this.availableEth = availableEth;
6290
+ }
6291
+ };
6292
+ async function assertSufficientGas(args) {
6293
+ const availableWei = await args.publicClient.getBalance({ address: args.address });
6294
+ if (availableWei < args.gasEstimate.maxCostWei) {
6295
+ throw new InsufficientGasError({
6296
+ address: args.address,
6297
+ requiredWei: args.gasEstimate.maxCostWei,
6298
+ availableWei
6299
+ });
6300
+ }
6301
+ }
6302
+
5111
6303
  // src/client/common/utils/userapi.ts
5112
6304
  var import_axios2 = __toESM(require("axios"), 1);
5113
6305
 
5114
6306
  // src/client/common/utils/auth.ts
5115
- var import_viem4 = require("viem");
5116
- var APP_CONTROLLER_ABI = (0, import_viem4.parseAbi)([
6307
+ var import_viem5 = require("viem");
6308
+ var APP_CONTROLLER_ABI = (0, import_viem5.parseAbi)([
5117
6309
  "function calculateApiPermissionDigestHash(bytes4 permission, uint256 expiry) view returns (bytes32)"
5118
6310
  ]);
5119
6311
  async function calculatePermissionSignature(options) {
@@ -5134,43 +6326,13 @@ async function calculatePermissionSignature(options) {
5134
6326
  });
5135
6327
  return { signature, digest };
5136
6328
  }
5137
- var generateBillingSigData = (product, expiry) => {
5138
- return {
5139
- domain: {
5140
- name: "EigenCloud Billing API",
5141
- version: "1"
5142
- },
5143
- types: {
5144
- BillingAuth: [
5145
- { name: "product", type: "string" },
5146
- { name: "expiry", type: "uint256" }
5147
- ]
5148
- },
5149
- primaryType: "BillingAuth",
5150
- message: {
5151
- product,
5152
- expiry
5153
- }
5154
- };
5155
- };
5156
- async function calculateBillingAuthSignature(options) {
5157
- const { walletClient, product, expiry } = options;
5158
- const account = walletClient.account;
5159
- if (!account) {
5160
- throw new Error("WalletClient must have an account attached");
5161
- }
5162
- const signature = await walletClient.signTypedData({
5163
- account,
5164
- ...generateBillingSigData(product, expiry)
5165
- });
5166
- return { signature, expiry };
5167
- }
5168
6329
 
5169
6330
  // src/client/common/utils/retry.ts
5170
6331
  var import_axios = __toESM(require("axios"), 1);
5171
6332
  var MAX_RETRIES = 5;
5172
6333
  var INITIAL_BACKOFF_MS = 1e3;
5173
6334
  var MAX_BACKOFF_MS = 3e4;
6335
+ var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 502, 503, 504]);
5174
6336
  function sleep(ms) {
5175
6337
  return new Promise((resolve2) => setTimeout(resolve2, ms));
5176
6338
  }
@@ -5190,7 +6352,7 @@ async function requestWithRetry(config) {
5190
6352
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
5191
6353
  const res = await (0, import_axios.default)({ ...config, validateStatus: () => true });
5192
6354
  lastResponse = res;
5193
- if (res.status !== 429) {
6355
+ if (!RETRYABLE_STATUSES.has(res.status)) {
5194
6356
  return res;
5195
6357
  }
5196
6358
  if (attempt < MAX_RETRIES) {
@@ -5219,7 +6381,7 @@ var CanViewAppLogsPermission = "0x2fd3f2fe";
5219
6381
  var CanViewSensitiveAppInfoPermission = "0x0e67b22f";
5220
6382
  var CanUpdateAppProfilePermission = "0x036fef61";
5221
6383
  function getDefaultClientId() {
5222
- const version = true ? "1.0.0-devep7" : "0.0.0";
6384
+ const version = true ? "1.0.0" : "0.0.0";
5223
6385
  return `ecloud-sdk/v${version}`;
5224
6386
  }
5225
6387
  var UserApiClient = class {
@@ -5254,7 +6416,6 @@ var UserApiClient = class {
5254
6416
  status: app.app_status,
5255
6417
  ip: app.ip,
5256
6418
  machineType: app.machine_type,
5257
- hostname: app.hostname,
5258
6419
  profile: app.profile,
5259
6420
  metrics: app.metrics,
5260
6421
  evmAddresses,
@@ -5320,28 +6481,6 @@ var UserApiClient = class {
5320
6481
  status: app.app_status || app.App_Status || ""
5321
6482
  }));
5322
6483
  }
5323
- /**
5324
- * Get deployments for an app from the gRPC-gateway endpoint.
5325
- * Returns deployment records including upgrade_phase for tracking upgrade progress.
5326
- *
5327
- * Endpoint: GET /v1/apps/:appAddress/deployments
5328
- */
5329
- async getDeployments(appAddress) {
5330
- const endpoint = `${this.config.userApiServerURL}/v1/apps/${appAddress}/deployments`;
5331
- const response = await this.makeEIP712AuthenticatedRequest(endpoint);
5332
- const result = await response.json();
5333
- const deployments = result.deployments || [];
5334
- return deployments.map((dep) => ({
5335
- id: dep.id || "",
5336
- externalId: dep.external_id || dep.externalId || "",
5337
- endpoint: dep.endpoint || "",
5338
- releaseId: dep.release_id || dep.releaseId || "",
5339
- upgradePhase: dep.upgrade_phase || dep.upgradePhase || "",
5340
- replacesDeploymentId: dep.replaces_deployment_id || dep.replacesDeploymentId || "",
5341
- createdAt: dep.created_at || dep.createdAt || "",
5342
- updatedAt: dep.updated_at || dep.updatedAt || ""
5343
- }));
5344
- }
5345
6484
  /**
5346
6485
  * Upload app profile information with optional image
5347
6486
  *
@@ -5475,48 +6614,6 @@ Please check:
5475
6614
  "X-eigenx-expiry": expiry.toString()
5476
6615
  };
5477
6616
  }
5478
- /**
5479
- * Make an EIP-712 authenticated request to the gRPC-gateway endpoints.
5480
- * Uses the billing/compute auth pattern: Authorization + X-Account + X-Expiry headers.
5481
- */
5482
- async makeEIP712AuthenticatedRequest(url) {
5483
- const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
5484
- const { signature } = await calculateBillingAuthSignature({
5485
- walletClient: this.walletClient,
5486
- product: "compute",
5487
- expiry
5488
- });
5489
- const headers = {
5490
- Authorization: `Bearer ${signature}`,
5491
- "X-Account": this.address,
5492
- "X-Expiry": expiry.toString(),
5493
- "x-client-id": this.clientId
5494
- };
5495
- try {
5496
- const response = await requestWithRetry({
5497
- method: "GET",
5498
- url,
5499
- headers,
5500
- maxRedirects: 0,
5501
- withCredentials: true
5502
- });
5503
- const status = response.status;
5504
- if (status < 200 || status >= 300) {
5505
- const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
5506
- throw new Error(`gRPC-gateway request failed: ${status} - ${body}`);
5507
- }
5508
- return {
5509
- json: async () => response.data,
5510
- text: async () => typeof response.data === "string" ? response.data : JSON.stringify(response.data)
5511
- };
5512
- } catch (error) {
5513
- if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
5514
- const cause = error.cause?.message || error.cause || error.message;
5515
- throw new Error(`Failed to connect to API at ${url}: ${cause}`);
5516
- }
5517
- throw error;
5518
- }
5519
- }
5520
6617
  // ==========================================================================
5521
6618
  // SIWE Session Management
5522
6619
  // ==========================================================================
@@ -5622,8 +6719,36 @@ function transformAppRelease(raw) {
5622
6719
 
5623
6720
  // src/client/common/contract/watcher.ts
5624
6721
  var WATCH_POLL_INTERVAL_SECONDS = 5;
6722
+ var WATCH_HEARTBEAT_INTERVAL_SECONDS = 30;
6723
+ var WATCH_DEFAULT_TIMEOUT_SECONDS = 10 * 60;
5625
6724
  var APP_STATUS_RUNNING = "Running";
5626
6725
  var APP_STATUS_FAILED = "Failed";
6726
+ var WatchTimeoutError = class extends Error {
6727
+ constructor(args) {
6728
+ super(
6729
+ args.message ?? `Timed out after ${args.elapsedSeconds}s waiting for app ${args.appId} (last status: ${args.lastStatus ?? "unknown"})`
6730
+ );
6731
+ this.name = "WatchTimeoutError";
6732
+ this.appId = args.appId;
6733
+ this.elapsedSeconds = args.elapsedSeconds;
6734
+ this.lastStatus = args.lastStatus;
6735
+ this.timeoutSeconds = args.timeoutSeconds;
6736
+ }
6737
+ };
6738
+ function resolveWatchTimeoutSeconds(explicit) {
6739
+ if (typeof explicit === "number" && Number.isFinite(explicit) && explicit > 0) {
6740
+ return Math.floor(explicit);
6741
+ }
6742
+ const raw = process.env.ECLOUD_WATCH_TIMEOUT_SECONDS;
6743
+ if (raw === void 0 || raw === "") {
6744
+ return WATCH_DEFAULT_TIMEOUT_SECONDS;
6745
+ }
6746
+ const parsed = Number(raw);
6747
+ if (!Number.isFinite(parsed) || parsed <= 0) {
6748
+ return WATCH_DEFAULT_TIMEOUT_SECONDS;
6749
+ }
6750
+ return Math.floor(parsed);
6751
+ }
5627
6752
  async function watchUntilRunning(options, logger) {
5628
6753
  const { walletClient, publicClient, environmentConfig, appId } = options;
5629
6754
  const userApiClient = new UserApiClient(environmentConfig, walletClient, publicClient);
@@ -5654,8 +6779,20 @@ async function watchUntilRunning(options, logger) {
5654
6779
  return false;
5655
6780
  };
5656
6781
  const startTime = Date.now();
6782
+ const timeoutSeconds = resolveWatchTimeoutSeconds(options.timeoutSeconds);
5657
6783
  let lastLoggedStatus;
6784
+ let lastHeartbeatAt = startTime;
5658
6785
  while (true) {
6786
+ const elapsedMs = Date.now() - startTime;
6787
+ const elapsed = Math.round(elapsedMs / 1e3);
6788
+ if (elapsed >= timeoutSeconds) {
6789
+ throw new WatchTimeoutError({
6790
+ appId,
6791
+ elapsedSeconds: elapsed,
6792
+ lastStatus: lastLoggedStatus,
6793
+ timeoutSeconds
6794
+ });
6795
+ }
5659
6796
  try {
5660
6797
  const info = await userApiClient.getInfos([appId], 1);
5661
6798
  if (info.length === 0) {
@@ -5665,62 +6802,35 @@ async function watchUntilRunning(options, logger) {
5665
6802
  const appInfo = info[0];
5666
6803
  const currentStatus = appInfo.status;
5667
6804
  const currentIP = appInfo.ip || "";
5668
- const elapsed = Math.round((Date.now() - startTime) / 1e3);
5669
6805
  if (currentStatus !== lastLoggedStatus) {
5670
6806
  logger.info(`Status: ${currentStatus} (${elapsed}s)`);
5671
6807
  lastLoggedStatus = currentStatus;
6808
+ lastHeartbeatAt = Date.now();
6809
+ } else if (Date.now() - lastHeartbeatAt >= WATCH_HEARTBEAT_INTERVAL_SECONDS * 1e3) {
6810
+ logger.info(`Status: ${currentStatus} (${elapsed}s)`);
6811
+ lastHeartbeatAt = Date.now();
5672
6812
  }
5673
6813
  if (stopCondition(currentStatus, currentIP)) {
5674
6814
  return currentIP || void 0;
5675
6815
  }
5676
6816
  await sleep2(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5677
6817
  } catch (error) {
6818
+ if (error instanceof WatchTimeoutError) {
6819
+ throw error;
6820
+ }
5678
6821
  logger.warn(`Failed to fetch app info: ${error.message}`);
5679
6822
  await sleep2(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5680
6823
  }
5681
6824
  }
5682
6825
  }
5683
6826
  var APP_STATUS_STOPPED = "Stopped";
5684
- var UPGRADE_PHASE_LABELS = {
5685
- provisioning: "Provisioning new instance",
5686
- health_check: "Running health checks",
5687
- draining: "Switching traffic & draining old instance",
5688
- complete: "Complete",
5689
- rolling_back: "Rolling back",
5690
- rollback_done: "Rollback complete",
5691
- db_handoff: "Database handoff",
5692
- // Prewarm-detach phases
5693
- awaiting_userdata: "Waiting for instance readiness",
5694
- draining_old: "Draining old instance",
5695
- detached: "Detaching storage",
5696
- attached_to_new: "Attaching storage to new instance",
5697
- finalizing: "Finalizing new instance",
5698
- flipping: "Switching traffic",
5699
- teardown_old: "Cleaning up old instance"
5700
- };
5701
- async function fetchUpgradePhase(userApiClient, appId) {
5702
- try {
5703
- const deployments = await userApiClient.getDeployments(appId);
5704
- if (deployments.length === 0) return void 0;
5705
- const sorted = [...deployments].sort((a, b) => {
5706
- const ta = a.createdAt ? new Date(a.createdAt).getTime() : 0;
5707
- const tb = b.createdAt ? new Date(b.createdAt).getTime() : 0;
5708
- return tb - ta;
5709
- });
5710
- return sorted[0].upgradePhase || void 0;
5711
- } catch {
5712
- return void 0;
5713
- }
5714
- }
5715
6827
  async function watchUntilUpgradeComplete(options, logger) {
5716
6828
  const { walletClient, publicClient, environmentConfig, appId } = options;
6829
+ const timeoutSeconds = resolveWatchTimeoutSeconds(options.timeoutSeconds);
5717
6830
  const userApiClient = new UserApiClient(environmentConfig, walletClient, publicClient);
5718
6831
  let initialStatus;
5719
6832
  let initialIP;
5720
6833
  let hasChanged = false;
5721
- const startTime = Date.now();
5722
- let lastLoggedStatus;
5723
- let lastLoggedPhase;
5724
6834
  const stopCondition = (status, ip) => {
5725
6835
  if (!initialStatus) {
5726
6836
  initialStatus = status;
@@ -5754,37 +6864,44 @@ async function watchUntilUpgradeComplete(options, logger) {
5754
6864
  }
5755
6865
  return false;
5756
6866
  };
6867
+ const startTime = Date.now();
6868
+ const deadline = startTime + timeoutSeconds * 1e3;
6869
+ let lastLoggedStatus;
6870
+ let lastObservedStatus;
5757
6871
  while (true) {
6872
+ if (Date.now() >= deadline) {
6873
+ const elapsedSeconds = Math.round((Date.now() - startTime) / 1e3);
6874
+ throw new WatchTimeoutError({
6875
+ appId,
6876
+ lastStatus: lastObservedStatus,
6877
+ elapsedSeconds,
6878
+ timeoutSeconds
6879
+ });
6880
+ }
5758
6881
  try {
5759
- const [infoResult, upgradePhase] = await Promise.all([
5760
- userApiClient.getInfos([appId], 1),
5761
- fetchUpgradePhase(userApiClient, appId)
5762
- ]);
5763
- if (infoResult.length === 0) {
6882
+ const info = await userApiClient.getInfos([appId], 1);
6883
+ if (info.length === 0) {
5764
6884
  await sleep2(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5765
6885
  continue;
5766
6886
  }
5767
- const appInfo = infoResult[0];
6887
+ const appInfo = info[0];
5768
6888
  const currentStatus = appInfo.status;
5769
6889
  const currentIP = appInfo.ip || "";
6890
+ lastObservedStatus = currentStatus;
5770
6891
  const elapsed = Math.round((Date.now() - startTime) / 1e3);
5771
- if (upgradePhase && upgradePhase !== lastLoggedPhase) {
5772
- const label = UPGRADE_PHASE_LABELS[upgradePhase] || upgradePhase;
5773
- logger.info(`Phase: ${label} (${elapsed}s)`);
5774
- lastLoggedPhase = upgradePhase;
5775
- }
5776
- if (!upgradePhase && currentStatus !== lastLoggedStatus) {
6892
+ if (currentStatus !== lastLoggedStatus) {
5777
6893
  logger.info(`Status: ${currentStatus} (${elapsed}s)`);
5778
6894
  lastLoggedStatus = currentStatus;
5779
6895
  }
5780
6896
  if (stopCondition(currentStatus, currentIP)) {
5781
- const totalElapsed = Math.round((Date.now() - startTime) / 1e3);
5782
- logger.info(`Upgrade completed in ${totalElapsed}s`);
5783
6897
  return;
5784
6898
  }
5785
6899
  await sleep2(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5786
6900
  } catch (error) {
5787
- if (error.message?.includes("entered")) {
6901
+ if (error instanceof WatchTimeoutError) {
6902
+ throw error;
6903
+ }
6904
+ if (typeof error?.message === "string" && error.message.includes("Failed")) {
5788
6905
  throw error;
5789
6906
  }
5790
6907
  logger.warn(`Failed to fetch app info: ${error.message}`);
@@ -5798,7 +6915,7 @@ function sleep2(ms) {
5798
6915
 
5799
6916
  // src/client/common/utils/validation.ts
5800
6917
  var import_fs = __toESM(require("fs"), 1);
5801
- var import_viem5 = require("viem");
6918
+ var import_viem6 = require("viem");
5802
6919
  function validateAppName(name) {
5803
6920
  if (!name) {
5804
6921
  throw new Error("App name cannot be empty");
@@ -5846,7 +6963,7 @@ function validateAppID(appID) {
5846
6963
  throw new Error("App ID is required");
5847
6964
  }
5848
6965
  const normalized = typeof appID === "string" ? addHexPrefix(appID) : appID;
5849
- if ((0, import_viem5.isAddress)(normalized)) {
6966
+ if ((0, import_viem6.isAddress)(normalized)) {
5850
6967
  return normalized;
5851
6968
  }
5852
6969
  throw new Error(`Invalid app ID: '${appID}' is not a valid address`);
@@ -5895,27 +7012,28 @@ var ChainAddresses = {
5895
7012
  PermissionController: "0x44632dfBdCb6D3E21EF613B0ca8A6A0c618F5a37"
5896
7013
  }
5897
7014
  };
5898
- var PLATFORM_ENV_TESTNET_SEPOLIA = "testnet-sepolia";
5899
- var PLATFORM_ENV_MAINNET_ETHEREUM = "mainnet-ethereum";
5900
- var DEFAULT_APP_BASE_DOMAIN = "eigencloud.xyz";
5901
7015
  var ENVIRONMENTS = {
5902
7016
  "sepolia-dev": {
5903
7017
  name: "sepolia",
5904
7018
  build: "dev",
5905
7019
  appControllerAddress: "0xa86DC1C47cb2518327fB4f9A1627F51966c83B92",
7020
+ releaseAbiVersion: "v1.5",
7021
+ // AppController upgraded to v1.5.x (containerPolicy)
5906
7022
  permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
5907
7023
  erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
5908
7024
  kmsServerURL: "http://10.128.0.57:8080",
5909
7025
  userApiServerURL: "https://userapi-compute-sepolia-dev.eigencloud.xyz",
5910
7026
  defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com",
5911
7027
  usdcCreditsAddress: "0xbdA3897c3A428763B59015C64AB766c288C97376",
5912
- platformEnv: PLATFORM_ENV_TESTNET_SEPOLIA,
5913
- appBaseDomain: DEFAULT_APP_BASE_DOMAIN
7028
+ baseUsdcCreditsAddress: "0x7673a47463F80c6a3553Db9E54c8cDcd5313d0ac",
7029
+ baseRPCURL: "https://base-sepolia-rpc.publicnode.com"
5914
7030
  },
5915
7031
  sepolia: {
5916
7032
  name: "sepolia",
5917
7033
  build: "prod",
5918
7034
  appControllerAddress: "0x0dd810a6ffba6a9820a10d97b659f07d8d23d4E2",
7035
+ releaseAbiVersion: "v1.4",
7036
+ // prod still on AppController v1.4.0 (3-field Release)
5919
7037
  permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
5920
7038
  erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
5921
7039
  kmsServerURL: "http://10.128.15.203:8080",
@@ -5923,21 +7041,21 @@ var ENVIRONMENTS = {
5923
7041
  defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com",
5924
7042
  billingRPCURL: "https://ethereum-rpc.publicnode.com",
5925
7043
  usdcCreditsAddress: "0xed9c88640ca9149Bd9f7ee6620074af10F2E145d",
5926
- platformEnv: PLATFORM_ENV_TESTNET_SEPOLIA,
5927
- appBaseDomain: DEFAULT_APP_BASE_DOMAIN
7044
+ baseUsdcCreditsAddress: "0x7673a47463F80c6a3553Db9E54c8cDcd5313d0ac",
7045
+ baseRPCURL: "https://base-sepolia-rpc.publicnode.com"
5928
7046
  },
5929
7047
  "mainnet-alpha": {
5930
7048
  name: "mainnet-alpha",
5931
7049
  build: "prod",
5932
7050
  appControllerAddress: "0xc38d35Fc995e75342A21CBd6D770305b142Fbe67",
7051
+ releaseAbiVersion: "v1.4",
7052
+ // prod still on AppController v1.4.0 (3-field Release)
5933
7053
  permissionControllerAddress: ChainAddresses[MAINNET_CHAIN_ID].PermissionController,
5934
7054
  erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
5935
7055
  kmsServerURL: "http://10.128.0.2:8080",
5936
7056
  userApiServerURL: "https://userapi-compute.eigencloud.xyz",
5937
7057
  defaultRPCURL: "https://ethereum-rpc.publicnode.com",
5938
- usdcCreditsAddress: "0xed9c88640ca9149Bd9f7ee6620074af10F2E145d",
5939
- platformEnv: PLATFORM_ENV_MAINNET_ETHEREUM,
5940
- appBaseDomain: DEFAULT_APP_BASE_DOMAIN
7058
+ usdcCreditsAddress: "0xed9c88640ca9149Bd9f7ee6620074af10F2E145d"
5941
7059
  }
5942
7060
  };
5943
7061
  var CHAIN_ID_TO_ENVIRONMENT = {
@@ -5981,7 +7099,7 @@ function getEnvironmentConfig(environment, chainID) {
5981
7099
  };
5982
7100
  }
5983
7101
  function getBuildType() {
5984
- const buildTimeType = true ? "dev"?.toLowerCase() : void 0;
7102
+ const buildTimeType = true ? "prod"?.toLowerCase() : void 0;
5985
7103
  const runtimeType = process.env.BUILD_TYPE?.toLowerCase();
5986
7104
  const buildType = buildTimeType || runtimeType;
5987
7105
  if (buildType === "dev") {
@@ -6332,6 +7450,11 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
6332
7450
  executions: batch.executions,
6333
7451
  authorizationList
6334
7452
  });
7453
+ await assertSufficientGas({
7454
+ publicClient: batch.publicClient,
7455
+ address: batch.walletClient.account.address,
7456
+ gasEstimate
7457
+ });
6335
7458
  const data = {
6336
7459
  appId: batch.appId,
6337
7460
  salt: batch.salt,
@@ -6590,6 +7713,11 @@ async function prepareDeploy(options, logger = defaultLogger) {
6590
7713
  executions: batch.executions,
6591
7714
  authorizationList
6592
7715
  });
7716
+ await assertSufficientGas({
7717
+ publicClient: batch.publicClient,
7718
+ address: batch.walletClient.account.address,
7719
+ gasEstimate
7720
+ });
6593
7721
  const data = {
6594
7722
  appId: batch.appId,
6595
7723
  salt: batch.salt,
@@ -6626,7 +7754,7 @@ async function executeDeploy(options) {
6626
7754
  }
6627
7755
  );
6628
7756
  }
6629
- async function watchDeployment(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry) {
7757
+ async function watchDeployment(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry, opts) {
6630
7758
  return withSDKTelemetry(
6631
7759
  {
6632
7760
  functionName: "watchDeployment",
@@ -6642,7 +7770,8 @@ async function watchDeployment(appId, walletClient, publicClient, environmentCon
6642
7770
  walletClient,
6643
7771
  publicClient,
6644
7772
  environmentConfig,
6645
- appId
7773
+ appId,
7774
+ timeoutSeconds: opts?.timeoutSeconds
6646
7775
  },
6647
7776
  logger
6648
7777
  );
@@ -6738,6 +7867,11 @@ async function prepareUpgradeFromVerifiableBuild(options, logger = defaultLogger
6738
7867
  executions: batch.executions,
6739
7868
  authorizationList
6740
7869
  });
7870
+ await assertSufficientGas({
7871
+ publicClient: batch.publicClient,
7872
+ address: batch.walletClient.account.address,
7873
+ gasEstimate
7874
+ });
6741
7875
  const data = {
6742
7876
  appId: batch.appId,
6743
7877
  executions: batch.executions,
@@ -6927,6 +8061,11 @@ async function prepareUpgrade(options, logger = defaultLogger) {
6927
8061
  executions: batch.executions,
6928
8062
  authorizationList
6929
8063
  });
8064
+ await assertSufficientGas({
8065
+ publicClient: batch.publicClient,
8066
+ address: batch.walletClient.account.address,
8067
+ gasEstimate
8068
+ });
6930
8069
  const data = {
6931
8070
  appId: batch.appId,
6932
8071
  executions: batch.executions,
@@ -6961,7 +8100,7 @@ async function executeUpgrade(options) {
6961
8100
  }
6962
8101
  );
6963
8102
  }
6964
- async function watchUpgrade(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry) {
8103
+ async function watchUpgrade(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry, opts) {
6965
8104
  return withSDKTelemetry(
6966
8105
  {
6967
8106
  functionName: "watchUpgrade",
@@ -6977,7 +8116,8 @@ async function watchUpgrade(appId, walletClient, publicClient, environmentConfig
6977
8116
  walletClient,
6978
8117
  publicClient,
6979
8118
  environmentConfig,
6980
- appId
8119
+ appId,
8120
+ timeoutSeconds: opts?.timeoutSeconds
6981
8121
  },
6982
8122
  logger
6983
8123
  );
@@ -7635,27 +8775,27 @@ async function logs(options, walletClient, publicClient, environmentConfig, logg
7635
8775
  }
7636
8776
 
7637
8777
  // src/client/modules/compute/app/index.ts
7638
- var CONTROLLER_ABI = (0, import_viem6.parseAbi)([
8778
+ var CONTROLLER_ABI = (0, import_viem7.parseAbi)([
7639
8779
  "function startApp(address appId)",
7640
8780
  "function stopApp(address appId)",
7641
8781
  "function terminateApp(address appId)"
7642
8782
  ]);
7643
8783
  function encodeStartAppData(appId) {
7644
- return (0, import_viem6.encodeFunctionData)({
8784
+ return (0, import_viem7.encodeFunctionData)({
7645
8785
  abi: CONTROLLER_ABI,
7646
8786
  functionName: "startApp",
7647
8787
  args: [appId]
7648
8788
  });
7649
8789
  }
7650
8790
  function encodeStopAppData(appId) {
7651
- return (0, import_viem6.encodeFunctionData)({
8791
+ return (0, import_viem7.encodeFunctionData)({
7652
8792
  abi: CONTROLLER_ABI,
7653
8793
  functionName: "stopApp",
7654
8794
  args: [appId]
7655
8795
  });
7656
8796
  }
7657
8797
  function encodeTerminateAppData(appId) {
7658
- return (0, import_viem6.encodeFunctionData)({
8798
+ return (0, import_viem7.encodeFunctionData)({
7659
8799
  abi: CONTROLLER_ABI,
7660
8800
  functionName: "terminateApp",
7661
8801
  args: [appId]
@@ -7669,7 +8809,7 @@ function createAppModule(ctx) {
7669
8809
  }
7670
8810
  const account = walletClient.account;
7671
8811
  const environment = getEnvironmentConfig(ctx.environment);
7672
- const logger = getLogger(ctx.verbose);
8812
+ const logger = ctx.logger ?? getLogger(ctx.verbose);
7673
8813
  return {
7674
8814
  async create(opts) {
7675
8815
  return createApp(opts, logger);
@@ -7781,14 +8921,15 @@ function createAppModule(ctx) {
7781
8921
  imageRef: result.imageRef
7782
8922
  };
7783
8923
  },
7784
- async watchDeployment(appId) {
8924
+ async watchDeployment(appId, opts) {
7785
8925
  return watchDeployment(
7786
8926
  appId,
7787
8927
  walletClient,
7788
8928
  publicClient,
7789
8929
  environment,
7790
8930
  logger,
7791
- skipTelemetry
8931
+ skipTelemetry,
8932
+ opts
7792
8933
  );
7793
8934
  },
7794
8935
  // Granular upgrade control
@@ -7846,8 +8987,16 @@ function createAppModule(ctx) {
7846
8987
  imageRef: result.imageRef
7847
8988
  };
7848
8989
  },
7849
- async watchUpgrade(appId) {
7850
- return watchUpgrade(appId, walletClient, publicClient, environment, logger, skipTelemetry);
8990
+ async watchUpgrade(appId, opts) {
8991
+ return watchUpgrade(
8992
+ appId,
8993
+ walletClient,
8994
+ publicClient,
8995
+ environment,
8996
+ logger,
8997
+ skipTelemetry,
8998
+ opts
8999
+ );
7851
9000
  },
7852
9001
  // Profile management
7853
9002
  async setProfile(appId, profile) {
@@ -7898,7 +9047,7 @@ function createAppModule(ctx) {
7898
9047
  },
7899
9048
  async () => {
7900
9049
  const pendingMessage = `Starting app ${appId}...`;
7901
- const data = (0, import_viem6.encodeFunctionData)({
9050
+ const data = (0, import_viem7.encodeFunctionData)({
7902
9051
  abi: CONTROLLER_ABI,
7903
9052
  functionName: "startApp",
7904
9053
  args: [appId]
@@ -7930,7 +9079,7 @@ function createAppModule(ctx) {
7930
9079
  },
7931
9080
  async () => {
7932
9081
  const pendingMessage = `Stopping app ${appId}...`;
7933
- const data = (0, import_viem6.encodeFunctionData)({
9082
+ const data = (0, import_viem7.encodeFunctionData)({
7934
9083
  abi: CONTROLLER_ABI,
7935
9084
  functionName: "stopApp",
7936
9085
  args: [appId]
@@ -7962,7 +9111,7 @@ function createAppModule(ctx) {
7962
9111
  },
7963
9112
  async () => {
7964
9113
  const pendingMessage = `Terminating app ${appId}...`;
7965
- const data = (0, import_viem6.encodeFunctionData)({
9114
+ const data = (0, import_viem7.encodeFunctionData)({
7966
9115
  abi: CONTROLLER_ABI,
7967
9116
  functionName: "terminateApp",
7968
9117
  args: [appId]