@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/index.cjs CHANGED
@@ -164,14 +164,17 @@ var index_exports = {};
164
164
  __export(index_exports, {
165
165
  AttestClient: () => AttestClient,
166
166
  AuthRequiredError: () => AuthRequiredError,
167
+ BASE_SEPOLIA_CHAIN_ID: () => BASE_SEPOLIA_CHAIN_ID,
167
168
  BUILD_STATUS: () => BUILD_STATUS,
168
169
  BadRequestError: () => BadRequestError,
169
170
  BillingApiClient: () => BillingApiClient,
170
171
  BuildError: () => BuildError,
171
172
  BuildFailedError: () => BuildFailedError,
172
173
  ConflictError: () => ConflictError,
174
+ EMPTY_CONTAINER_POLICY: () => EMPTY_CONTAINER_POLICY,
173
175
  ERC20ABI: () => ERC20_default,
174
176
  ForbiddenError: () => ForbiddenError,
177
+ InsufficientGasError: () => InsufficientGasError,
175
178
  JwtProvider: () => JwtProvider,
176
179
  NoopClient: () => NoopClient,
177
180
  NotFoundError: () => NotFoundError,
@@ -181,14 +184,18 @@ __export(index_exports, {
181
184
  TimeoutError: () => TimeoutError,
182
185
  USDCCreditsABI: () => USDCCredits_default,
183
186
  UserApiClient: () => UserApiClient,
187
+ WATCH_DEFAULT_TIMEOUT_SECONDS: () => WATCH_DEFAULT_TIMEOUT_SECONDS,
188
+ WatchTimeoutError: () => WatchTimeoutError,
184
189
  addHexPrefix: () => addHexPrefix,
185
190
  addMetric: () => addMetric,
186
191
  addMetricWithDimensions: () => addMetricWithDimensions,
192
+ assertSufficientGas: () => assertSufficientGas,
187
193
  assertValidFilePath: () => assertValidFilePath,
188
194
  assertValidImageReference: () => assertValidImageReference,
189
195
  assertValidPrivateKey: () => assertValidPrivateKey,
190
196
  calculateAppID: () => calculateAppID,
191
197
  checkERC7702Delegation: () => checkERC7702Delegation,
198
+ createAdminModule: () => createAdminModule,
192
199
  createApp: () => createApp,
193
200
  createAppEnvironment: () => createAppEnvironment,
194
201
  createBillingModule: () => createBillingModule,
@@ -284,7 +291,7 @@ __export(index_exports, {
284
291
  module.exports = __toCommonJS(index_exports);
285
292
 
286
293
  // src/client/modules/compute/app/index.ts
287
- var import_viem6 = require("viem");
294
+ var import_viem7 = require("viem");
288
295
 
289
296
  // src/client/common/docker/build.ts
290
297
  var child_process = __toESM(require("child_process"), 1);
@@ -292,7 +299,7 @@ var import_util = require("util");
292
299
 
293
300
  // src/client/common/constants.ts
294
301
  var import_chains = require("viem/chains");
295
- var SUPPORTED_CHAINS = [import_chains.mainnet, import_chains.sepolia];
302
+ var SUPPORTED_CHAINS = [import_chains.mainnet, import_chains.sepolia, import_chains.baseSepolia];
296
303
  var DOCKER_PLATFORM = "linux/amd64";
297
304
  var REGISTRY_PROPAGATION_WAIT_SECONDS = 3;
298
305
  var LAYERED_DOCKERFILE_NAME = "Dockerfile.eigencompute";
@@ -300,7 +307,6 @@ var ENV_SOURCE_SCRIPT_NAME = "compute-source-env.sh";
300
307
  var KMS_CLIENT_BINARY_NAME = "kms-client";
301
308
  var KMS_SIGNING_KEY_NAME = "kms-signing-public-key.pem";
302
309
  var TLS_KEYGEN_BINARY_NAME = "tls-keygen";
303
- var DRAIN_WATCHER_BINARY_NAME = "ecloud-drain-watcher";
304
310
  var CADDYFILE_NAME = "Caddyfile";
305
311
  var LAYERED_BUILD_DIR_PREFIX = "ecloud-layered-build";
306
312
 
@@ -587,12 +593,14 @@ USER root
587
593
  {{/if}}
588
594
 
589
595
  # Copy core TEE components
596
+ # CA bundle for kms-client / tls-keygen to validate HTTPS calls to
597
+ # eigencloud.xyz endpoints. Bundled at a non-standard path and consumed
598
+ # only via SSL_CERT_FILE in compute-source-env.sh, so the user's
599
+ # /etc/ssl/ is never touched.
600
+ COPY --from=alpine:3.20.10 /etc/ssl/certs/ca-certificates.crt /usr/local/share/eigenx-ca-certs.crt
590
601
  COPY compute-source-env.sh /usr/local/bin/
591
602
  COPY kms-client /usr/local/bin/
592
603
  COPY kms-signing-public-key.pem /usr/local/bin/
593
- {{#if includeDrainWatcher}}
594
- COPY ecloud-drain-watcher /usr/local/bin/
595
- {{/if}}
596
604
 
597
605
  {{#if includeTLS}}
598
606
  # Copy Caddy from official image
@@ -606,8 +614,7 @@ COPY Caddyfile /etc/caddy/
606
614
  {{#if originalUser}}
607
615
  # Make binaries executable (755 for executables, 644 for keys)
608
616
  RUN chmod 755 /usr/local/bin/compute-source-env.sh \\
609
- && chmod 755 /usr/local/bin/kms-client{{#if includeDrainWatcher}} \\
610
- && chmod 755 /usr/local/bin/ecloud-drain-watcher{{/if}}{{#if includeTLS}} \\
617
+ && chmod 755 /usr/local/bin/kms-client{{#if includeTLS}} \\
611
618
  && chmod 755 /usr/local/bin/tls-keygen \\
612
619
  && chmod 755 /usr/local/bin/caddy{{/if}} \\
613
620
  && chmod 644 /usr/local/bin/kms-signing-public-key.pem
@@ -617,8 +624,7 @@ ENV __ECLOUD_ORIGINAL_USER={{originalUser}}
617
624
  {{else}}
618
625
  # Make binaries executable (preserve existing permissions, just add execute)
619
626
  RUN chmod +x /usr/local/bin/compute-source-env.sh \\
620
- && chmod +x /usr/local/bin/kms-client{{#if includeDrainWatcher}} \\
621
- && chmod +x /usr/local/bin/ecloud-drain-watcher{{/if}}{{#if includeTLS}} \\
627
+ && chmod +x /usr/local/bin/kms-client{{#if includeTLS}} \\
622
628
  && chmod +x /usr/local/bin/tls-keygen{{/if}}
623
629
  {{/if}}
624
630
 
@@ -631,36 +637,8 @@ LABEL tee.launch_policy.log_redirect={{logRedirect}}
631
637
  LABEL tee.launch_policy.monitoring_memory_allow={{resourceUsageAllow}}
632
638
  {{/if}}
633
639
 
634
- # Allow-list the envvars the ecloud-platform sets via GCE \`tee-env-*\`
635
- # metadata. Without this label, Confidential Space's launcher rejects
636
- # any \`tee-env-*\` override at container-start with
637
- # "env var {...} is not allowed to be overridden on this image" and
638
- # exits with code 1 \u2014 which terminates the VM before the entrypoint
639
- # ever runs. User-supplied env vars flow through KMS (not tee-env-*)
640
- # and don't need to be listed here.
641
- #
642
- # Entries:
643
- # - ECLOUD_PD_EXPECTED set on PD-backed apps so the entrypoint
644
- # (compute-source-env.sh) knows to wait for
645
- # the persistent disk before exec'ing the
646
- # user workload.
647
- # - ECLOUD_PLATFORM_HOST the platform-routed hostname
648
- # (<addr>.<env>.eigencloud.xyz) so the
649
- # entrypoint's setup_tls can issue an ACME
650
- # cert for it. Injected by the CLI into
651
- # publicEnv at deploy/upgrade time and
652
- # propagated by ecloud-platform's
653
- # compute.go as a tee-env-* metadata key.
654
- #
655
- # The CS launcher parses this label as a comma-separated list
656
- # (go-tpm-tools/launcher/spec/launch_policy.go:185 \u2014 strings.Split
657
- # on ","). Quotes are not required; keep the value bare for
658
- # consistency with compute-tee's and eigenx-kms's existing labels.
659
- LABEL tee.launch_policy.allow_env_override=ECLOUD_PD_EXPECTED,ECLOUD_PLATFORM_HOST
660
-
661
640
  LABEL eigenx_cli_version={{ecloudCLIVersion}}
662
641
  LABEL eigenx_vm_image=eigen
663
- LABEL eigenx_container_contract=v1
664
642
 
665
643
  {{#if includeTLS}}
666
644
  # Expose both HTTP and HTTPS ports for Caddy
@@ -682,54 +660,11 @@ var import_handlebars2 = __toESM(require("handlebars"), 1);
682
660
 
683
661
  // src/client/common/templates/compute-source-env.sh.tmpl
684
662
  var compute_source_env_sh_default = `#!/bin/sh
685
- # EigenCompute container entrypoint script
686
- # This script handles KMS secret fetching, TLS setup, and privilege dropping
687
- # before executing the user's application.
688
- #
689
- # Handlebars template variables (replaced at build time by the CLI):
690
- # kmsServerURL - URL of the KMS server
691
- # userAPIURL - URL of the user API (ecloud-platform)
692
- # The KMS signing public key is copied into the image as
693
- # /usr/local/bin/kms-signing-public-key.pem at layer-build time by the CLI.
694
- #
695
- # ecloud-platform divergence from compute-tee:
696
- # This script emits ECLOUD_READY / ECLOUD_FAIL / ECLOUD_AWAITING_USERDATA /
697
- # ECLOUD_DETACHED markers to stdout at key lifecycle points. The GCP
698
- # provisioner's serial-console watcher in ecloud-platform
699
- # (pkg/services/infraService/providers/gcp/compute.go) parses those
700
- # markers to gate "VM ready" and to coordinate the prewarm-detach
701
- # upgrade flow. Without the markers, the platform's waitForStartupReady
702
- # times out at ~10 minutes per deploy, rollback fires, and the VM is
703
- # deleted \u2014 seen in dev on 2026-05-04 with an older copy of this
704
- # template that lacked the markers.
705
- #
706
- # Prewarm-detach contract:
707
- # - If ECLOUD_PD_EXPECTED=1 and /mnt/disks/userdata is not present at boot,
708
- # emit ECLOUD_AWAITING_USERDATA and wait until the disk is attached.
709
- # - On SIGTERM (drain-requested), forward to child, wait for exit, sync
710
- # + unmount /mnt/disks/userdata, emit ECLOUD_DETACHED, exit.
711
- # - ECLOUD_READY is emitted once runtime is bootstrapped (same as before).
712
- # - ECLOUD_FAIL is emitted on any unrecoverable setup error.
713
- # Keep the markers on any line that resolves a lifecycle outcome.
714
- #
715
- # This file is kept in lockstep with
716
- # ecloud-platform/pkg/services/buildService/assets/compute-source-env.sh.tmpl
717
- # \u2014 if you change one, change the other. Differences vs the platform copy
718
- # are intentionally minimal:
719
- # - Handlebars placeholders use the CLI's naming (kmsServerURL,
720
- # userAPIURL) rather than the platform's (KMS_SERVER_URL,
721
- # USER_API_URL). (See top of file for real placeholder syntax \u2014
722
- # not repeated here so Handlebars doesn't expand it in this comment.)
723
- # - KMS signing key is read from a file the CLI copies into the image,
724
- # not heredoc-embedded in the script, because the CLI's image
725
- # layering writes it as a separate file (kms-signing-public-key.pem).
726
- # - TLS binary is \`tls-keygen\` (CLI-bundled) not \`tls-client\`.
727
-
728
663
  echo "compute-source-env.sh: Running setup script..."
729
664
 
730
665
  # Fetch and source environment variables from KMS
731
666
  echo "Fetching secrets from KMS..."
732
- if /usr/local/bin/kms-client \\
667
+ if SSL_CERT_FILE=/usr/local/share/eigenx-ca-certs.crt /usr/local/bin/kms-client \\
733
668
  --kms-server-url "{{kmsServerURL}}" \\
734
669
  --kms-signing-key-file /usr/local/bin/kms-signing-public-key.pem \\
735
670
  --userapi-url "{{userAPIURL}}" \\
@@ -740,187 +675,93 @@ if /usr/local/bin/kms-client \\
740
675
  else
741
676
  echo "compute-source-env.sh: ERROR - Failed to fetch environment variables from KMS"
742
677
  echo "compute-source-env.sh: Exiting - cannot start user workload without KMS secrets"
743
- echo "ECLOUD_FAIL kms_bootstrap"
744
678
  exit 1
745
679
  fi
746
680
 
747
- # issue_cert_for runs tls-keygen for a single hostname and copies the
748
- # produced fullchain/privkey into $1's output directory ($2). Returns
749
- # 0 on success, non-zero on any failure (caller decides whether that's
750
- # fatal). tls-keygen writes to /run/tls/{fullchain,privkey}.pem by
751
- # default; we move those to a per-host subdirectory so the two-site
752
- # Caddyfile can select the right cert by path.
753
- issue_cert_for() {
754
- local host="$1"
755
- local cert_dir="$2"
756
- local mnemonic="$3"
757
- local challenge="$4"
758
- local staging_flag="$5"
759
-
760
- echo "compute-source-env.sh: Obtaining TLS certificate for $host (challenge=$challenge)..."
761
- # Remove any stale default outputs from a prior tls-keygen run
762
- rm -f /run/tls/fullchain.pem /run/tls/privkey.pem
763
- if ! MNEMONIC="$mnemonic" DOMAIN="$host" API_URL="{{userAPIURL}}" /usr/local/bin/tls-keygen \\
764
- -challenge "$challenge" \\
765
- $staging_flag; then
766
- return 1
767
- fi
768
- mkdir -p "$cert_dir"
769
- mv /run/tls/fullchain.pem "$cert_dir/fullchain.pem"
770
- mv /run/tls/privkey.pem "$cert_dir/privkey.pem"
771
- echo "compute-source-env.sh: Cert for $host written to $cert_dir"
772
- }
773
-
774
- # Setup TLS: issues certs for
775
- # - ECLOUD_PLATFORM_HOST (platform-routed <addr>.<env>.eigencloud.xyz),
776
- # when the CLI/platform has set it
777
- # - DOMAIN (user-supplied custom domain), when set and non-localhost
778
- #
779
- # No client-side DNS precheck. Earlier versions tried to gate ACME on
780
- # "does this hostname resolve to my external IP" but that's wrong for
781
- # the platform-routing model (DNS points at the shared nginx NLB, not
782
- # the VM) and was preventing cert issuance on the production path.
783
- # tls-client (eigencompute-containers/tls-client) does its own DNS
784
- # poll before calling ACME and surfaces a clear error when challenges
785
- # can't reach the VM, which is the right place for that check \u2014
786
- # attempting it here from inside the VM cannot tell platform-routed
787
- # from compute-tee-routed apps.
681
+ # Setup TLS if tls-keygen is present (which means TLS was configured at build time)
788
682
  setup_tls() {
789
683
  # If tls-keygen isn't present, TLS wasn't configured during build
790
684
  if [ ! -x /usr/local/bin/tls-keygen ]; then
791
685
  echo "compute-source-env.sh: TLS not configured (no tls-keygen binary)"
792
686
  return 0
793
687
  fi
794
-
795
- local platform_host="\${ECLOUD_PLATFORM_HOST:-}"
796
- local user_domain="\${DOMAIN:-}"
688
+
689
+ local domain="\${DOMAIN:-}"
797
690
  local mnemonic="\${MNEMONIC:-}"
798
-
799
- # Normalize "localhost" DOMAIN to unset \u2014 compute-tee apps often
800
- # set DOMAIN=localhost as a sentinel for "no custom domain".
801
- if [ "$user_domain" = "localhost" ]; then
802
- user_domain=""
803
- fi
804
-
805
- # If the user set DOMAIN to the same value as the platform
806
- # hostname, collapse them. Without this we'd burn a second ACME
807
- # issuance for the exact same name (each VM boot counts against
808
- # Let's Encrypt's 5-certs-per-domain-per-week limit), and Caddy
809
- # would then refuse to start because two site blocks bound to
810
- # the same address is a config error. Also unset DOMAIN so the
811
- # DOMAIN site block in the Caddyfile falls back to its
812
- # "localhost.user.invalid" placeholder and stays dormant.
813
- if [ -n "$platform_host" ] && [ "$user_domain" = "$platform_host" ]; then
814
- echo "compute-source-env.sh: DOMAIN matches ECLOUD_PLATFORM_HOST; skipping duplicate cert"
815
- user_domain=""
816
- unset DOMAIN
817
- fi
818
-
819
- if [ -z "$platform_host" ] && [ -z "$user_domain" ]; then
820
- echo "compute-source-env.sh: TLS skipped (neither ECLOUD_PLATFORM_HOST nor DOMAIN set)"
821
- return 0
691
+
692
+ # Since tls-keygen is present, TLS is expected - validate requirements
693
+ if [ -z "$domain" ] || [ "$domain" = "localhost" ]; then
694
+ echo "compute-source-env.sh: ERROR - TLS binary present but DOMAIN not configured or is localhost"
695
+ echo "compute-source-env.sh: Set DOMAIN environment variable to a valid domain"
696
+ exit 1
822
697
  fi
823
-
698
+
824
699
  if [ -z "$mnemonic" ]; then
825
- echo "compute-source-env.sh: ERROR - TLS requested but MNEMONIC not available"
826
- echo "ECLOUD_FAIL tls_mnemonic_missing"
700
+ echo "compute-source-env.sh: ERROR - TLS binary present but MNEMONIC not available"
701
+ echo "compute-source-env.sh: Cannot obtain TLS certificate without mnemonic"
827
702
  exit 1
828
703
  fi
829
-
704
+
830
705
  if [ ! -x /usr/local/bin/caddy ]; then
831
- echo "compute-source-env.sh: ERROR - TLS requested but Caddy not found"
832
- echo "ECLOUD_FAIL tls_caddy_missing"
706
+ echo "compute-source-env.sh: ERROR - TLS binary present but Caddy not found"
833
707
  exit 1
834
708
  fi
835
-
709
+
710
+ echo "compute-source-env.sh: Setting up TLS for domain: $domain"
711
+
712
+ # Obtain TLS certificate using ACME
713
+ # Default to http-01, but allow override via ACME_CHALLENGE env var
836
714
  local challenge="\${ACME_CHALLENGE:-http-01}"
715
+
716
+ # Check if we should use staging (for testing)
837
717
  local staging_flag=""
838
718
  if [ "\${ACME_STAGING:-false}" = "true" ]; then
839
719
  staging_flag="-staging"
840
- echo "compute-source-env.sh: Using Let's Encrypt STAGING environment"
841
- fi
842
-
843
- local certs_issued=0
844
-
845
- if [ -n "$platform_host" ]; then
846
- if issue_cert_for "$platform_host" "/run/tls/platform" "$mnemonic" "$challenge" "$staging_flag"; then
847
- certs_issued=$((certs_issued + 1))
848
- else
849
- echo "compute-source-env.sh: ERROR - failed to issue cert for platform host $platform_host"
850
- echo "ECLOUD_FAIL tls_setup"
851
- exit 1
852
- fi
853
- fi
854
-
855
- if [ -n "$user_domain" ]; then
856
- if issue_cert_for "$user_domain" "/run/tls/domain" "$mnemonic" "$challenge" "$staging_flag"; then
857
- certs_issued=$((certs_issued + 1))
858
- else
859
- echo "compute-source-env.sh: ERROR - failed to issue cert for user domain $user_domain"
860
- echo "ECLOUD_FAIL tls_setup"
861
- exit 1
862
- fi
720
+ echo "compute-source-env.sh: Using Let's Encrypt STAGING environment (certificates won't be trusted)"
863
721
  fi
864
-
865
- if [ "$certs_issued" -eq 0 ]; then
866
- echo "compute-source-env.sh: no certs issued, skipping Caddy (app will serve plaintext until DNS is wired)"
867
- return 0
722
+
723
+ echo "compute-source-env.sh: Obtaining TLS certificate using $challenge challenge..."
724
+ # Pass the API URL for certificate persistence
725
+ if ! SSL_CERT_FILE=/usr/local/share/eigenx-ca-certs.crt \\
726
+ MNEMONIC="$mnemonic" DOMAIN="$domain" API_URL="{{userAPIURL}}" /usr/local/bin/tls-keygen \\
727
+ -challenge "$challenge" \\
728
+ $staging_flag; then
729
+ echo "compute-source-env.sh: ERROR - Failed to obtain TLS certificate"
730
+ echo "compute-source-env.sh: Certificate issuance failed for $domain"
731
+ exit 1
868
732
  fi
869
-
870
- # Caddy's validate step checks that every \`tls <cert> <key>\` file
871
- # exists, even on site blocks bound to dormant placeholder
872
- # hostnames. The default Caddyfile declares both a platform site
873
- # and a user-domain site; when only one is configured, the other
874
- # block's cert paths are never populated and validate fails with
875
- # "Invalid Caddyfile". Point the unused block at the issued
876
- # block's cert files so validate passes \u2014 the dormant block can't
877
- # receive real traffic (its hostname falls back to
878
- # localhost.{platform,user}.invalid, which Caddy routes by SNI and
879
- # never matches public traffic), so the symlink is never actually
880
- # presented. Skipped when a user-supplied Caddyfile is in use,
881
- # since we don't know what cert paths it references.
882
- if [ -d /run/tls/platform ] && [ ! -e /run/tls/domain/fullchain.pem ]; then
883
- mkdir -p /run/tls/domain
884
- ln -sf /run/tls/platform/fullchain.pem /run/tls/domain/fullchain.pem
885
- ln -sf /run/tls/platform/privkey.pem /run/tls/domain/privkey.pem
886
- elif [ -d /run/tls/domain ] && [ ! -e /run/tls/platform/fullchain.pem ]; then
887
- mkdir -p /run/tls/platform
888
- ln -sf /run/tls/domain/fullchain.pem /run/tls/platform/fullchain.pem
889
- ln -sf /run/tls/domain/privkey.pem /run/tls/platform/privkey.pem
733
+
734
+ echo "compute-source-env.sh: TLS certificate obtained successfully"
735
+
736
+ # Validate Caddyfile before starting
737
+ if ! /usr/local/bin/caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile 2>/dev/null; then
738
+ echo "compute-source-env.sh: ERROR - Invalid Caddyfile"
739
+ echo "compute-source-env.sh: TLS was requested (DOMAIN=$domain) but setup failed"
740
+ exit 1
890
741
  fi
891
-
892
- # Validate Caddyfile before starting. Don't redirect stderr \u2014 when
893
- # validate fails, Caddy's diagnostic is the only signal that lands
894
- # in ReadinessError.SerialTail, so silencing it leaves operators
895
- # staring at a bare "tls_invalid_caddyfile" with no detail.
896
- if [ -f /etc/caddy/Caddyfile ]; then
897
- if ! /usr/local/bin/caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile; then
898
- echo "compute-source-env.sh: ERROR - Invalid Caddyfile"
899
- echo "ECLOUD_FAIL tls_invalid_caddyfile"
742
+
743
+ # Start Caddy in background
744
+ echo "compute-source-env.sh: Starting Caddy reverse proxy..."
745
+
746
+ # Check if Caddy logs should be enabled
747
+ if [ "\${ENABLE_CADDY_LOGS:-false}" = "true" ]; then
748
+ if ! /usr/local/bin/caddy start --config /etc/caddy/Caddyfile --adapter caddyfile 2>&1; then
749
+ echo "compute-source-env.sh: ERROR - Failed to start Caddy"
750
+ echo "compute-source-env.sh: TLS was requested (DOMAIN=$domain) but setup failed"
900
751
  exit 1
901
752
  fi
902
-
903
- echo "compute-source-env.sh: Starting Caddy reverse proxy..."
904
- if [ "\${ENABLE_CADDY_LOGS:-false}" = "true" ]; then
905
- if ! /usr/local/bin/caddy start --config /etc/caddy/Caddyfile --adapter caddyfile 2>&1; then
906
- echo "compute-source-env.sh: ERROR - Failed to start Caddy"
907
- echo "ECLOUD_FAIL tls_caddy_start"
908
- exit 1
909
- fi
910
- else
911
- if ! /usr/local/bin/caddy start --config /etc/caddy/Caddyfile --adapter caddyfile >/dev/null 2>&1; then
912
- echo "compute-source-env.sh: ERROR - Failed to start Caddy"
913
- echo "ECLOUD_FAIL tls_caddy_start"
914
- exit 1
915
- fi
916
- fi
917
-
918
- sleep 2
919
- echo "compute-source-env.sh: Caddy started successfully"
920
753
  else
921
- echo "compute-source-env.sh: No Caddyfile found, skipping Caddy"
754
+ # Redirect Caddy output to /dev/null to silence logs
755
+ if ! /usr/local/bin/caddy start --config /etc/caddy/Caddyfile --adapter caddyfile >/dev/null 2>&1; then
756
+ echo "compute-source-env.sh: ERROR - Failed to start Caddy"
757
+ echo "compute-source-env.sh: TLS was requested (DOMAIN=$domain) but setup failed"
758
+ exit 1
759
+ fi
922
760
  fi
923
-
761
+
762
+ # Give Caddy a moment to fully initialize
763
+ sleep 2
764
+ echo "compute-source-env.sh: Caddy started successfully"
924
765
  return 0
925
766
  }
926
767
 
@@ -931,233 +772,15 @@ setup_tls
931
772
  export KMS_SERVER_URL="{{kmsServerURL}}"
932
773
  export KMS_PUBLIC_KEY="$(cat /usr/local/bin/kms-signing-public-key.pem)"
933
774
 
934
- # \u2500\u2500 Prewarm-detach: wait for PD if expected \u2500\u2500
935
- # Orchestrator sets ECLOUD_PD_EXPECTED=1 on apps using StorageBackend=pd.
936
- # When the prewarm path is used, the new VM boots WITHOUT the disk; we
937
- # signal awaiting-userdata and poll until the disk is attached.
938
- USERDATA_MOUNT="/mnt/disks/userdata"
939
- USERDATA_DEV="/dev/disk/by-id/google-persistent_storage_1"
940
-
941
- wait_for_userdata() {
942
- if [ "\${ECLOUD_PD_EXPECTED:-0}" != "1" ]; then
943
- return 0
944
- fi
945
- if mountpoint -q "$USERDATA_MOUNT" 2>/dev/null; then
946
- echo "compute-source-env.sh: userdata already mounted at $USERDATA_MOUNT"
947
- return 0
948
- fi
949
- # Refuse to proceed if the tools we need for safe first-attach
950
- # detection are missing. Without blkid we cannot tell an empty new
951
- # disk from an already-formatted one \u2014 running mkfs.ext4 on the
952
- # latter would destroy data.
953
- if ! command -v blkid >/dev/null 2>&1; then
954
- echo "ECLOUD_FAIL pd_tools_missing"
955
- exit 1
956
- fi
957
- echo "ECLOUD_AWAITING_USERDATA"
958
- echo "compute-source-env.sh: waiting for PD at $USERDATA_DEV..."
959
- # Poll for up to 10 minutes (120 * 5s). The orchestrator's overall
960
- # attach timeout is shorter; the ceiling here just bounds the wait
961
- # for manual / diagnostic scenarios.
962
- local i=0
963
- local mount_failures=0
964
- while [ "$i" -lt 120 ]; do
965
- if [ -e "$USERDATA_DEV" ]; then
966
- mkdir -p "$USERDATA_MOUNT"
967
- if mount -o noatime "$USERDATA_DEV" "$USERDATA_MOUNT" 2>/dev/null; then
968
- echo "compute-source-env.sh: PD mounted at $USERDATA_MOUNT"
969
- return 0
970
- fi
971
- # Disk present but mount failed. Check whether it has a
972
- # recognized filesystem. \`blkid -s TYPE -o value\` prints the
973
- # FS type (empty if none). We only mkfs when there is
974
- # demonstrably NO filesystem \u2014 never on the basis of blkid
975
- # returning non-zero alone, which could mean "blkid missing"
976
- # or "device busy".
977
- local fstype
978
- fstype=$(blkid -s TYPE -o value "$USERDATA_DEV" 2>/dev/null)
979
- if [ -z "$fstype" ]; then
980
- echo "compute-source-env.sh: formatting $USERDATA_DEV (first attach)"
981
- mkfs.ext4 -F -L eclouddata "$USERDATA_DEV" >/dev/null 2>&1 || {
982
- echo "ECLOUD_FAIL pd_mkfs_failed"
983
- exit 1
984
- }
985
- mount -o noatime "$USERDATA_DEV" "$USERDATA_MOUNT" || {
986
- echo "ECLOUD_FAIL pd_mount_after_format_failed"
987
- exit 1
988
- }
989
- return 0
990
- fi
991
- # Disk has a filesystem but mount still failed. Give it a
992
- # few retries to cover transient cases (device busy, udev
993
- # still settling), but don't pretend this is an attach
994
- # timeout if it persists.
995
- mount_failures=$((mount_failures + 1))
996
- if [ "$mount_failures" -ge 6 ]; then
997
- echo "ECLOUD_FAIL pd_mount_failed"
998
- exit 1
999
- fi
1000
- else
1001
- # Device disappeared (e.g. udev re-enumeration between
1002
- # attach and mount). Reset the consecutive-failure counter
1003
- # so only true back-to-back mount failures trip
1004
- # pd_mount_failed; a device blip should not steal retries.
1005
- mount_failures=0
1006
- fi
1007
- i=$((i + 1))
1008
- sleep 5
1009
- done
1010
- echo "ECLOUD_FAIL pd_attach_timeout"
1011
- exit 1
1012
- }
1013
-
1014
- wait_for_userdata
1015
-
1016
- # \u2500\u2500 Prewarm-detach: install SIGTERM handler for graceful drain \u2500\u2500
1017
- # Orchestrator signals drain by setting the instance metadata key
1018
- # ECLOUD_DRAIN_REQUESTED=1, which a host-level agent translates into
1019
- # SIGTERM on PID 1. On SIGTERM we:
1020
- # 1. Forward to the child (wakes the user's app for graceful exit)
1021
- # 2. Wait for child exit
1022
- # 3. Sync + unmount the PD
1023
- # 4. Emit ECLOUD_DETACHED so the orchestrator can proceed to detach
1024
- CHILD_PID=""
1025
- _DRAIN_IN_PROGRESS=0
1026
-
1027
- drain_handler() {
1028
- # Guard against re-entry if SIGTERM arrives twice (e.g. both the
1029
- # drain_watcher and an external signal fire in quick succession).
1030
- if [ "$_DRAIN_IN_PROGRESS" = "1" ]; then
1031
- return 0
1032
- fi
1033
- _DRAIN_IN_PROGRESS=1
1034
- echo "compute-source-env.sh: received drain signal, forwarding to child pgid=$CHILD_PID"
1035
- if [ -n "$CHILD_PID" ]; then
1036
- # Send to the process group so intermediate wrappers (su, sh -c,
1037
- # etc.) don't swallow the signal. The leading \`-\` targets the
1038
- # pgid, which equals the direct child's pid for a shell-backgrounded
1039
- # process. Fall back to the pid alone if pgid signaling fails
1040
- # (e.g. kernel older than 3.9 or PID namespace edge cases).
1041
- kill -TERM -"$CHILD_PID" 2>/dev/null || kill -TERM "$CHILD_PID" 2>/dev/null || true
1042
- # Give the app up to 30s to exit cleanly.
1043
- local i=0
1044
- while [ "$i" -lt 30 ] && kill -0 "$CHILD_PID" 2>/dev/null; do
1045
- i=$((i + 1))
1046
- sleep 1
1047
- done
1048
- if kill -0 "$CHILD_PID" 2>/dev/null; then
1049
- echo "compute-source-env.sh: child did not exit in 30s, sending SIGKILL"
1050
- kill -KILL -"$CHILD_PID" 2>/dev/null || kill -KILL "$CHILD_PID" 2>/dev/null || true
1051
- # Reap the process so its in-flight I/O is flushed to the
1052
- # filesystem before we sync + unmount. SIGKILL schedules
1053
- # death; wait guarantees it's complete.
1054
- wait "$CHILD_PID" 2>/dev/null || true
1055
- fi
1056
- fi
1057
- if [ "\${ECLOUD_PD_EXPECTED:-0}" = "1" ] && mountpoint -q "$USERDATA_MOUNT" 2>/dev/null; then
1058
- sync
1059
- if umount "$USERDATA_MOUNT" 2>/dev/null; then
1060
- echo "compute-source-env.sh: unmounted $USERDATA_MOUNT cleanly"
1061
- else
1062
- # Force lazy unmount as last resort \u2014 orchestrator still needs
1063
- # the DETACHED signal to proceed.
1064
- umount -l "$USERDATA_MOUNT" 2>/dev/null || true
1065
- echo "compute-source-env.sh: WARNING - used lazy unmount on $USERDATA_MOUNT"
1066
- fi
1067
- # ECLOUD_DETACHED is strictly a PD-lifecycle signal. Only emit
1068
- # it when we actually had a PD mount in play, so serial-log
1069
- # parsers and alerting for non-PD apps don't see spurious
1070
- # lifecycle markers on routine container SIGTERM.
1071
- echo "ECLOUD_DETACHED"
1072
- fi
1073
- # Always exit 0: drain is a managed shutdown and the orchestrator
1074
- # waits on ECLOUD_DETACHED, not the container exit code. Forwarding
1075
- # the child's exit status here would make a crash-during-drain look
1076
- # like a drain failure to whatever reads the container exit code.
1077
- exit 0
1078
- }
1079
- trap drain_handler TERM
1080
-
1081
- # \u2500\u2500 Prewarm-detach: background drain watcher \u2500\u2500
1082
- # Container metadata delivery in Confidential Space is limited, so we
1083
- # poll the instance metadata server for ECLOUD_DRAIN_REQUESTED and
1084
- # raise SIGTERM on ourselves when it flips to "1".
1085
- #
1086
- # Try wget first (present in most Alpine bases), fall back to curl.
1087
- # If neither is present, drain watcher is disabled \u2014 the orchestrator
1088
- # will hit its drain timeout and fail the upgrade explicitly, which is
1089
- # the correct behavior (we cannot silently ignore a drain request).
1090
- _fetch_drain_flag() {
1091
- local url="http://metadata.google.internal/computeMetadata/v1/instance/attributes/ECLOUD_DRAIN_REQUESTED"
1092
- if command -v wget >/dev/null 2>&1; then
1093
- wget -q --tries=1 --timeout=2 --header='Metadata-Flavor: Google' -O - "$url" 2>/dev/null
1094
- elif command -v curl >/dev/null 2>&1; then
1095
- curl -sf --max-time 2 -H 'Metadata-Flavor: Google' "$url" 2>/dev/null
1096
- else
1097
- return 2
1098
- fi
1099
- }
1100
-
1101
- drain_watcher() {
1102
- # Preflight: confirm we have an HTTP client
1103
- if ! _fetch_drain_flag >/dev/null 2>&1; then
1104
- # Either no http client available OR metadata server not
1105
- # responding yet. If no client, give up and log; otherwise the
1106
- # loop below will retry.
1107
- if ! command -v wget >/dev/null 2>&1 && ! command -v curl >/dev/null 2>&1; then
1108
- echo "compute-source-env.sh: WARNING - no wget/curl; drain_watcher disabled"
1109
- return 0
1110
- fi
1111
- fi
1112
- while true; do
1113
- local v
1114
- v=$(_fetch_drain_flag || true)
1115
- if [ "$v" = "1" ]; then
1116
- echo "compute-source-env.sh: drain_watcher saw ECLOUD_DRAIN_REQUESTED=1, signaling PID 1"
1117
- # The CS launcher runs this script directly as PID 1, so
1118
- # kill -TERM 1 delivers SIGTERM to the shell that installed
1119
- # the drain_handler trap. If the launch mechanism ever
1120
- # wraps this script in another process, this assumption
1121
- # breaks and drain will silently no-op \u2014 audit here.
1122
- kill -TERM 1 2>/dev/null || true
1123
- return 0
1124
- fi
1125
- sleep 2
1126
- done
1127
- }
1128
-
1129
- if [ "\${ECLOUD_PD_EXPECTED:-0}" = "1" ]; then
1130
- # Assumption: the orchestrator only flips ECLOUD_DRAIN_REQUESTED=1
1131
- # after observing ECLOUD_AWAITING_USERDATA (old VM) or
1132
- # ECLOUD_READY (new VM), so CHILD_PID is always set by the time
1133
- # drain_handler fires. If drain somehow arrived in the tiny window
1134
- # between this watcher spawn and CHILD_PID assignment below,
1135
- # drain_handler would skip the child-kill branch and still emit
1136
- # ECLOUD_DETACHED \u2014 harmless because there's nothing to drain yet.
1137
- if [ -x /usr/local/bin/ecloud-drain-watcher ]; then
1138
- /usr/local/bin/ecloud-drain-watcher &
1139
- else
1140
- drain_watcher &
1141
- fi
1142
- fi
1143
-
1144
775
  echo "compute-source-env.sh: Environment sourced."
1145
- echo "ECLOUD_READY runtime_bootstrapped"
1146
776
 
1147
777
  # Drop privileges to original user for the application command
1148
778
  if [ -n "$__ECLOUD_ORIGINAL_USER" ] && [ "$(id -u)" = "0" ]; then
1149
779
  echo "compute-source-env.sh: Dropping privileges to user: $__ECLOUD_ORIGINAL_USER"
1150
- # Must background the child so our trap can fire; exec replaces PID 1.
1151
- su -s /bin/sh "$__ECLOUD_ORIGINAL_USER" -c 'exec "$@"' -- sh "$@" &
1152
- CHILD_PID=$!
1153
- wait "$CHILD_PID"
1154
- exit $?
780
+ exec su -s /bin/sh "$__ECLOUD_ORIGINAL_USER" -c 'exec "$@"' -- sh "$@"
1155
781
  fi
1156
782
 
1157
- "$@" &
1158
- CHILD_PID=$!
1159
- wait "$CHILD_PID"
1160
- exit $?
783
+ exec "$@"
1161
784
  `;
1162
785
 
1163
786
  // src/client/common/templates/scriptTemplate.ts
@@ -1166,14 +789,6 @@ function processScriptTemplate(data) {
1166
789
  return template(data);
1167
790
  }
1168
791
 
1169
- // src/client/common/templates/Caddyfile.default.tmpl
1170
- 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';
1171
-
1172
- // src/client/common/templates/caddyfileTemplate.ts
1173
- function getDefaultCaddyfile() {
1174
- return Caddyfile_default_default;
1175
- }
1176
-
1177
792
  // keys/mainnet-alpha/prod/kms-encryption-public-key.pem
1178
793
  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-----";
1179
794
 
@@ -1349,9 +964,15 @@ async function layerLocalImage(options, logger) {
1349
964
  const imageConfig = await extractImageConfig(docker, sourceImageRef);
1350
965
  const originalCmd = imageConfig.cmd.length > 0 ? imageConfig.cmd : imageConfig.entrypoint;
1351
966
  const originalUser = imageConfig.user;
1352
- const includeTLS = true;
1353
- const drainWatcherSource = findBinary("ecloud-drain-watcher-linux-amd64");
1354
- const includeDrainWatcher = fs.existsSync(drainWatcherSource);
967
+ let includeTLS = false;
968
+ if (envFilePath && fs.existsSync(envFilePath)) {
969
+ const envContent = fs.readFileSync(envFilePath, "utf-8");
970
+ const domainMatch = envContent.match(/^DOMAIN=(.+)$/m);
971
+ if (domainMatch && domainMatch[1] && domainMatch[1] !== "localhost") {
972
+ includeTLS = true;
973
+ logger.debug(`Found DOMAIN=${domainMatch[1]} in ${envFilePath}, including TLS components`);
974
+ }
975
+ }
1355
976
  const layeredDockerfileContent = processDockerfileTemplate({
1356
977
  baseImage: sourceImageRef,
1357
978
  originalCmd: JSON.stringify(originalCmd),
@@ -1359,9 +980,8 @@ async function layerLocalImage(options, logger) {
1359
980
  logRedirect,
1360
981
  resourceUsageAllow,
1361
982
  includeTLS,
1362
- ecloudCLIVersion: "0.1.0",
983
+ ecloudCLIVersion: "0.1.0"
1363
984
  // TODO: Get from package.json
1364
- includeDrainWatcher
1365
985
  });
1366
986
  const scriptContent = processScriptTemplate({
1367
987
  kmsServerURL: environmentConfig.kmsServerURL,
@@ -1371,8 +991,7 @@ async function layerLocalImage(options, logger) {
1371
991
  environmentConfig,
1372
992
  layeredDockerfileContent,
1373
993
  scriptContent,
1374
- includeTLS,
1375
- includeDrainWatcher ? drainWatcherSource : void 0
994
+ includeTLS
1376
995
  // logger
1377
996
  );
1378
997
  try {
@@ -1387,7 +1006,7 @@ async function layerLocalImage(options, logger) {
1387
1006
  fs.rmSync(tempDir, { recursive: true, force: true });
1388
1007
  }
1389
1008
  }
1390
- async function setupLayeredBuildDirectory(environmentConfig, layeredDockerfileContent, scriptContent, includeTLS, drainWatcherSource) {
1009
+ async function setupLayeredBuildDirectory(environmentConfig, layeredDockerfileContent, scriptContent, includeTLS) {
1391
1010
  const tempDir = fs.mkdtempSync(path2.join(os.tmpdir(), LAYERED_BUILD_DIR_PREFIX));
1392
1011
  try {
1393
1012
  const layeredDockerfilePath = path2.join(tempDir, LAYERED_DOCKERFILE_NAME);
@@ -1411,11 +1030,6 @@ async function setupLayeredBuildDirectory(environmentConfig, layeredDockerfileCo
1411
1030
  }
1412
1031
  fs.copyFileSync(kmsClientSource, kmsClientPath);
1413
1032
  fs.chmodSync(kmsClientPath, 493);
1414
- if (drainWatcherSource && fs.existsSync(drainWatcherSource)) {
1415
- const drainWatcherPath = path2.join(tempDir, DRAIN_WATCHER_BINARY_NAME);
1416
- fs.copyFileSync(drainWatcherSource, drainWatcherPath);
1417
- fs.chmodSync(drainWatcherPath, 493);
1418
- }
1419
1033
  if (includeTLS) {
1420
1034
  const tlsKeygenPath = path2.join(tempDir, TLS_KEYGEN_BINARY_NAME);
1421
1035
  const tlsKeygenSource = findBinary("tls-keygen-linux-amd64");
@@ -1426,13 +1040,15 @@ async function setupLayeredBuildDirectory(environmentConfig, layeredDockerfileCo
1426
1040
  }
1427
1041
  fs.copyFileSync(tlsKeygenSource, tlsKeygenPath);
1428
1042
  fs.chmodSync(tlsKeygenPath, 493);
1429
- const userCaddyfilePath = path2.join(process.cwd(), CADDYFILE_NAME);
1430
- const destCaddyfilePath = path2.join(tempDir, CADDYFILE_NAME);
1431
- if (fs.existsSync(userCaddyfilePath)) {
1432
- fs.copyFileSync(userCaddyfilePath, destCaddyfilePath);
1433
- fs.chmodSync(destCaddyfilePath, 420);
1043
+ const caddyfilePath = path2.join(process.cwd(), CADDYFILE_NAME);
1044
+ if (fs.existsSync(caddyfilePath)) {
1045
+ const caddyfileContent = fs.readFileSync(caddyfilePath);
1046
+ const destCaddyfilePath = path2.join(tempDir, CADDYFILE_NAME);
1047
+ fs.writeFileSync(destCaddyfilePath, caddyfileContent, { mode: 420 });
1434
1048
  } else {
1435
- fs.writeFileSync(destCaddyfilePath, getDefaultCaddyfile(), { mode: 420 });
1049
+ throw new Error(
1050
+ "TLS is enabled (DOMAIN is set) but Caddyfile not found. Run configure TLS to set up TLS configuration"
1051
+ );
1436
1052
  }
1437
1053
  }
1438
1054
  return tempDir;
@@ -1500,16 +1116,7 @@ async function extractDigestFromSinglePlatform(manifest, imageRef) {
1500
1116
  architecture: inspectData[0].Architecture
1501
1117
  } : null;
1502
1118
  if (!config) {
1503
- if (manifest.config?.digest) {
1504
- const digest = hexStringToBytes32(manifest.config.digest);
1505
- const registry = extractRegistryName(imageRef);
1506
- return {
1507
- digest,
1508
- registry,
1509
- platform: DOCKER_PLATFORM
1510
- };
1511
- }
1512
- throw new Error(`Could not determine platform for ${imageRef}`);
1119
+ throw createPlatformErrorMessage(imageRef, ["unknown (could not determine architecture)"]);
1513
1120
  }
1514
1121
  const platform2 = `${config.os}/${config.architecture}`;
1515
1122
  if (platform2 === DOCKER_PLATFORM) {
@@ -1581,14 +1188,11 @@ Image: ${imageRef}
1581
1188
  Found platform(s): ${platforms.join(", ")}
1582
1189
  Required platform: ${DOCKER_PLATFORM}
1583
1190
 
1584
- To fix this issue:
1585
- 1. Manual fix:
1586
- a. Rebuild your image with the correct platform:
1587
- docker build --platform ${DOCKER_PLATFORM} -t ${imageRef} .
1588
- b. Push the rebuilt image to your remote registry:
1589
- docker push ${imageRef}
1590
-
1591
- 2. Or use the SDK to build with the correct platform automatically.`;
1191
+ To fix, either:
1192
+ 1. Rebuild the image for ${DOCKER_PLATFORM} and push it:
1193
+ docker buildx build --platform ${DOCKER_PLATFORM} -t ${imageRef} --push .
1194
+ 2. Or use a verifiable build (--verifiable --repo <repo> --commit <sha>), which
1195
+ builds server-side and needs no local Docker.`;
1592
1196
  return new Error(errorMsg);
1593
1197
  }
1594
1198
 
@@ -1693,6 +1297,8 @@ async function prepareRelease(options, logger) {
1693
1297
  logger.info(`Waiting ${REGISTRY_PROPAGATION_WAIT_SECONDS} seconds for registry propagation...`);
1694
1298
  await new Promise((resolve2) => setTimeout(resolve2, REGISTRY_PROPAGATION_WAIT_SECONDS * 1e3));
1695
1299
  } else {
1300
+ logger.info("Verifying image platform (linux/amd64)...");
1301
+ await getImageDigestAndName(imageRef);
1696
1302
  logger.info("Checking if image needs layering...");
1697
1303
  finalImageRef = await layerRemoteImageIfNeeded(
1698
1304
  {
@@ -1851,6 +1457,13 @@ function extractRegistryNameNoDocker(imageRef) {
1851
1457
  var import_viem = require("viem");
1852
1458
 
1853
1459
  // src/client/common/types/index.ts
1460
+ var EMPTY_CONTAINER_POLICY = {
1461
+ args: [],
1462
+ cmdOverride: [],
1463
+ env: [],
1464
+ envOverride: [],
1465
+ restartPolicy: ""
1466
+ };
1854
1467
  var noopLogger = {
1855
1468
  debug: () => {
1856
1469
  },
@@ -3091,49 +2704,1594 @@ async function executeBatch(options, logger = noopLogger) {
3091
2704
  } else {
3092
2705
  revertReason = callError.message || "Unknown reason";
3093
2706
  }
3094
- }
3095
- throw new Error(`Transaction reverted: ${hash}. Reason: ${revertReason}`);
2707
+ }
2708
+ throw new Error(`Transaction reverted: ${hash}. Reason: ${revertReason}`);
2709
+ }
2710
+ return hash;
2711
+ }
2712
+
2713
+ // src/client/common/contract/caller.ts
2714
+ var import_viem3 = require("viem");
2715
+
2716
+ // src/client/common/utils/helpers.ts
2717
+ var import_viem2 = require("viem");
2718
+ var import_chains2 = require("viem/chains");
2719
+ var import_accounts = require("viem/accounts");
2720
+ function getChainFromID(chainID, fallback2 = import_chains2.sepolia) {
2721
+ const id = Number(chainID);
2722
+ return (0, import_viem2.extractChain)({ chains: SUPPORTED_CHAINS, id }) || fallback2;
2723
+ }
2724
+ function createClients(options) {
2725
+ const { privateKey, rpcUrl, chainId } = options;
2726
+ const privateKeyHex = addHexPrefix(privateKey);
2727
+ const account = (0, import_accounts.privateKeyToAccount)(privateKeyHex);
2728
+ const chain = getChainFromID(chainId);
2729
+ const transport = typeof rpcUrl === "string" ? (0, import_viem2.http)(rpcUrl) : (0, import_viem2.fallback)(rpcUrl.map((url) => (0, import_viem2.http)(url)));
2730
+ const publicClient = (0, import_viem2.createPublicClient)({
2731
+ chain,
2732
+ transport
2733
+ });
2734
+ const walletClient = (0, import_viem2.createWalletClient)({
2735
+ account,
2736
+ chain,
2737
+ transport
2738
+ });
2739
+ return { walletClient, publicClient };
2740
+ }
2741
+ function addHexPrefix(value) {
2742
+ return value.startsWith("0x") ? value : `0x${value}`;
2743
+ }
2744
+ function stripHexPrefix(value) {
2745
+ return value.startsWith("0x") ? value.slice(2) : value;
2746
+ }
2747
+
2748
+ // src/client/common/abis/AppController.json
2749
+ var AppController_default = [
2750
+ {
2751
+ type: "constructor",
2752
+ inputs: [
2753
+ {
2754
+ name: "_version",
2755
+ type: "string",
2756
+ internalType: "string"
2757
+ },
2758
+ {
2759
+ name: "_permissionController",
2760
+ type: "address",
2761
+ internalType: "contractIPermissionController"
2762
+ },
2763
+ {
2764
+ name: "_releaseManager",
2765
+ type: "address",
2766
+ internalType: "contractIReleaseManager"
2767
+ },
2768
+ {
2769
+ name: "_computeAVSRegistrar",
2770
+ type: "address",
2771
+ internalType: "contractIComputeAVSRegistrar"
2772
+ },
2773
+ {
2774
+ name: "_computeOperator",
2775
+ type: "address",
2776
+ internalType: "contractIComputeOperator"
2777
+ },
2778
+ {
2779
+ name: "_appBeacon",
2780
+ type: "address",
2781
+ internalType: "contractIBeacon"
2782
+ }
2783
+ ],
2784
+ stateMutability: "nonpayable"
2785
+ },
2786
+ {
2787
+ type: "function",
2788
+ name: "API_PERMISSION_TYPEHASH",
2789
+ inputs: [],
2790
+ outputs: [
2791
+ {
2792
+ name: "",
2793
+ type: "bytes32",
2794
+ internalType: "bytes32"
2795
+ }
2796
+ ],
2797
+ stateMutability: "view"
2798
+ },
2799
+ {
2800
+ type: "function",
2801
+ name: "appBeacon",
2802
+ inputs: [],
2803
+ outputs: [
2804
+ {
2805
+ name: "",
2806
+ type: "address",
2807
+ internalType: "contractIBeacon"
2808
+ }
2809
+ ],
2810
+ stateMutability: "view"
2811
+ },
2812
+ {
2813
+ type: "function",
2814
+ name: "calculateApiPermissionDigestHash",
2815
+ inputs: [
2816
+ {
2817
+ name: "permission",
2818
+ type: "bytes4",
2819
+ internalType: "bytes4"
2820
+ },
2821
+ {
2822
+ name: "expiry",
2823
+ type: "uint256",
2824
+ internalType: "uint256"
2825
+ }
2826
+ ],
2827
+ outputs: [
2828
+ {
2829
+ name: "",
2830
+ type: "bytes32",
2831
+ internalType: "bytes32"
2832
+ }
2833
+ ],
2834
+ stateMutability: "view"
2835
+ },
2836
+ {
2837
+ type: "function",
2838
+ name: "calculateAppId",
2839
+ inputs: [
2840
+ {
2841
+ name: "deployer",
2842
+ type: "address",
2843
+ internalType: "address"
2844
+ },
2845
+ {
2846
+ name: "salt",
2847
+ type: "bytes32",
2848
+ internalType: "bytes32"
2849
+ }
2850
+ ],
2851
+ outputs: [
2852
+ {
2853
+ name: "",
2854
+ type: "address",
2855
+ internalType: "contractIApp"
2856
+ }
2857
+ ],
2858
+ stateMutability: "view"
2859
+ },
2860
+ {
2861
+ type: "function",
2862
+ name: "computeAVSRegistrar",
2863
+ inputs: [],
2864
+ outputs: [
2865
+ {
2866
+ name: "",
2867
+ type: "address",
2868
+ internalType: "contractIComputeAVSRegistrar"
2869
+ }
2870
+ ],
2871
+ stateMutability: "view"
2872
+ },
2873
+ {
2874
+ type: "function",
2875
+ name: "computeOperator",
2876
+ inputs: [],
2877
+ outputs: [
2878
+ {
2879
+ name: "",
2880
+ type: "address",
2881
+ internalType: "contractIComputeOperator"
2882
+ }
2883
+ ],
2884
+ stateMutability: "view"
2885
+ },
2886
+ {
2887
+ type: "function",
2888
+ name: "confirmUpgrade",
2889
+ inputs: [
2890
+ {
2891
+ name: "app",
2892
+ type: "address",
2893
+ internalType: "contractIApp"
2894
+ }
2895
+ ],
2896
+ outputs: [],
2897
+ stateMutability: "nonpayable"
2898
+ },
2899
+ {
2900
+ type: "function",
2901
+ name: "createApp",
2902
+ inputs: [
2903
+ {
2904
+ name: "salt",
2905
+ type: "bytes32",
2906
+ internalType: "bytes32"
2907
+ },
2908
+ {
2909
+ name: "release",
2910
+ type: "tuple",
2911
+ internalType: "structIAppController.Release",
2912
+ components: [
2913
+ {
2914
+ name: "rmsRelease",
2915
+ type: "tuple",
2916
+ internalType: "structIReleaseManagerTypes.Release",
2917
+ components: [
2918
+ {
2919
+ name: "artifacts",
2920
+ type: "tuple[]",
2921
+ internalType: "structIReleaseManagerTypes.Artifact[]",
2922
+ components: [
2923
+ {
2924
+ name: "digest",
2925
+ type: "bytes32",
2926
+ internalType: "bytes32"
2927
+ },
2928
+ {
2929
+ name: "registry",
2930
+ type: "string",
2931
+ internalType: "string"
2932
+ }
2933
+ ]
2934
+ },
2935
+ {
2936
+ name: "upgradeByTime",
2937
+ type: "uint32",
2938
+ internalType: "uint32"
2939
+ }
2940
+ ]
2941
+ },
2942
+ {
2943
+ name: "publicEnv",
2944
+ type: "bytes",
2945
+ internalType: "bytes"
2946
+ },
2947
+ {
2948
+ name: "encryptedEnv",
2949
+ type: "bytes",
2950
+ internalType: "bytes"
2951
+ },
2952
+ {
2953
+ name: "containerPolicy",
2954
+ type: "tuple",
2955
+ internalType: "structIAppController.ContainerPolicy",
2956
+ components: [
2957
+ {
2958
+ name: "args",
2959
+ type: "string[]",
2960
+ internalType: "string[]"
2961
+ },
2962
+ {
2963
+ name: "cmdOverride",
2964
+ type: "string[]",
2965
+ internalType: "string[]"
2966
+ },
2967
+ {
2968
+ name: "env",
2969
+ type: "tuple[]",
2970
+ internalType: "structIAppController.EnvVar[]",
2971
+ components: [
2972
+ {
2973
+ name: "key",
2974
+ type: "string",
2975
+ internalType: "string"
2976
+ },
2977
+ {
2978
+ name: "value",
2979
+ type: "string",
2980
+ internalType: "string"
2981
+ }
2982
+ ]
2983
+ },
2984
+ {
2985
+ name: "envOverride",
2986
+ type: "tuple[]",
2987
+ internalType: "structIAppController.EnvVar[]",
2988
+ components: [
2989
+ {
2990
+ name: "key",
2991
+ type: "string",
2992
+ internalType: "string"
2993
+ },
2994
+ {
2995
+ name: "value",
2996
+ type: "string",
2997
+ internalType: "string"
2998
+ }
2999
+ ]
3000
+ },
3001
+ {
3002
+ name: "restartPolicy",
3003
+ type: "string",
3004
+ internalType: "string"
3005
+ }
3006
+ ]
3007
+ }
3008
+ ]
3009
+ }
3010
+ ],
3011
+ outputs: [
3012
+ {
3013
+ name: "app",
3014
+ type: "address",
3015
+ internalType: "contractIApp"
3016
+ }
3017
+ ],
3018
+ stateMutability: "nonpayable"
3019
+ },
3020
+ {
3021
+ type: "function",
3022
+ name: "createAppWithIsolatedBilling",
3023
+ inputs: [
3024
+ {
3025
+ name: "salt",
3026
+ type: "bytes32",
3027
+ internalType: "bytes32"
3028
+ },
3029
+ {
3030
+ name: "release",
3031
+ type: "tuple",
3032
+ internalType: "structIAppController.Release",
3033
+ components: [
3034
+ {
3035
+ name: "rmsRelease",
3036
+ type: "tuple",
3037
+ internalType: "structIReleaseManagerTypes.Release",
3038
+ components: [
3039
+ {
3040
+ name: "artifacts",
3041
+ type: "tuple[]",
3042
+ internalType: "structIReleaseManagerTypes.Artifact[]",
3043
+ components: [
3044
+ {
3045
+ name: "digest",
3046
+ type: "bytes32",
3047
+ internalType: "bytes32"
3048
+ },
3049
+ {
3050
+ name: "registry",
3051
+ type: "string",
3052
+ internalType: "string"
3053
+ }
3054
+ ]
3055
+ },
3056
+ {
3057
+ name: "upgradeByTime",
3058
+ type: "uint32",
3059
+ internalType: "uint32"
3060
+ }
3061
+ ]
3062
+ },
3063
+ {
3064
+ name: "publicEnv",
3065
+ type: "bytes",
3066
+ internalType: "bytes"
3067
+ },
3068
+ {
3069
+ name: "encryptedEnv",
3070
+ type: "bytes",
3071
+ internalType: "bytes"
3072
+ },
3073
+ {
3074
+ name: "containerPolicy",
3075
+ type: "tuple",
3076
+ internalType: "structIAppController.ContainerPolicy",
3077
+ components: [
3078
+ {
3079
+ name: "args",
3080
+ type: "string[]",
3081
+ internalType: "string[]"
3082
+ },
3083
+ {
3084
+ name: "cmdOverride",
3085
+ type: "string[]",
3086
+ internalType: "string[]"
3087
+ },
3088
+ {
3089
+ name: "env",
3090
+ type: "tuple[]",
3091
+ internalType: "structIAppController.EnvVar[]",
3092
+ components: [
3093
+ {
3094
+ name: "key",
3095
+ type: "string",
3096
+ internalType: "string"
3097
+ },
3098
+ {
3099
+ name: "value",
3100
+ type: "string",
3101
+ internalType: "string"
3102
+ }
3103
+ ]
3104
+ },
3105
+ {
3106
+ name: "envOverride",
3107
+ type: "tuple[]",
3108
+ internalType: "structIAppController.EnvVar[]",
3109
+ components: [
3110
+ {
3111
+ name: "key",
3112
+ type: "string",
3113
+ internalType: "string"
3114
+ },
3115
+ {
3116
+ name: "value",
3117
+ type: "string",
3118
+ internalType: "string"
3119
+ }
3120
+ ]
3121
+ },
3122
+ {
3123
+ name: "restartPolicy",
3124
+ type: "string",
3125
+ internalType: "string"
3126
+ }
3127
+ ]
3128
+ }
3129
+ ]
3130
+ }
3131
+ ],
3132
+ outputs: [
3133
+ {
3134
+ name: "app",
3135
+ type: "address",
3136
+ internalType: "contractIApp"
3137
+ }
3138
+ ],
3139
+ stateMutability: "nonpayable"
3140
+ },
3141
+ {
3142
+ type: "function",
3143
+ name: "createEmptyApp",
3144
+ inputs: [
3145
+ {
3146
+ name: "salt",
3147
+ type: "bytes32",
3148
+ internalType: "bytes32"
3149
+ }
3150
+ ],
3151
+ outputs: [
3152
+ {
3153
+ name: "app",
3154
+ type: "address",
3155
+ internalType: "contractIApp"
3156
+ }
3157
+ ],
3158
+ stateMutability: "nonpayable"
3159
+ },
3160
+ {
3161
+ type: "function",
3162
+ name: "createEmptyAppWithIsolatedBilling",
3163
+ inputs: [
3164
+ {
3165
+ name: "salt",
3166
+ type: "bytes32",
3167
+ internalType: "bytes32"
3168
+ }
3169
+ ],
3170
+ outputs: [
3171
+ {
3172
+ name: "app",
3173
+ type: "address",
3174
+ internalType: "contractIApp"
3175
+ }
3176
+ ],
3177
+ stateMutability: "nonpayable"
3178
+ },
3179
+ {
3180
+ type: "function",
3181
+ name: "domainSeparator",
3182
+ inputs: [],
3183
+ outputs: [
3184
+ {
3185
+ name: "",
3186
+ type: "bytes32",
3187
+ internalType: "bytes32"
3188
+ }
3189
+ ],
3190
+ stateMutability: "view"
3191
+ },
3192
+ {
3193
+ type: "function",
3194
+ name: "getActiveAppCount",
3195
+ inputs: [
3196
+ {
3197
+ name: "user",
3198
+ type: "address",
3199
+ internalType: "address"
3200
+ }
3201
+ ],
3202
+ outputs: [
3203
+ {
3204
+ name: "",
3205
+ type: "uint32",
3206
+ internalType: "uint32"
3207
+ }
3208
+ ],
3209
+ stateMutability: "view"
3210
+ },
3211
+ {
3212
+ type: "function",
3213
+ name: "getAppCreator",
3214
+ inputs: [
3215
+ {
3216
+ name: "app",
3217
+ type: "address",
3218
+ internalType: "contractIApp"
3219
+ }
3220
+ ],
3221
+ outputs: [
3222
+ {
3223
+ name: "",
3224
+ type: "address",
3225
+ internalType: "address"
3226
+ }
3227
+ ],
3228
+ stateMutability: "view"
3229
+ },
3230
+ {
3231
+ type: "function",
3232
+ name: "getAppLatestReleaseBlockNumber",
3233
+ inputs: [
3234
+ {
3235
+ name: "app",
3236
+ type: "address",
3237
+ internalType: "contractIApp"
3238
+ }
3239
+ ],
3240
+ outputs: [
3241
+ {
3242
+ name: "",
3243
+ type: "uint32",
3244
+ internalType: "uint32"
3245
+ }
3246
+ ],
3247
+ stateMutability: "view"
3248
+ },
3249
+ {
3250
+ type: "function",
3251
+ name: "getAppOperatorSetId",
3252
+ inputs: [
3253
+ {
3254
+ name: "app",
3255
+ type: "address",
3256
+ internalType: "contractIApp"
3257
+ }
3258
+ ],
3259
+ outputs: [
3260
+ {
3261
+ name: "",
3262
+ type: "uint32",
3263
+ internalType: "uint32"
3264
+ }
3265
+ ],
3266
+ stateMutability: "view"
3267
+ },
3268
+ {
3269
+ type: "function",
3270
+ name: "getAppPendingReleaseBlockNumber",
3271
+ inputs: [
3272
+ {
3273
+ name: "app",
3274
+ type: "address",
3275
+ internalType: "contractIApp"
3276
+ }
3277
+ ],
3278
+ outputs: [
3279
+ {
3280
+ name: "",
3281
+ type: "uint32",
3282
+ internalType: "uint32"
3283
+ }
3284
+ ],
3285
+ stateMutability: "view"
3286
+ },
3287
+ {
3288
+ type: "function",
3289
+ name: "getAppStatus",
3290
+ inputs: [
3291
+ {
3292
+ name: "app",
3293
+ type: "address",
3294
+ internalType: "contractIApp"
3295
+ }
3296
+ ],
3297
+ outputs: [
3298
+ {
3299
+ name: "",
3300
+ type: "uint8",
3301
+ internalType: "enumIAppController.AppStatus"
3302
+ }
3303
+ ],
3304
+ stateMutability: "view"
3305
+ },
3306
+ {
3307
+ type: "function",
3308
+ name: "getApps",
3309
+ inputs: [
3310
+ {
3311
+ name: "offset",
3312
+ type: "uint256",
3313
+ internalType: "uint256"
3314
+ },
3315
+ {
3316
+ name: "limit",
3317
+ type: "uint256",
3318
+ internalType: "uint256"
3319
+ }
3320
+ ],
3321
+ outputs: [
3322
+ {
3323
+ name: "apps",
3324
+ type: "address[]",
3325
+ internalType: "contractIApp[]"
3326
+ },
3327
+ {
3328
+ name: "appConfigsMem",
3329
+ type: "tuple[]",
3330
+ internalType: "structIAppController.AppConfig[]",
3331
+ components: [
3332
+ {
3333
+ name: "creator",
3334
+ type: "address",
3335
+ internalType: "address"
3336
+ },
3337
+ {
3338
+ name: "operatorSetId",
3339
+ type: "uint32",
3340
+ internalType: "uint32"
3341
+ },
3342
+ {
3343
+ name: "latestReleaseBlockNumber",
3344
+ type: "uint32",
3345
+ internalType: "uint32"
3346
+ },
3347
+ {
3348
+ name: "pendingReleaseBlockNumber",
3349
+ type: "uint32",
3350
+ internalType: "uint32"
3351
+ },
3352
+ {
3353
+ name: "status",
3354
+ type: "uint8",
3355
+ internalType: "enumIAppController.AppStatus"
3356
+ }
3357
+ ]
3358
+ }
3359
+ ],
3360
+ stateMutability: "view"
3361
+ },
3362
+ {
3363
+ type: "function",
3364
+ name: "getAppsByBillingAccount",
3365
+ inputs: [
3366
+ {
3367
+ name: "account",
3368
+ type: "address",
3369
+ internalType: "address"
3370
+ },
3371
+ {
3372
+ name: "offset",
3373
+ type: "uint256",
3374
+ internalType: "uint256"
3375
+ },
3376
+ {
3377
+ name: "limit",
3378
+ type: "uint256",
3379
+ internalType: "uint256"
3380
+ }
3381
+ ],
3382
+ outputs: [
3383
+ {
3384
+ name: "apps",
3385
+ type: "address[]",
3386
+ internalType: "contractIApp[]"
3387
+ },
3388
+ {
3389
+ name: "appConfigsMem",
3390
+ type: "tuple[]",
3391
+ internalType: "structIAppController.AppConfig[]",
3392
+ components: [
3393
+ {
3394
+ name: "creator",
3395
+ type: "address",
3396
+ internalType: "address"
3397
+ },
3398
+ {
3399
+ name: "operatorSetId",
3400
+ type: "uint32",
3401
+ internalType: "uint32"
3402
+ },
3403
+ {
3404
+ name: "latestReleaseBlockNumber",
3405
+ type: "uint32",
3406
+ internalType: "uint32"
3407
+ },
3408
+ {
3409
+ name: "pendingReleaseBlockNumber",
3410
+ type: "uint32",
3411
+ internalType: "uint32"
3412
+ },
3413
+ {
3414
+ name: "status",
3415
+ type: "uint8",
3416
+ internalType: "enumIAppController.AppStatus"
3417
+ }
3418
+ ]
3419
+ }
3420
+ ],
3421
+ stateMutability: "view"
3422
+ },
3423
+ {
3424
+ type: "function",
3425
+ name: "getAppsByCreator",
3426
+ inputs: [
3427
+ {
3428
+ name: "creator",
3429
+ type: "address",
3430
+ internalType: "address"
3431
+ },
3432
+ {
3433
+ name: "offset",
3434
+ type: "uint256",
3435
+ internalType: "uint256"
3436
+ },
3437
+ {
3438
+ name: "limit",
3439
+ type: "uint256",
3440
+ internalType: "uint256"
3441
+ }
3442
+ ],
3443
+ outputs: [
3444
+ {
3445
+ name: "apps",
3446
+ type: "address[]",
3447
+ internalType: "contractIApp[]"
3448
+ },
3449
+ {
3450
+ name: "appConfigsMem",
3451
+ type: "tuple[]",
3452
+ internalType: "structIAppController.AppConfig[]",
3453
+ components: [
3454
+ {
3455
+ name: "creator",
3456
+ type: "address",
3457
+ internalType: "address"
3458
+ },
3459
+ {
3460
+ name: "operatorSetId",
3461
+ type: "uint32",
3462
+ internalType: "uint32"
3463
+ },
3464
+ {
3465
+ name: "latestReleaseBlockNumber",
3466
+ type: "uint32",
3467
+ internalType: "uint32"
3468
+ },
3469
+ {
3470
+ name: "pendingReleaseBlockNumber",
3471
+ type: "uint32",
3472
+ internalType: "uint32"
3473
+ },
3474
+ {
3475
+ name: "status",
3476
+ type: "uint8",
3477
+ internalType: "enumIAppController.AppStatus"
3478
+ }
3479
+ ]
3480
+ }
3481
+ ],
3482
+ stateMutability: "view"
3483
+ },
3484
+ {
3485
+ type: "function",
3486
+ name: "getAppsByDeveloper",
3487
+ inputs: [
3488
+ {
3489
+ name: "developer",
3490
+ type: "address",
3491
+ internalType: "address"
3492
+ },
3493
+ {
3494
+ name: "offset",
3495
+ type: "uint256",
3496
+ internalType: "uint256"
3497
+ },
3498
+ {
3499
+ name: "limit",
3500
+ type: "uint256",
3501
+ internalType: "uint256"
3502
+ }
3503
+ ],
3504
+ outputs: [
3505
+ {
3506
+ name: "apps",
3507
+ type: "address[]",
3508
+ internalType: "contractIApp[]"
3509
+ },
3510
+ {
3511
+ name: "appConfigsMem",
3512
+ type: "tuple[]",
3513
+ internalType: "structIAppController.AppConfig[]",
3514
+ components: [
3515
+ {
3516
+ name: "creator",
3517
+ type: "address",
3518
+ internalType: "address"
3519
+ },
3520
+ {
3521
+ name: "operatorSetId",
3522
+ type: "uint32",
3523
+ internalType: "uint32"
3524
+ },
3525
+ {
3526
+ name: "latestReleaseBlockNumber",
3527
+ type: "uint32",
3528
+ internalType: "uint32"
3529
+ },
3530
+ {
3531
+ name: "pendingReleaseBlockNumber",
3532
+ type: "uint32",
3533
+ internalType: "uint32"
3534
+ },
3535
+ {
3536
+ name: "status",
3537
+ type: "uint8",
3538
+ internalType: "enumIAppController.AppStatus"
3539
+ }
3540
+ ]
3541
+ }
3542
+ ],
3543
+ stateMutability: "view"
3544
+ },
3545
+ {
3546
+ type: "function",
3547
+ name: "getBillingAccount",
3548
+ inputs: [
3549
+ {
3550
+ name: "app",
3551
+ type: "address",
3552
+ internalType: "contractIApp"
3553
+ }
3554
+ ],
3555
+ outputs: [
3556
+ {
3557
+ name: "",
3558
+ type: "address",
3559
+ internalType: "address"
3560
+ }
3561
+ ],
3562
+ stateMutability: "view"
3563
+ },
3564
+ {
3565
+ type: "function",
3566
+ name: "getBillingType",
3567
+ inputs: [
3568
+ {
3569
+ name: "app",
3570
+ type: "address",
3571
+ internalType: "contractIApp"
3572
+ }
3573
+ ],
3574
+ outputs: [
3575
+ {
3576
+ name: "",
3577
+ type: "uint8",
3578
+ internalType: "enumIAppController.BillingType"
3579
+ }
3580
+ ],
3581
+ stateMutability: "view"
3582
+ },
3583
+ {
3584
+ type: "function",
3585
+ name: "getMaxActiveAppsPerUser",
3586
+ inputs: [
3587
+ {
3588
+ name: "user",
3589
+ type: "address",
3590
+ internalType: "address"
3591
+ }
3592
+ ],
3593
+ outputs: [
3594
+ {
3595
+ name: "",
3596
+ type: "uint32",
3597
+ internalType: "uint32"
3598
+ }
3599
+ ],
3600
+ stateMutability: "view"
3601
+ },
3602
+ {
3603
+ type: "function",
3604
+ name: "globalActiveAppCount",
3605
+ inputs: [],
3606
+ outputs: [
3607
+ {
3608
+ name: "",
3609
+ type: "uint32",
3610
+ internalType: "uint32"
3611
+ }
3612
+ ],
3613
+ stateMutability: "view"
3614
+ },
3615
+ {
3616
+ type: "function",
3617
+ name: "initialize",
3618
+ inputs: [
3619
+ {
3620
+ name: "admin",
3621
+ type: "address",
3622
+ internalType: "address"
3623
+ }
3624
+ ],
3625
+ outputs: [],
3626
+ stateMutability: "nonpayable"
3627
+ },
3628
+ {
3629
+ type: "function",
3630
+ name: "maxGlobalActiveApps",
3631
+ inputs: [],
3632
+ outputs: [
3633
+ {
3634
+ name: "",
3635
+ type: "uint32",
3636
+ internalType: "uint32"
3637
+ }
3638
+ ],
3639
+ stateMutability: "view"
3640
+ },
3641
+ {
3642
+ type: "function",
3643
+ name: "permissionController",
3644
+ inputs: [],
3645
+ outputs: [
3646
+ {
3647
+ name: "",
3648
+ type: "address",
3649
+ internalType: "contractIPermissionController"
3650
+ }
3651
+ ],
3652
+ stateMutability: "view"
3653
+ },
3654
+ {
3655
+ type: "function",
3656
+ name: "releaseManager",
3657
+ inputs: [],
3658
+ outputs: [
3659
+ {
3660
+ name: "",
3661
+ type: "address",
3662
+ internalType: "contractIReleaseManager"
3663
+ }
3664
+ ],
3665
+ stateMutability: "view"
3666
+ },
3667
+ {
3668
+ type: "function",
3669
+ name: "setMaxActiveAppsPerUser",
3670
+ inputs: [
3671
+ {
3672
+ name: "user",
3673
+ type: "address",
3674
+ internalType: "address"
3675
+ },
3676
+ {
3677
+ name: "limit",
3678
+ type: "uint32",
3679
+ internalType: "uint32"
3680
+ }
3681
+ ],
3682
+ outputs: [],
3683
+ stateMutability: "nonpayable"
3684
+ },
3685
+ {
3686
+ type: "function",
3687
+ name: "setMaxGlobalActiveApps",
3688
+ inputs: [
3689
+ {
3690
+ name: "limit",
3691
+ type: "uint32",
3692
+ internalType: "uint32"
3693
+ }
3694
+ ],
3695
+ outputs: [],
3696
+ stateMutability: "nonpayable"
3697
+ },
3698
+ {
3699
+ type: "function",
3700
+ name: "startApp",
3701
+ inputs: [
3702
+ {
3703
+ name: "app",
3704
+ type: "address",
3705
+ internalType: "contractIApp"
3706
+ }
3707
+ ],
3708
+ outputs: [],
3709
+ stateMutability: "nonpayable"
3710
+ },
3711
+ {
3712
+ type: "function",
3713
+ name: "stopApp",
3714
+ inputs: [
3715
+ {
3716
+ name: "app",
3717
+ type: "address",
3718
+ internalType: "contractIApp"
3719
+ }
3720
+ ],
3721
+ outputs: [],
3722
+ stateMutability: "nonpayable"
3723
+ },
3724
+ {
3725
+ type: "function",
3726
+ name: "suspend",
3727
+ inputs: [
3728
+ {
3729
+ name: "account",
3730
+ type: "address",
3731
+ internalType: "address"
3732
+ },
3733
+ {
3734
+ name: "apps",
3735
+ type: "address[]",
3736
+ internalType: "contractIApp[]"
3737
+ }
3738
+ ],
3739
+ outputs: [],
3740
+ stateMutability: "nonpayable"
3741
+ },
3742
+ {
3743
+ type: "function",
3744
+ name: "terminateApp",
3745
+ inputs: [
3746
+ {
3747
+ name: "app",
3748
+ type: "address",
3749
+ internalType: "contractIApp"
3750
+ }
3751
+ ],
3752
+ outputs: [],
3753
+ stateMutability: "nonpayable"
3754
+ },
3755
+ {
3756
+ type: "function",
3757
+ name: "terminateAppByAdmin",
3758
+ inputs: [
3759
+ {
3760
+ name: "app",
3761
+ type: "address",
3762
+ internalType: "contractIApp"
3763
+ }
3764
+ ],
3765
+ outputs: [],
3766
+ stateMutability: "nonpayable"
3767
+ },
3768
+ {
3769
+ type: "function",
3770
+ name: "updateAppMetadataURI",
3771
+ inputs: [
3772
+ {
3773
+ name: "app",
3774
+ type: "address",
3775
+ internalType: "contractIApp"
3776
+ },
3777
+ {
3778
+ name: "metadataURI",
3779
+ type: "string",
3780
+ internalType: "string"
3781
+ }
3782
+ ],
3783
+ outputs: [],
3784
+ stateMutability: "nonpayable"
3785
+ },
3786
+ {
3787
+ type: "function",
3788
+ name: "upgradeApp",
3789
+ inputs: [
3790
+ {
3791
+ name: "app",
3792
+ type: "address",
3793
+ internalType: "contractIApp"
3794
+ },
3795
+ {
3796
+ name: "release",
3797
+ type: "tuple",
3798
+ internalType: "structIAppController.Release",
3799
+ components: [
3800
+ {
3801
+ name: "rmsRelease",
3802
+ type: "tuple",
3803
+ internalType: "structIReleaseManagerTypes.Release",
3804
+ components: [
3805
+ {
3806
+ name: "artifacts",
3807
+ type: "tuple[]",
3808
+ internalType: "structIReleaseManagerTypes.Artifact[]",
3809
+ components: [
3810
+ {
3811
+ name: "digest",
3812
+ type: "bytes32",
3813
+ internalType: "bytes32"
3814
+ },
3815
+ {
3816
+ name: "registry",
3817
+ type: "string",
3818
+ internalType: "string"
3819
+ }
3820
+ ]
3821
+ },
3822
+ {
3823
+ name: "upgradeByTime",
3824
+ type: "uint32",
3825
+ internalType: "uint32"
3826
+ }
3827
+ ]
3828
+ },
3829
+ {
3830
+ name: "publicEnv",
3831
+ type: "bytes",
3832
+ internalType: "bytes"
3833
+ },
3834
+ {
3835
+ name: "encryptedEnv",
3836
+ type: "bytes",
3837
+ internalType: "bytes"
3838
+ },
3839
+ {
3840
+ name: "containerPolicy",
3841
+ type: "tuple",
3842
+ internalType: "structIAppController.ContainerPolicy",
3843
+ components: [
3844
+ {
3845
+ name: "args",
3846
+ type: "string[]",
3847
+ internalType: "string[]"
3848
+ },
3849
+ {
3850
+ name: "cmdOverride",
3851
+ type: "string[]",
3852
+ internalType: "string[]"
3853
+ },
3854
+ {
3855
+ name: "env",
3856
+ type: "tuple[]",
3857
+ internalType: "structIAppController.EnvVar[]",
3858
+ components: [
3859
+ {
3860
+ name: "key",
3861
+ type: "string",
3862
+ internalType: "string"
3863
+ },
3864
+ {
3865
+ name: "value",
3866
+ type: "string",
3867
+ internalType: "string"
3868
+ }
3869
+ ]
3870
+ },
3871
+ {
3872
+ name: "envOverride",
3873
+ type: "tuple[]",
3874
+ internalType: "structIAppController.EnvVar[]",
3875
+ components: [
3876
+ {
3877
+ name: "key",
3878
+ type: "string",
3879
+ internalType: "string"
3880
+ },
3881
+ {
3882
+ name: "value",
3883
+ type: "string",
3884
+ internalType: "string"
3885
+ }
3886
+ ]
3887
+ },
3888
+ {
3889
+ name: "restartPolicy",
3890
+ type: "string",
3891
+ internalType: "string"
3892
+ }
3893
+ ]
3894
+ }
3895
+ ]
3896
+ }
3897
+ ],
3898
+ outputs: [
3899
+ {
3900
+ name: "",
3901
+ type: "uint256",
3902
+ internalType: "uint256"
3903
+ }
3904
+ ],
3905
+ stateMutability: "nonpayable"
3906
+ },
3907
+ {
3908
+ type: "function",
3909
+ name: "version",
3910
+ inputs: [],
3911
+ outputs: [
3912
+ {
3913
+ name: "",
3914
+ type: "string",
3915
+ internalType: "string"
3916
+ }
3917
+ ],
3918
+ stateMutability: "view"
3919
+ },
3920
+ {
3921
+ type: "event",
3922
+ name: "AppCreated",
3923
+ inputs: [
3924
+ {
3925
+ name: "creator",
3926
+ type: "address",
3927
+ indexed: true,
3928
+ internalType: "address"
3929
+ },
3930
+ {
3931
+ name: "app",
3932
+ type: "address",
3933
+ indexed: true,
3934
+ internalType: "contractIApp"
3935
+ },
3936
+ {
3937
+ name: "operatorSetId",
3938
+ type: "uint32",
3939
+ indexed: false,
3940
+ internalType: "uint32"
3941
+ }
3942
+ ],
3943
+ anonymous: false
3944
+ },
3945
+ {
3946
+ type: "event",
3947
+ name: "AppMetadataURIUpdated",
3948
+ inputs: [
3949
+ {
3950
+ name: "app",
3951
+ type: "address",
3952
+ indexed: true,
3953
+ internalType: "contractIApp"
3954
+ },
3955
+ {
3956
+ name: "metadataURI",
3957
+ type: "string",
3958
+ indexed: false,
3959
+ internalType: "string"
3960
+ }
3961
+ ],
3962
+ anonymous: false
3963
+ },
3964
+ {
3965
+ type: "event",
3966
+ name: "AppStarted",
3967
+ inputs: [
3968
+ {
3969
+ name: "app",
3970
+ type: "address",
3971
+ indexed: true,
3972
+ internalType: "contractIApp"
3973
+ }
3974
+ ],
3975
+ anonymous: false
3976
+ },
3977
+ {
3978
+ type: "event",
3979
+ name: "AppStopped",
3980
+ inputs: [
3981
+ {
3982
+ name: "app",
3983
+ type: "address",
3984
+ indexed: true,
3985
+ internalType: "contractIApp"
3986
+ }
3987
+ ],
3988
+ anonymous: false
3989
+ },
3990
+ {
3991
+ type: "event",
3992
+ name: "AppSuspended",
3993
+ inputs: [
3994
+ {
3995
+ name: "app",
3996
+ type: "address",
3997
+ indexed: true,
3998
+ internalType: "contractIApp"
3999
+ }
4000
+ ],
4001
+ anonymous: false
4002
+ },
4003
+ {
4004
+ type: "event",
4005
+ name: "AppTerminated",
4006
+ inputs: [
4007
+ {
4008
+ name: "app",
4009
+ type: "address",
4010
+ indexed: true,
4011
+ internalType: "contractIApp"
4012
+ }
4013
+ ],
4014
+ anonymous: false
4015
+ },
4016
+ {
4017
+ type: "event",
4018
+ name: "AppTerminatedByAdmin",
4019
+ inputs: [
4020
+ {
4021
+ name: "app",
4022
+ type: "address",
4023
+ indexed: true,
4024
+ internalType: "contractIApp"
4025
+ }
4026
+ ],
4027
+ anonymous: false
4028
+ },
4029
+ {
4030
+ type: "event",
4031
+ name: "AppUpgraded",
4032
+ inputs: [
4033
+ {
4034
+ name: "app",
4035
+ type: "address",
4036
+ indexed: true,
4037
+ internalType: "contractIApp"
4038
+ },
4039
+ {
4040
+ name: "rmsReleaseId",
4041
+ type: "uint256",
4042
+ indexed: false,
4043
+ internalType: "uint256"
4044
+ },
4045
+ {
4046
+ name: "release",
4047
+ type: "tuple",
4048
+ indexed: false,
4049
+ internalType: "structIAppController.Release",
4050
+ components: [
4051
+ {
4052
+ name: "rmsRelease",
4053
+ type: "tuple",
4054
+ internalType: "structIReleaseManagerTypes.Release",
4055
+ components: [
4056
+ {
4057
+ name: "artifacts",
4058
+ type: "tuple[]",
4059
+ internalType: "structIReleaseManagerTypes.Artifact[]",
4060
+ components: [
4061
+ {
4062
+ name: "digest",
4063
+ type: "bytes32",
4064
+ internalType: "bytes32"
4065
+ },
4066
+ {
4067
+ name: "registry",
4068
+ type: "string",
4069
+ internalType: "string"
4070
+ }
4071
+ ]
4072
+ },
4073
+ {
4074
+ name: "upgradeByTime",
4075
+ type: "uint32",
4076
+ internalType: "uint32"
4077
+ }
4078
+ ]
4079
+ },
4080
+ {
4081
+ name: "publicEnv",
4082
+ type: "bytes",
4083
+ internalType: "bytes"
4084
+ },
4085
+ {
4086
+ name: "encryptedEnv",
4087
+ type: "bytes",
4088
+ internalType: "bytes"
4089
+ },
4090
+ {
4091
+ name: "containerPolicy",
4092
+ type: "tuple",
4093
+ internalType: "structIAppController.ContainerPolicy",
4094
+ components: [
4095
+ {
4096
+ name: "args",
4097
+ type: "string[]",
4098
+ internalType: "string[]"
4099
+ },
4100
+ {
4101
+ name: "cmdOverride",
4102
+ type: "string[]",
4103
+ internalType: "string[]"
4104
+ },
4105
+ {
4106
+ name: "env",
4107
+ type: "tuple[]",
4108
+ internalType: "structIAppController.EnvVar[]",
4109
+ components: [
4110
+ {
4111
+ name: "key",
4112
+ type: "string",
4113
+ internalType: "string"
4114
+ },
4115
+ {
4116
+ name: "value",
4117
+ type: "string",
4118
+ internalType: "string"
4119
+ }
4120
+ ]
4121
+ },
4122
+ {
4123
+ name: "envOverride",
4124
+ type: "tuple[]",
4125
+ internalType: "structIAppController.EnvVar[]",
4126
+ components: [
4127
+ {
4128
+ name: "key",
4129
+ type: "string",
4130
+ internalType: "string"
4131
+ },
4132
+ {
4133
+ name: "value",
4134
+ type: "string",
4135
+ internalType: "string"
4136
+ }
4137
+ ]
4138
+ },
4139
+ {
4140
+ name: "restartPolicy",
4141
+ type: "string",
4142
+ internalType: "string"
4143
+ }
4144
+ ]
4145
+ }
4146
+ ]
4147
+ }
4148
+ ],
4149
+ anonymous: false
4150
+ },
4151
+ {
4152
+ type: "event",
4153
+ name: "GlobalMaxActiveAppsSet",
4154
+ inputs: [
4155
+ {
4156
+ name: "limit",
4157
+ type: "uint32",
4158
+ indexed: false,
4159
+ internalType: "uint32"
4160
+ }
4161
+ ],
4162
+ anonymous: false
4163
+ },
4164
+ {
4165
+ type: "event",
4166
+ name: "Initialized",
4167
+ inputs: [
4168
+ {
4169
+ name: "version",
4170
+ type: "uint8",
4171
+ indexed: false,
4172
+ internalType: "uint8"
4173
+ }
4174
+ ],
4175
+ anonymous: false
4176
+ },
4177
+ {
4178
+ type: "event",
4179
+ name: "MaxActiveAppsSet",
4180
+ inputs: [
4181
+ {
4182
+ name: "user",
4183
+ type: "address",
4184
+ indexed: true,
4185
+ internalType: "address"
4186
+ },
4187
+ {
4188
+ name: "limit",
4189
+ type: "uint32",
4190
+ indexed: false,
4191
+ internalType: "uint32"
4192
+ }
4193
+ ],
4194
+ anonymous: false
4195
+ },
4196
+ {
4197
+ type: "event",
4198
+ name: "UpgradeConfirmed",
4199
+ inputs: [
4200
+ {
4201
+ name: "app",
4202
+ type: "address",
4203
+ indexed: true,
4204
+ internalType: "contractIApp"
4205
+ },
4206
+ {
4207
+ name: "pendingReleaseBlockNumber",
4208
+ type: "uint32",
4209
+ indexed: false,
4210
+ internalType: "uint32"
4211
+ }
4212
+ ],
4213
+ anonymous: false
4214
+ },
4215
+ {
4216
+ type: "error",
4217
+ name: "AccountHasActiveApps",
4218
+ inputs: []
4219
+ },
4220
+ {
4221
+ type: "error",
4222
+ name: "AppAlreadyExists",
4223
+ inputs: []
4224
+ },
4225
+ {
4226
+ type: "error",
4227
+ name: "AppDoesNotExist",
4228
+ inputs: []
4229
+ },
4230
+ {
4231
+ type: "error",
4232
+ name: "GlobalMaxActiveAppsExceeded",
4233
+ inputs: []
4234
+ },
4235
+ {
4236
+ type: "error",
4237
+ name: "InvalidAppStatus",
4238
+ inputs: []
4239
+ },
4240
+ {
4241
+ type: "error",
4242
+ name: "InvalidPermissions",
4243
+ inputs: []
4244
+ },
4245
+ {
4246
+ type: "error",
4247
+ name: "InvalidReleaseMetadataURI",
4248
+ inputs: []
4249
+ },
4250
+ {
4251
+ type: "error",
4252
+ name: "InvalidShortString",
4253
+ inputs: []
4254
+ },
4255
+ {
4256
+ type: "error",
4257
+ name: "InvalidSignature",
4258
+ inputs: []
4259
+ },
4260
+ {
4261
+ type: "error",
4262
+ name: "MaxActiveAppsExceeded",
4263
+ inputs: []
4264
+ },
4265
+ {
4266
+ type: "error",
4267
+ name: "MoreThanOneArtifact",
4268
+ inputs: []
4269
+ },
4270
+ {
4271
+ type: "error",
4272
+ name: "NoPendingUpgrade",
4273
+ inputs: []
4274
+ },
4275
+ {
4276
+ type: "error",
4277
+ name: "SignatureExpired",
4278
+ inputs: []
4279
+ },
4280
+ {
4281
+ type: "error",
4282
+ name: "StringTooLong",
4283
+ inputs: [
4284
+ {
4285
+ name: "str",
4286
+ type: "string",
4287
+ internalType: "string"
4288
+ }
4289
+ ]
3096
4290
  }
3097
- return hash;
3098
- }
3099
-
3100
- // src/client/common/contract/caller.ts
3101
- var import_viem3 = require("viem");
3102
-
3103
- // src/client/common/utils/helpers.ts
3104
- var import_viem2 = require("viem");
3105
- var import_chains2 = require("viem/chains");
3106
- var import_accounts = require("viem/accounts");
3107
- function getChainFromID(chainID, fallback2 = import_chains2.sepolia) {
3108
- const id = Number(chainID);
3109
- return (0, import_viem2.extractChain)({ chains: SUPPORTED_CHAINS, id }) || fallback2;
3110
- }
3111
- function createClients(options) {
3112
- const { privateKey, rpcUrl, chainId } = options;
3113
- const privateKeyHex = addHexPrefix(privateKey);
3114
- const account = (0, import_accounts.privateKeyToAccount)(privateKeyHex);
3115
- const chain = getChainFromID(chainId);
3116
- const transport = typeof rpcUrl === "string" ? (0, import_viem2.http)(rpcUrl) : (0, import_viem2.fallback)(rpcUrl.map((url) => (0, import_viem2.http)(url)));
3117
- const publicClient = (0, import_viem2.createPublicClient)({
3118
- chain,
3119
- transport
3120
- });
3121
- const walletClient = (0, import_viem2.createWalletClient)({
3122
- account,
3123
- chain,
3124
- transport
3125
- });
3126
- return { walletClient, publicClient };
3127
- }
3128
- function addHexPrefix(value) {
3129
- return value.startsWith("0x") ? value : `0x${value}`;
3130
- }
3131
- function stripHexPrefix(value) {
3132
- return value.startsWith("0x") ? value.slice(2) : value;
3133
- }
4291
+ ];
3134
4292
 
3135
- // src/client/common/abis/AppController.json
3136
- var AppController_default = [
4293
+ // src/client/common/abis/AppController.v1_4.json
4294
+ var AppController_v1_4_default = [
3137
4295
  {
3138
4296
  type: "constructor",
3139
4297
  inputs: [
@@ -4817,6 +5975,38 @@ var PermissionController_default = [
4817
5975
  ];
4818
5976
 
4819
5977
  // src/client/common/contract/caller.ts
5978
+ function appControllerAbiFor(environmentConfig) {
5979
+ return environmentConfig.releaseAbiVersion === "v1.4" ? AppController_v1_4_default : AppController_default;
5980
+ }
5981
+ function supportsContainerPolicy(environmentConfig) {
5982
+ return environmentConfig.releaseAbiVersion !== "v1.4";
5983
+ }
5984
+ function containerPolicyForViem(policy = EMPTY_CONTAINER_POLICY) {
5985
+ return {
5986
+ args: policy.args,
5987
+ cmdOverride: policy.cmdOverride,
5988
+ env: policy.env.map((e) => ({ key: e.key, value: e.value })),
5989
+ envOverride: policy.envOverride.map((e) => ({ key: e.key, value: e.value })),
5990
+ restartPolicy: policy.restartPolicy
5991
+ };
5992
+ }
5993
+ function releaseForViem(release, environmentConfig) {
5994
+ const base = {
5995
+ rmsRelease: {
5996
+ artifacts: release.rmsRelease.artifacts.map((artifact) => ({
5997
+ digest: `0x${(0, import_viem3.bytesToHex)(artifact.digest).slice(2).padStart(64, "0")}`,
5998
+ registry: artifact.registry
5999
+ })),
6000
+ upgradeByTime: release.rmsRelease.upgradeByTime
6001
+ },
6002
+ publicEnv: (0, import_viem3.bytesToHex)(release.publicEnv),
6003
+ encryptedEnv: (0, import_viem3.bytesToHex)(release.encryptedEnv)
6004
+ };
6005
+ if (!supportsContainerPolicy(environmentConfig)) {
6006
+ return base;
6007
+ }
6008
+ return { ...base, containerPolicy: containerPolicyForViem(release.containerPolicy) };
6009
+ }
4820
6010
  function formatETH(wei) {
4821
6011
  const eth = Number(wei) / 1e18;
4822
6012
  const costStr = eth.toFixed(6);
@@ -4854,7 +6044,7 @@ async function calculateAppID(options) {
4854
6044
  const saltHex = `0x${paddedSaltHex}`;
4855
6045
  const appID = await publicClient.readContract({
4856
6046
  address: environmentConfig.appControllerAddress,
4857
- abi: AppController_default,
6047
+ abi: appControllerAbiFor(environmentConfig),
4858
6048
  functionName: "calculateAppId",
4859
6049
  args: [ownerAddress, saltHex]
4860
6050
  });
@@ -4878,22 +6068,12 @@ async function prepareDeployBatch(options, logger = noopLogger) {
4878
6068
  const saltHexString = (0, import_viem3.bytesToHex)(salt).slice(2);
4879
6069
  const paddedSaltHex = saltHexString.padStart(64, "0");
4880
6070
  const saltHex = `0x${paddedSaltHex}`;
4881
- const releaseForViem = {
4882
- rmsRelease: {
4883
- artifacts: release.rmsRelease.artifacts.map((artifact) => ({
4884
- digest: `0x${(0, import_viem3.bytesToHex)(artifact.digest).slice(2).padStart(64, "0")}`,
4885
- registry: artifact.registry
4886
- })),
4887
- upgradeByTime: release.rmsRelease.upgradeByTime
4888
- },
4889
- publicEnv: (0, import_viem3.bytesToHex)(release.publicEnv),
4890
- encryptedEnv: (0, import_viem3.bytesToHex)(release.encryptedEnv)
4891
- };
6071
+ const release_ = releaseForViem(release, environmentConfig);
4892
6072
  const functionName = options.billTo === "app" ? "createAppWithIsolatedBilling" : "createApp";
4893
6073
  const createData = (0, import_viem3.encodeFunctionData)({
4894
- abi: AppController_default,
6074
+ abi: appControllerAbiFor(environmentConfig),
4895
6075
  functionName,
4896
- args: [saltHex, releaseForViem]
6076
+ args: [saltHex, release_]
4897
6077
  });
4898
6078
  const acceptAdminData = (0, import_viem3.encodeFunctionData)({
4899
6079
  abi: PermissionController_default,
@@ -4981,21 +6161,11 @@ async function prepareUpgradeBatch(options) {
4981
6161
  publicLogs,
4982
6162
  needsPermissionChange
4983
6163
  } = options;
4984
- const releaseForViem = {
4985
- rmsRelease: {
4986
- artifacts: release.rmsRelease.artifacts.map((artifact) => ({
4987
- digest: `0x${(0, import_viem3.bytesToHex)(artifact.digest).slice(2).padStart(64, "0")}`,
4988
- registry: artifact.registry
4989
- })),
4990
- upgradeByTime: release.rmsRelease.upgradeByTime
4991
- },
4992
- publicEnv: (0, import_viem3.bytesToHex)(release.publicEnv),
4993
- encryptedEnv: (0, import_viem3.bytesToHex)(release.encryptedEnv)
4994
- };
6164
+ const release_ = releaseForViem(release, environmentConfig);
4995
6165
  const upgradeData = (0, import_viem3.encodeFunctionData)({
4996
- abi: AppController_default,
6166
+ abi: appControllerAbiFor(environmentConfig),
4997
6167
  functionName: "upgradeApp",
4998
- args: [appID, releaseForViem]
6168
+ args: [appID, release_]
4999
6169
  });
5000
6170
  const executions = [
5001
6171
  {
@@ -5129,7 +6299,7 @@ ${pendingMessage}`);
5129
6299
  if (callError.data) {
5130
6300
  try {
5131
6301
  const decoded = (0, import_viem3.decodeErrorResult)({
5132
- abi: AppController_default,
6302
+ abi: appControllerAbiFor(environmentConfig),
5133
6303
  data: callError.data
5134
6304
  });
5135
6305
  const formattedError = formatAppControllerError(decoded);
@@ -5182,7 +6352,7 @@ function formatAppControllerError(decoded) {
5182
6352
  async function getActiveAppCount(publicClient, environmentConfig, user) {
5183
6353
  const count = await publicClient.readContract({
5184
6354
  address: environmentConfig.appControllerAddress,
5185
- abi: AppController_default,
6355
+ abi: appControllerAbiFor(environmentConfig),
5186
6356
  functionName: "getActiveAppCount",
5187
6357
  args: [user]
5188
6358
  });
@@ -5191,7 +6361,7 @@ async function getActiveAppCount(publicClient, environmentConfig, user) {
5191
6361
  async function getMaxActiveAppsPerUser(publicClient, environmentConfig, user) {
5192
6362
  const quota = await publicClient.readContract({
5193
6363
  address: environmentConfig.appControllerAddress,
5194
- abi: AppController_default,
6364
+ abi: appControllerAbiFor(environmentConfig),
5195
6365
  functionName: "getMaxActiveAppsPerUser",
5196
6366
  args: [user]
5197
6367
  });
@@ -5200,7 +6370,7 @@ async function getMaxActiveAppsPerUser(publicClient, environmentConfig, user) {
5200
6370
  async function getAppsByDeveloper(publicClient, environmentConfig, developer, offset, limit) {
5201
6371
  const result = await publicClient.readContract({
5202
6372
  address: environmentConfig.appControllerAddress,
5203
- abi: AppController_default,
6373
+ abi: appControllerAbiFor(environmentConfig),
5204
6374
  functionName: "getAppsByDeveloper",
5205
6375
  args: [developer, offset, limit]
5206
6376
  });
@@ -5212,7 +6382,7 @@ async function getAppsByDeveloper(publicClient, environmentConfig, developer, of
5212
6382
  async function getBillingType(publicClient, environmentConfig, app) {
5213
6383
  const result = await publicClient.readContract({
5214
6384
  address: environmentConfig.appControllerAddress,
5215
- abi: AppController_default,
6385
+ abi: appControllerAbiFor(environmentConfig),
5216
6386
  functionName: "getBillingType",
5217
6387
  args: [app]
5218
6388
  });
@@ -5221,7 +6391,7 @@ async function getBillingType(publicClient, environmentConfig, app) {
5221
6391
  async function getAppsByBillingAccount(publicClient, environmentConfig, account, offset, limit) {
5222
6392
  const result = await publicClient.readContract({
5223
6393
  address: environmentConfig.appControllerAddress,
5224
- abi: AppController_default,
6394
+ abi: appControllerAbiFor(environmentConfig),
5225
6395
  functionName: "getAppsByBillingAccount",
5226
6396
  args: [account, offset, limit]
5227
6397
  });
@@ -5255,7 +6425,7 @@ async function getAppLatestReleaseBlockNumbers(publicClient, environmentConfig,
5255
6425
  appIDs.map(
5256
6426
  (appID) => publicClient.readContract({
5257
6427
  address: environmentConfig.appControllerAddress,
5258
- abi: AppController_default,
6428
+ abi: appControllerAbiFor(environmentConfig),
5259
6429
  functionName: "getAppLatestReleaseBlockNumber",
5260
6430
  args: [appID]
5261
6431
  }).catch(() => null)
@@ -5334,12 +6504,41 @@ async function undelegate(options, logger = noopLogger) {
5334
6504
  return hash;
5335
6505
  }
5336
6506
 
6507
+ // src/client/common/gas/insufficientGas.ts
6508
+ var import_viem4 = require("viem");
6509
+ var InsufficientGasError = class extends Error {
6510
+ constructor(args) {
6511
+ const requiredEth = (0, import_viem4.formatEther)(args.requiredWei);
6512
+ const availableEth = (0, import_viem4.formatEther)(args.availableWei);
6513
+ super(
6514
+ `Insufficient ETH for gas: wallet ${args.address} has ${availableEth} ETH but this transaction needs ~${requiredEth} ETH.
6515
+ Compute credits do not pay on-chain gas \u2014 fund the wallet with ETH and retry.`
6516
+ );
6517
+ this.name = "InsufficientGasError";
6518
+ this.address = args.address;
6519
+ this.requiredWei = args.requiredWei;
6520
+ this.availableWei = args.availableWei;
6521
+ this.requiredEth = requiredEth;
6522
+ this.availableEth = availableEth;
6523
+ }
6524
+ };
6525
+ async function assertSufficientGas(args) {
6526
+ const availableWei = await args.publicClient.getBalance({ address: args.address });
6527
+ if (availableWei < args.gasEstimate.maxCostWei) {
6528
+ throw new InsufficientGasError({
6529
+ address: args.address,
6530
+ requiredWei: args.gasEstimate.maxCostWei,
6531
+ availableWei
6532
+ });
6533
+ }
6534
+ }
6535
+
5337
6536
  // src/client/common/utils/userapi.ts
5338
6537
  var import_axios2 = __toESM(require("axios"), 1);
5339
6538
 
5340
6539
  // src/client/common/utils/auth.ts
5341
- var import_viem4 = require("viem");
5342
- var APP_CONTROLLER_ABI = (0, import_viem4.parseAbi)([
6540
+ var import_viem5 = require("viem");
6541
+ var APP_CONTROLLER_ABI = (0, import_viem5.parseAbi)([
5343
6542
  "function calculateApiPermissionDigestHash(bytes4 permission, uint256 expiry) view returns (bytes32)"
5344
6543
  ]);
5345
6544
  async function calculatePermissionSignature(options) {
@@ -5397,6 +6596,7 @@ var import_axios = __toESM(require("axios"), 1);
5397
6596
  var MAX_RETRIES = 5;
5398
6597
  var INITIAL_BACKOFF_MS = 1e3;
5399
6598
  var MAX_BACKOFF_MS = 3e4;
6599
+ var RETRYABLE_STATUSES = /* @__PURE__ */ new Set([429, 502, 503, 504]);
5400
6600
  function sleep(ms) {
5401
6601
  return new Promise((resolve2) => setTimeout(resolve2, ms));
5402
6602
  }
@@ -5416,7 +6616,7 @@ async function requestWithRetry(config) {
5416
6616
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
5417
6617
  const res = await (0, import_axios.default)({ ...config, validateStatus: () => true });
5418
6618
  lastResponse = res;
5419
- if (res.status !== 429) {
6619
+ if (!RETRYABLE_STATUSES.has(res.status)) {
5420
6620
  return res;
5421
6621
  }
5422
6622
  if (attempt < MAX_RETRIES) {
@@ -5445,7 +6645,7 @@ var CanViewAppLogsPermission = "0x2fd3f2fe";
5445
6645
  var CanViewSensitiveAppInfoPermission = "0x0e67b22f";
5446
6646
  var CanUpdateAppProfilePermission = "0x036fef61";
5447
6647
  function getDefaultClientId() {
5448
- const version = true ? "1.0.0-devep7" : "0.0.0";
6648
+ const version = true ? "1.0.0" : "0.0.0";
5449
6649
  return `ecloud-sdk/v${version}`;
5450
6650
  }
5451
6651
  var UserApiClient = class {
@@ -5480,7 +6680,6 @@ var UserApiClient = class {
5480
6680
  status: app.app_status,
5481
6681
  ip: app.ip,
5482
6682
  machineType: app.machine_type,
5483
- hostname: app.hostname,
5484
6683
  profile: app.profile,
5485
6684
  metrics: app.metrics,
5486
6685
  evmAddresses,
@@ -5546,28 +6745,6 @@ var UserApiClient = class {
5546
6745
  status: app.app_status || app.App_Status || ""
5547
6746
  }));
5548
6747
  }
5549
- /**
5550
- * Get deployments for an app from the gRPC-gateway endpoint.
5551
- * Returns deployment records including upgrade_phase for tracking upgrade progress.
5552
- *
5553
- * Endpoint: GET /v1/apps/:appAddress/deployments
5554
- */
5555
- async getDeployments(appAddress) {
5556
- const endpoint = `${this.config.userApiServerURL}/v1/apps/${appAddress}/deployments`;
5557
- const response = await this.makeEIP712AuthenticatedRequest(endpoint);
5558
- const result = await response.json();
5559
- const deployments = result.deployments || [];
5560
- return deployments.map((dep) => ({
5561
- id: dep.id || "",
5562
- externalId: dep.external_id || dep.externalId || "",
5563
- endpoint: dep.endpoint || "",
5564
- releaseId: dep.release_id || dep.releaseId || "",
5565
- upgradePhase: dep.upgrade_phase || dep.upgradePhase || "",
5566
- replacesDeploymentId: dep.replaces_deployment_id || dep.replacesDeploymentId || "",
5567
- createdAt: dep.created_at || dep.createdAt || "",
5568
- updatedAt: dep.updated_at || dep.updatedAt || ""
5569
- }));
5570
- }
5571
6748
  /**
5572
6749
  * Upload app profile information with optional image
5573
6750
  *
@@ -5701,48 +6878,6 @@ Please check:
5701
6878
  "X-eigenx-expiry": expiry.toString()
5702
6879
  };
5703
6880
  }
5704
- /**
5705
- * Make an EIP-712 authenticated request to the gRPC-gateway endpoints.
5706
- * Uses the billing/compute auth pattern: Authorization + X-Account + X-Expiry headers.
5707
- */
5708
- async makeEIP712AuthenticatedRequest(url) {
5709
- const expiry = BigInt(Math.floor(Date.now() / 1e3) + 5 * 60);
5710
- const { signature } = await calculateBillingAuthSignature({
5711
- walletClient: this.walletClient,
5712
- product: "compute",
5713
- expiry
5714
- });
5715
- const headers = {
5716
- Authorization: `Bearer ${signature}`,
5717
- "X-Account": this.address,
5718
- "X-Expiry": expiry.toString(),
5719
- "x-client-id": this.clientId
5720
- };
5721
- try {
5722
- const response = await requestWithRetry({
5723
- method: "GET",
5724
- url,
5725
- headers,
5726
- maxRedirects: 0,
5727
- withCredentials: true
5728
- });
5729
- const status = response.status;
5730
- if (status < 200 || status >= 300) {
5731
- const body = typeof response.data === "string" ? response.data : JSON.stringify(response.data);
5732
- throw new Error(`gRPC-gateway request failed: ${status} - ${body}`);
5733
- }
5734
- return {
5735
- json: async () => response.data,
5736
- text: async () => typeof response.data === "string" ? response.data : JSON.stringify(response.data)
5737
- };
5738
- } catch (error) {
5739
- if (error.message?.includes("fetch failed") || error.message?.includes("ECONNREFUSED") || error.message?.includes("ENOTFOUND") || error.cause) {
5740
- const cause = error.cause?.message || error.cause || error.message;
5741
- throw new Error(`Failed to connect to API at ${url}: ${cause}`);
5742
- }
5743
- throw error;
5744
- }
5745
- }
5746
6881
  // ==========================================================================
5747
6882
  // SIWE Session Management
5748
6883
  // ==========================================================================
@@ -5848,8 +6983,36 @@ function transformAppRelease(raw) {
5848
6983
 
5849
6984
  // src/client/common/contract/watcher.ts
5850
6985
  var WATCH_POLL_INTERVAL_SECONDS = 5;
6986
+ var WATCH_HEARTBEAT_INTERVAL_SECONDS = 30;
6987
+ var WATCH_DEFAULT_TIMEOUT_SECONDS = 10 * 60;
5851
6988
  var APP_STATUS_RUNNING = "Running";
5852
6989
  var APP_STATUS_FAILED = "Failed";
6990
+ var WatchTimeoutError = class extends Error {
6991
+ constructor(args) {
6992
+ super(
6993
+ args.message ?? `Timed out after ${args.elapsedSeconds}s waiting for app ${args.appId} (last status: ${args.lastStatus ?? "unknown"})`
6994
+ );
6995
+ this.name = "WatchTimeoutError";
6996
+ this.appId = args.appId;
6997
+ this.elapsedSeconds = args.elapsedSeconds;
6998
+ this.lastStatus = args.lastStatus;
6999
+ this.timeoutSeconds = args.timeoutSeconds;
7000
+ }
7001
+ };
7002
+ function resolveWatchTimeoutSeconds(explicit) {
7003
+ if (typeof explicit === "number" && Number.isFinite(explicit) && explicit > 0) {
7004
+ return Math.floor(explicit);
7005
+ }
7006
+ const raw = process.env.ECLOUD_WATCH_TIMEOUT_SECONDS;
7007
+ if (raw === void 0 || raw === "") {
7008
+ return WATCH_DEFAULT_TIMEOUT_SECONDS;
7009
+ }
7010
+ const parsed = Number(raw);
7011
+ if (!Number.isFinite(parsed) || parsed <= 0) {
7012
+ return WATCH_DEFAULT_TIMEOUT_SECONDS;
7013
+ }
7014
+ return Math.floor(parsed);
7015
+ }
5853
7016
  async function watchUntilRunning(options, logger) {
5854
7017
  const { walletClient, publicClient, environmentConfig, appId } = options;
5855
7018
  const userApiClient = new UserApiClient(environmentConfig, walletClient, publicClient);
@@ -5880,8 +7043,20 @@ async function watchUntilRunning(options, logger) {
5880
7043
  return false;
5881
7044
  };
5882
7045
  const startTime = Date.now();
7046
+ const timeoutSeconds = resolveWatchTimeoutSeconds(options.timeoutSeconds);
5883
7047
  let lastLoggedStatus;
7048
+ let lastHeartbeatAt = startTime;
5884
7049
  while (true) {
7050
+ const elapsedMs = Date.now() - startTime;
7051
+ const elapsed = Math.round(elapsedMs / 1e3);
7052
+ if (elapsed >= timeoutSeconds) {
7053
+ throw new WatchTimeoutError({
7054
+ appId,
7055
+ elapsedSeconds: elapsed,
7056
+ lastStatus: lastLoggedStatus,
7057
+ timeoutSeconds
7058
+ });
7059
+ }
5885
7060
  try {
5886
7061
  const info = await userApiClient.getInfos([appId], 1);
5887
7062
  if (info.length === 0) {
@@ -5891,62 +7066,35 @@ async function watchUntilRunning(options, logger) {
5891
7066
  const appInfo = info[0];
5892
7067
  const currentStatus = appInfo.status;
5893
7068
  const currentIP = appInfo.ip || "";
5894
- const elapsed = Math.round((Date.now() - startTime) / 1e3);
5895
7069
  if (currentStatus !== lastLoggedStatus) {
5896
7070
  logger.info(`Status: ${currentStatus} (${elapsed}s)`);
5897
7071
  lastLoggedStatus = currentStatus;
7072
+ lastHeartbeatAt = Date.now();
7073
+ } else if (Date.now() - lastHeartbeatAt >= WATCH_HEARTBEAT_INTERVAL_SECONDS * 1e3) {
7074
+ logger.info(`Status: ${currentStatus} (${elapsed}s)`);
7075
+ lastHeartbeatAt = Date.now();
5898
7076
  }
5899
7077
  if (stopCondition(currentStatus, currentIP)) {
5900
7078
  return currentIP || void 0;
5901
7079
  }
5902
7080
  await sleep2(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5903
7081
  } catch (error) {
7082
+ if (error instanceof WatchTimeoutError) {
7083
+ throw error;
7084
+ }
5904
7085
  logger.warn(`Failed to fetch app info: ${error.message}`);
5905
7086
  await sleep2(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5906
7087
  }
5907
7088
  }
5908
7089
  }
5909
7090
  var APP_STATUS_STOPPED = "Stopped";
5910
- var UPGRADE_PHASE_LABELS = {
5911
- provisioning: "Provisioning new instance",
5912
- health_check: "Running health checks",
5913
- draining: "Switching traffic & draining old instance",
5914
- complete: "Complete",
5915
- rolling_back: "Rolling back",
5916
- rollback_done: "Rollback complete",
5917
- db_handoff: "Database handoff",
5918
- // Prewarm-detach phases
5919
- awaiting_userdata: "Waiting for instance readiness",
5920
- draining_old: "Draining old instance",
5921
- detached: "Detaching storage",
5922
- attached_to_new: "Attaching storage to new instance",
5923
- finalizing: "Finalizing new instance",
5924
- flipping: "Switching traffic",
5925
- teardown_old: "Cleaning up old instance"
5926
- };
5927
- async function fetchUpgradePhase(userApiClient, appId) {
5928
- try {
5929
- const deployments = await userApiClient.getDeployments(appId);
5930
- if (deployments.length === 0) return void 0;
5931
- const sorted = [...deployments].sort((a, b) => {
5932
- const ta = a.createdAt ? new Date(a.createdAt).getTime() : 0;
5933
- const tb = b.createdAt ? new Date(b.createdAt).getTime() : 0;
5934
- return tb - ta;
5935
- });
5936
- return sorted[0].upgradePhase || void 0;
5937
- } catch {
5938
- return void 0;
5939
- }
5940
- }
5941
7091
  async function watchUntilUpgradeComplete(options, logger) {
5942
7092
  const { walletClient, publicClient, environmentConfig, appId } = options;
7093
+ const timeoutSeconds = resolveWatchTimeoutSeconds(options.timeoutSeconds);
5943
7094
  const userApiClient = new UserApiClient(environmentConfig, walletClient, publicClient);
5944
7095
  let initialStatus;
5945
7096
  let initialIP;
5946
7097
  let hasChanged = false;
5947
- const startTime = Date.now();
5948
- let lastLoggedStatus;
5949
- let lastLoggedPhase;
5950
7098
  const stopCondition = (status, ip) => {
5951
7099
  if (!initialStatus) {
5952
7100
  initialStatus = status;
@@ -5980,37 +7128,44 @@ async function watchUntilUpgradeComplete(options, logger) {
5980
7128
  }
5981
7129
  return false;
5982
7130
  };
7131
+ const startTime = Date.now();
7132
+ const deadline = startTime + timeoutSeconds * 1e3;
7133
+ let lastLoggedStatus;
7134
+ let lastObservedStatus;
5983
7135
  while (true) {
7136
+ if (Date.now() >= deadline) {
7137
+ const elapsedSeconds = Math.round((Date.now() - startTime) / 1e3);
7138
+ throw new WatchTimeoutError({
7139
+ appId,
7140
+ lastStatus: lastObservedStatus,
7141
+ elapsedSeconds,
7142
+ timeoutSeconds
7143
+ });
7144
+ }
5984
7145
  try {
5985
- const [infoResult, upgradePhase] = await Promise.all([
5986
- userApiClient.getInfos([appId], 1),
5987
- fetchUpgradePhase(userApiClient, appId)
5988
- ]);
5989
- if (infoResult.length === 0) {
7146
+ const info = await userApiClient.getInfos([appId], 1);
7147
+ if (info.length === 0) {
5990
7148
  await sleep2(WATCH_POLL_INTERVAL_SECONDS * 1e3);
5991
7149
  continue;
5992
7150
  }
5993
- const appInfo = infoResult[0];
7151
+ const appInfo = info[0];
5994
7152
  const currentStatus = appInfo.status;
5995
7153
  const currentIP = appInfo.ip || "";
7154
+ lastObservedStatus = currentStatus;
5996
7155
  const elapsed = Math.round((Date.now() - startTime) / 1e3);
5997
- if (upgradePhase && upgradePhase !== lastLoggedPhase) {
5998
- const label = UPGRADE_PHASE_LABELS[upgradePhase] || upgradePhase;
5999
- logger.info(`Phase: ${label} (${elapsed}s)`);
6000
- lastLoggedPhase = upgradePhase;
6001
- }
6002
- if (!upgradePhase && currentStatus !== lastLoggedStatus) {
7156
+ if (currentStatus !== lastLoggedStatus) {
6003
7157
  logger.info(`Status: ${currentStatus} (${elapsed}s)`);
6004
7158
  lastLoggedStatus = currentStatus;
6005
7159
  }
6006
7160
  if (stopCondition(currentStatus, currentIP)) {
6007
- const totalElapsed = Math.round((Date.now() - startTime) / 1e3);
6008
- logger.info(`Upgrade completed in ${totalElapsed}s`);
6009
7161
  return;
6010
7162
  }
6011
7163
  await sleep2(WATCH_POLL_INTERVAL_SECONDS * 1e3);
6012
7164
  } catch (error) {
6013
- if (error.message?.includes("entered")) {
7165
+ if (error instanceof WatchTimeoutError) {
7166
+ throw error;
7167
+ }
7168
+ if (typeof error?.message === "string" && error.message.includes("Failed")) {
6014
7169
  throw error;
6015
7170
  }
6016
7171
  logger.warn(`Failed to fetch app info: ${error.message}`);
@@ -6025,7 +7180,7 @@ function sleep2(ms) {
6025
7180
  // src/client/common/utils/validation.ts
6026
7181
  var import_fs = __toESM(require("fs"), 1);
6027
7182
  var import_path = __toESM(require("path"), 1);
6028
- var import_viem5 = require("viem");
7183
+ var import_viem6 = require("viem");
6029
7184
  function validateAppName(name) {
6030
7185
  if (!name) {
6031
7186
  throw new Error("App name cannot be empty");
@@ -6177,7 +7332,7 @@ function validateAppID(appID) {
6177
7332
  throw new Error("App ID is required");
6178
7333
  }
6179
7334
  const normalized = typeof appID === "string" ? addHexPrefix(appID) : appID;
6180
- if ((0, import_viem5.isAddress)(normalized)) {
7335
+ if ((0, import_viem6.isAddress)(normalized)) {
6181
7336
  return normalized;
6182
7337
  }
6183
7338
  throw new Error(`Invalid app ID: '${appID}' is not a valid address`);
@@ -6321,6 +7476,7 @@ function validateLogsParams(params) {
6321
7476
  // src/client/common/config/environment.ts
6322
7477
  var SEPOLIA_CHAIN_ID = 11155111;
6323
7478
  var MAINNET_CHAIN_ID = 1;
7479
+ var BASE_SEPOLIA_CHAIN_ID = 84532;
6324
7480
  var CommonAddresses = {
6325
7481
  ERC7702Delegator: "0x63c0c19a282a1b52b07dd5a65b58948a07dae32b"
6326
7482
  };
@@ -6340,27 +7496,28 @@ var BILLING_ENVIRONMENTS = {
6340
7496
  billingApiServerURL: "https://billingapi.eigencloud.xyz"
6341
7497
  }
6342
7498
  };
6343
- var PLATFORM_ENV_TESTNET_SEPOLIA = "testnet-sepolia";
6344
- var PLATFORM_ENV_MAINNET_ETHEREUM = "mainnet-ethereum";
6345
- var DEFAULT_APP_BASE_DOMAIN = "eigencloud.xyz";
6346
7499
  var ENVIRONMENTS = {
6347
7500
  "sepolia-dev": {
6348
7501
  name: "sepolia",
6349
7502
  build: "dev",
6350
7503
  appControllerAddress: "0xa86DC1C47cb2518327fB4f9A1627F51966c83B92",
7504
+ releaseAbiVersion: "v1.5",
7505
+ // AppController upgraded to v1.5.x (containerPolicy)
6351
7506
  permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
6352
7507
  erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
6353
7508
  kmsServerURL: "http://10.128.0.57:8080",
6354
7509
  userApiServerURL: "https://userapi-compute-sepolia-dev.eigencloud.xyz",
6355
7510
  defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com",
6356
7511
  usdcCreditsAddress: "0xbdA3897c3A428763B59015C64AB766c288C97376",
6357
- platformEnv: PLATFORM_ENV_TESTNET_SEPOLIA,
6358
- appBaseDomain: DEFAULT_APP_BASE_DOMAIN
7512
+ baseUsdcCreditsAddress: "0x7673a47463F80c6a3553Db9E54c8cDcd5313d0ac",
7513
+ baseRPCURL: "https://base-sepolia-rpc.publicnode.com"
6359
7514
  },
6360
7515
  sepolia: {
6361
7516
  name: "sepolia",
6362
7517
  build: "prod",
6363
7518
  appControllerAddress: "0x0dd810a6ffba6a9820a10d97b659f07d8d23d4E2",
7519
+ releaseAbiVersion: "v1.4",
7520
+ // prod still on AppController v1.4.0 (3-field Release)
6364
7521
  permissionControllerAddress: ChainAddresses[SEPOLIA_CHAIN_ID].PermissionController,
6365
7522
  erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
6366
7523
  kmsServerURL: "http://10.128.15.203:8080",
@@ -6368,21 +7525,21 @@ var ENVIRONMENTS = {
6368
7525
  defaultRPCURL: "https://ethereum-sepolia-rpc.publicnode.com",
6369
7526
  billingRPCURL: "https://ethereum-rpc.publicnode.com",
6370
7527
  usdcCreditsAddress: "0xed9c88640ca9149Bd9f7ee6620074af10F2E145d",
6371
- platformEnv: PLATFORM_ENV_TESTNET_SEPOLIA,
6372
- appBaseDomain: DEFAULT_APP_BASE_DOMAIN
7528
+ baseUsdcCreditsAddress: "0x7673a47463F80c6a3553Db9E54c8cDcd5313d0ac",
7529
+ baseRPCURL: "https://base-sepolia-rpc.publicnode.com"
6373
7530
  },
6374
7531
  "mainnet-alpha": {
6375
7532
  name: "mainnet-alpha",
6376
7533
  build: "prod",
6377
7534
  appControllerAddress: "0xc38d35Fc995e75342A21CBd6D770305b142Fbe67",
7535
+ releaseAbiVersion: "v1.4",
7536
+ // prod still on AppController v1.4.0 (3-field Release)
6378
7537
  permissionControllerAddress: ChainAddresses[MAINNET_CHAIN_ID].PermissionController,
6379
7538
  erc7702DelegatorAddress: CommonAddresses.ERC7702Delegator,
6380
7539
  kmsServerURL: "http://10.128.0.2:8080",
6381
7540
  userApiServerURL: "https://userapi-compute.eigencloud.xyz",
6382
7541
  defaultRPCURL: "https://ethereum-rpc.publicnode.com",
6383
- usdcCreditsAddress: "0xed9c88640ca9149Bd9f7ee6620074af10F2E145d",
6384
- platformEnv: PLATFORM_ENV_MAINNET_ETHEREUM,
6385
- appBaseDomain: DEFAULT_APP_BASE_DOMAIN
7542
+ usdcCreditsAddress: "0xed9c88640ca9149Bd9f7ee6620074af10F2E145d"
6386
7543
  }
6387
7544
  };
6388
7545
  var CHAIN_ID_TO_ENVIRONMENT = {
@@ -6442,7 +7599,7 @@ function getBillingEnvironmentConfig(build) {
6442
7599
  };
6443
7600
  }
6444
7601
  function getBuildType() {
6445
- const buildTimeType = true ? "dev"?.toLowerCase() : void 0;
7602
+ const buildTimeType = true ? "prod"?.toLowerCase() : void 0;
6446
7603
  const runtimeType = process.env.BUILD_TYPE?.toLowerCase();
6447
7604
  const buildType = buildTimeType || runtimeType;
6448
7605
  if (buildType === "dev") {
@@ -6759,6 +7916,63 @@ var BillingApiClient = class {
6759
7916
  return resp.json();
6760
7917
  }
6761
7918
  // ==========================================================================
7919
+ // Admin - Coupon Methods
7920
+ // ==========================================================================
7921
+ async createCoupon(amountCents) {
7922
+ const endpoint = `${this.config.billingApiServerURL}/admin/coupons`;
7923
+ const resp = await this.makeAuthenticatedRequest(endpoint, "POST", "compute", { amountCents });
7924
+ return resp.json();
7925
+ }
7926
+ async listCoupons(opts) {
7927
+ const params = new URLSearchParams();
7928
+ if (opts?.offset !== void 0) params.set("offset", opts.offset.toString());
7929
+ if (opts?.limit !== void 0) params.set("limit", opts.limit.toString());
7930
+ if (opts?.active !== void 0) params.set("active", opts.active.toString());
7931
+ if (opts?.redeemed !== void 0) params.set("redeemed", opts.redeemed.toString());
7932
+ const qs = params.toString();
7933
+ const endpoint = `${this.config.billingApiServerURL}/admin/coupons${qs ? `?${qs}` : ""}`;
7934
+ const resp = await this.makeAuthenticatedRequest(endpoint, "GET", "compute");
7935
+ return resp.json();
7936
+ }
7937
+ async getCoupon(id) {
7938
+ const endpoint = `${this.config.billingApiServerURL}/admin/coupons/${id}`;
7939
+ const resp = await this.makeAuthenticatedRequest(endpoint, "GET", "compute");
7940
+ return resp.json();
7941
+ }
7942
+ async deactivateCoupon(id) {
7943
+ const endpoint = `${this.config.billingApiServerURL}/admin/coupons/${id}/deactivate`;
7944
+ await this.makeAuthenticatedRequest(endpoint, "POST", "compute");
7945
+ }
7946
+ async redeemCouponForUser(id, address) {
7947
+ const endpoint = `${this.config.billingApiServerURL}/admin/coupons/${id}/redeem`;
7948
+ await this.makeAuthenticatedRequest(endpoint, "POST", "compute", { address });
7949
+ }
7950
+ // ==========================================================================
7951
+ // Admin - Admin Management Methods
7952
+ // ==========================================================================
7953
+ async addAdmin(address) {
7954
+ const endpoint = `${this.config.billingApiServerURL}/admin/admins`;
7955
+ const resp = await this.makeAuthenticatedRequest(endpoint, "POST", "compute", { address });
7956
+ return resp.json();
7957
+ }
7958
+ async removeAdmin(address) {
7959
+ const endpoint = `${this.config.billingApiServerURL}/admin/admins/${address}`;
7960
+ await this.makeAuthenticatedRequest(endpoint, "DELETE", "compute");
7961
+ }
7962
+ async listAdmins() {
7963
+ const endpoint = `${this.config.billingApiServerURL}/admin/admins`;
7964
+ const resp = await this.makeAuthenticatedRequest(endpoint, "GET", "compute");
7965
+ return resp.json();
7966
+ }
7967
+ // ==========================================================================
7968
+ // User - Coupon Redemption
7969
+ // ==========================================================================
7970
+ async redeemCoupon(code) {
7971
+ const endpoint = `${this.config.billingApiServerURL}/v1/coupons/redeem`;
7972
+ const resp = await this.makeAuthenticatedRequest(endpoint, "POST", "compute", { code });
7973
+ return resp.json();
7974
+ }
7975
+ // ==========================================================================
6762
7976
  // Internal Methods
6763
7977
  // ==========================================================================
6764
7978
  /**
@@ -7174,6 +8388,11 @@ async function prepareDeployFromVerifiableBuild(options, logger = defaultLogger)
7174
8388
  executions: batch.executions,
7175
8389
  authorizationList
7176
8390
  });
8391
+ await assertSufficientGas({
8392
+ publicClient: batch.publicClient,
8393
+ address: batch.walletClient.account.address,
8394
+ gasEstimate
8395
+ });
7177
8396
  const data = {
7178
8397
  appId: batch.appId,
7179
8398
  salt: batch.salt,
@@ -7432,6 +8651,11 @@ async function prepareDeploy(options, logger = defaultLogger) {
7432
8651
  executions: batch.executions,
7433
8652
  authorizationList
7434
8653
  });
8654
+ await assertSufficientGas({
8655
+ publicClient: batch.publicClient,
8656
+ address: batch.walletClient.account.address,
8657
+ gasEstimate
8658
+ });
7435
8659
  const data = {
7436
8660
  appId: batch.appId,
7437
8661
  salt: batch.salt,
@@ -7468,7 +8692,7 @@ async function executeDeploy(options) {
7468
8692
  }
7469
8693
  );
7470
8694
  }
7471
- async function watchDeployment(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry) {
8695
+ async function watchDeployment(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry, opts) {
7472
8696
  return withSDKTelemetry(
7473
8697
  {
7474
8698
  functionName: "watchDeployment",
@@ -7484,7 +8708,8 @@ async function watchDeployment(appId, walletClient, publicClient, environmentCon
7484
8708
  walletClient,
7485
8709
  publicClient,
7486
8710
  environmentConfig,
7487
- appId
8711
+ appId,
8712
+ timeoutSeconds: opts?.timeoutSeconds
7488
8713
  },
7489
8714
  logger
7490
8715
  );
@@ -7580,6 +8805,11 @@ async function prepareUpgradeFromVerifiableBuild(options, logger = defaultLogger
7580
8805
  executions: batch.executions,
7581
8806
  authorizationList
7582
8807
  });
8808
+ await assertSufficientGas({
8809
+ publicClient: batch.publicClient,
8810
+ address: batch.walletClient.account.address,
8811
+ gasEstimate
8812
+ });
7583
8813
  const data = {
7584
8814
  appId: batch.appId,
7585
8815
  executions: batch.executions,
@@ -7769,6 +8999,11 @@ async function prepareUpgrade(options, logger = defaultLogger) {
7769
8999
  executions: batch.executions,
7770
9000
  authorizationList
7771
9001
  });
9002
+ await assertSufficientGas({
9003
+ publicClient: batch.publicClient,
9004
+ address: batch.walletClient.account.address,
9005
+ gasEstimate
9006
+ });
7772
9007
  const data = {
7773
9008
  appId: batch.appId,
7774
9009
  executions: batch.executions,
@@ -7803,7 +9038,7 @@ async function executeUpgrade(options) {
7803
9038
  }
7804
9039
  );
7805
9040
  }
7806
- async function watchUpgrade(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry) {
9041
+ async function watchUpgrade(appId, walletClient, publicClient, environmentConfig, logger = defaultLogger, skipTelemetry, opts) {
7807
9042
  return withSDKTelemetry(
7808
9043
  {
7809
9044
  functionName: "watchUpgrade",
@@ -7819,7 +9054,8 @@ async function watchUpgrade(appId, walletClient, publicClient, environmentConfig
7819
9054
  walletClient,
7820
9055
  publicClient,
7821
9056
  environmentConfig,
7822
- appId
9057
+ appId,
9058
+ timeoutSeconds: opts?.timeoutSeconds
7823
9059
  },
7824
9060
  logger
7825
9061
  );
@@ -8477,27 +9713,27 @@ async function logs(options, walletClient, publicClient, environmentConfig, logg
8477
9713
  }
8478
9714
 
8479
9715
  // src/client/modules/compute/app/index.ts
8480
- var CONTROLLER_ABI = (0, import_viem6.parseAbi)([
9716
+ var CONTROLLER_ABI = (0, import_viem7.parseAbi)([
8481
9717
  "function startApp(address appId)",
8482
9718
  "function stopApp(address appId)",
8483
9719
  "function terminateApp(address appId)"
8484
9720
  ]);
8485
9721
  function encodeStartAppData(appId) {
8486
- return (0, import_viem6.encodeFunctionData)({
9722
+ return (0, import_viem7.encodeFunctionData)({
8487
9723
  abi: CONTROLLER_ABI,
8488
9724
  functionName: "startApp",
8489
9725
  args: [appId]
8490
9726
  });
8491
9727
  }
8492
9728
  function encodeStopAppData(appId) {
8493
- return (0, import_viem6.encodeFunctionData)({
9729
+ return (0, import_viem7.encodeFunctionData)({
8494
9730
  abi: CONTROLLER_ABI,
8495
9731
  functionName: "stopApp",
8496
9732
  args: [appId]
8497
9733
  });
8498
9734
  }
8499
9735
  function encodeTerminateAppData(appId) {
8500
- return (0, import_viem6.encodeFunctionData)({
9736
+ return (0, import_viem7.encodeFunctionData)({
8501
9737
  abi: CONTROLLER_ABI,
8502
9738
  functionName: "terminateApp",
8503
9739
  args: [appId]
@@ -8511,7 +9747,7 @@ function createAppModule(ctx) {
8511
9747
  }
8512
9748
  const account = walletClient.account;
8513
9749
  const environment = getEnvironmentConfig(ctx.environment);
8514
- const logger = getLogger(ctx.verbose);
9750
+ const logger = ctx.logger ?? getLogger(ctx.verbose);
8515
9751
  return {
8516
9752
  async create(opts) {
8517
9753
  return createApp(opts, logger);
@@ -8623,14 +9859,15 @@ function createAppModule(ctx) {
8623
9859
  imageRef: result.imageRef
8624
9860
  };
8625
9861
  },
8626
- async watchDeployment(appId) {
9862
+ async watchDeployment(appId, opts) {
8627
9863
  return watchDeployment(
8628
9864
  appId,
8629
9865
  walletClient,
8630
9866
  publicClient,
8631
9867
  environment,
8632
9868
  logger,
8633
- skipTelemetry
9869
+ skipTelemetry,
9870
+ opts
8634
9871
  );
8635
9872
  },
8636
9873
  // Granular upgrade control
@@ -8688,8 +9925,16 @@ function createAppModule(ctx) {
8688
9925
  imageRef: result.imageRef
8689
9926
  };
8690
9927
  },
8691
- async watchUpgrade(appId) {
8692
- return watchUpgrade(appId, walletClient, publicClient, environment, logger, skipTelemetry);
9928
+ async watchUpgrade(appId, opts) {
9929
+ return watchUpgrade(
9930
+ appId,
9931
+ walletClient,
9932
+ publicClient,
9933
+ environment,
9934
+ logger,
9935
+ skipTelemetry,
9936
+ opts
9937
+ );
8693
9938
  },
8694
9939
  // Profile management
8695
9940
  async setProfile(appId, profile) {
@@ -8740,7 +9985,7 @@ function createAppModule(ctx) {
8740
9985
  },
8741
9986
  async () => {
8742
9987
  const pendingMessage = `Starting app ${appId}...`;
8743
- const data = (0, import_viem6.encodeFunctionData)({
9988
+ const data = (0, import_viem7.encodeFunctionData)({
8744
9989
  abi: CONTROLLER_ABI,
8745
9990
  functionName: "startApp",
8746
9991
  args: [appId]
@@ -8772,7 +10017,7 @@ function createAppModule(ctx) {
8772
10017
  },
8773
10018
  async () => {
8774
10019
  const pendingMessage = `Stopping app ${appId}...`;
8775
- const data = (0, import_viem6.encodeFunctionData)({
10020
+ const data = (0, import_viem7.encodeFunctionData)({
8776
10021
  abi: CONTROLLER_ABI,
8777
10022
  functionName: "stopApp",
8778
10023
  args: [appId]
@@ -8804,7 +10049,7 @@ function createAppModule(ctx) {
8804
10049
  },
8805
10050
  async () => {
8806
10051
  const pendingMessage = `Terminating app ${appId}...`;
8807
- const data = (0, import_viem6.encodeFunctionData)({
10052
+ const data = (0, import_viem7.encodeFunctionData)({
8808
10053
  abi: CONTROLLER_ABI,
8809
10054
  functionName: "terminateApp",
8810
10055
  args: [appId]
@@ -8871,7 +10116,7 @@ function createComputeModule(config) {
8871
10116
  }
8872
10117
 
8873
10118
  // src/client/modules/billing/index.ts
8874
- var import_viem7 = require("viem");
10119
+ var import_viem8 = require("viem");
8875
10120
 
8876
10121
  // src/client/common/abis/USDCCredits.json
8877
10122
  var USDCCredits_default = [
@@ -8964,7 +10209,7 @@ var ERC20_default = [
8964
10209
 
8965
10210
  // src/client/modules/billing/index.ts
8966
10211
  function createBillingModule(config) {
8967
- const { verbose = false, skipTelemetry = false, walletClient, publicClient, environment } = config;
10212
+ const { verbose = false, skipTelemetry = false, walletClient, publicClient, environment, privateKey } = config;
8968
10213
  if (!walletClient.account) {
8969
10214
  throw new Error("WalletClient must have an account attached");
8970
10215
  }
@@ -8973,35 +10218,69 @@ function createBillingModule(config) {
8973
10218
  const billingEnvConfig = getBillingEnvironmentConfig(getBuildType());
8974
10219
  const billingApi = new BillingApiClient(billingEnvConfig, walletClient, { verbose });
8975
10220
  const environmentConfig = getEnvironmentConfig(environment);
8976
- const usdcCreditsAddress = environmentConfig.usdcCreditsAddress;
8977
- if (!usdcCreditsAddress) {
10221
+ if (!environmentConfig.usdcCreditsAddress) {
8978
10222
  throw new Error(`USDCCredits contract address not configured for environment "${environment}"`);
8979
10223
  }
10224
+ const usdcCreditsAddress = environmentConfig.usdcCreditsAddress;
10225
+ const baseUsdcCreditsAddress = environmentConfig.baseUsdcCreditsAddress;
10226
+ const baseRPCURL = environmentConfig.baseRPCURL;
10227
+ function resolveChainConfig(chain) {
10228
+ if (chain === "base") {
10229
+ if (!baseUsdcCreditsAddress || !baseRPCURL) {
10230
+ throw new Error(`Base chain not configured for environment "${environment}"`);
10231
+ }
10232
+ if (!privateKey) {
10233
+ throw new Error("Private key required for Base chain transactions");
10234
+ }
10235
+ const baseClients = createClients({
10236
+ privateKey,
10237
+ rpcUrl: baseRPCURL,
10238
+ chainId: BigInt(BASE_SEPOLIA_CHAIN_ID)
10239
+ });
10240
+ return {
10241
+ pub: baseClients.publicClient,
10242
+ wallet: baseClients.walletClient,
10243
+ creditsAddress: baseUsdcCreditsAddress,
10244
+ envConfig: {
10245
+ ...environmentConfig,
10246
+ chainID: BigInt(BASE_SEPOLIA_CHAIN_ID),
10247
+ defaultRPCURL: baseRPCURL
10248
+ }
10249
+ };
10250
+ }
10251
+ return {
10252
+ pub: publicClient,
10253
+ wallet: walletClient,
10254
+ creditsAddress: usdcCreditsAddress,
10255
+ envConfig: environmentConfig
10256
+ };
10257
+ }
8980
10258
  const module2 = {
8981
10259
  address,
8982
- async getTopUpInfo() {
8983
- const usdcAddress = await publicClient.readContract({
8984
- address: usdcCreditsAddress,
10260
+ async getTopUpInfo(opts) {
10261
+ const { pub, creditsAddress } = resolveChainConfig(opts?.chain);
10262
+ const usdcAddress = await pub.readContract({
10263
+ address: creditsAddress,
8985
10264
  abi: USDCCredits_default,
8986
10265
  functionName: "usdc"
8987
10266
  });
8988
10267
  const [minimumPurchase, usdcBalance, currentAllowance] = await Promise.all([
8989
- publicClient.readContract({
8990
- address: usdcCreditsAddress,
10268
+ pub.readContract({
10269
+ address: creditsAddress,
8991
10270
  abi: USDCCredits_default,
8992
10271
  functionName: "minimumPurchase"
8993
10272
  }),
8994
- publicClient.readContract({
10273
+ pub.readContract({
8995
10274
  address: usdcAddress,
8996
10275
  abi: ERC20_default,
8997
10276
  functionName: "balanceOf",
8998
10277
  args: [address]
8999
10278
  }),
9000
- publicClient.readContract({
10279
+ pub.readContract({
9001
10280
  address: usdcAddress,
9002
10281
  abi: ERC20_default,
9003
10282
  functionName: "allowance",
9004
- args: [address, usdcCreditsAddress]
10283
+ args: [address, creditsAddress]
9005
10284
  })
9006
10285
  ]);
9007
10286
  return { usdcAddress, minimumPurchase, usdcBalance, currentAllowance };
@@ -9011,27 +10290,28 @@ function createBillingModule(config) {
9011
10290
  {
9012
10291
  functionName: "topUp",
9013
10292
  skipTelemetry,
9014
- properties: { amount: opts.amount.toString() }
10293
+ properties: { amount: opts.amount.toString(), chain: opts.chain || "ethereum" }
9015
10294
  },
9016
10295
  async () => {
9017
10296
  const targetAccount = opts.account ?? address;
9018
- const { usdcAddress, currentAllowance } = await module2.getTopUpInfo();
10297
+ const { pub, wallet, creditsAddress, envConfig } = resolveChainConfig(opts.chain);
10298
+ const { usdcAddress, currentAllowance } = await module2.getTopUpInfo({ chain: opts.chain });
9019
10299
  const executions = [];
9020
10300
  if (currentAllowance < opts.amount) {
9021
10301
  executions.push({
9022
10302
  target: usdcAddress,
9023
10303
  value: 0n,
9024
- callData: (0, import_viem7.encodeFunctionData)({
10304
+ callData: (0, import_viem8.encodeFunctionData)({
9025
10305
  abi: ERC20_default,
9026
10306
  functionName: "approve",
9027
- args: [usdcCreditsAddress, opts.amount]
10307
+ args: [creditsAddress, opts.amount]
9028
10308
  })
9029
10309
  });
9030
10310
  }
9031
10311
  executions.push({
9032
- target: usdcCreditsAddress,
10312
+ target: creditsAddress,
9033
10313
  value: 0n,
9034
- callData: (0, import_viem7.encodeFunctionData)({
10314
+ callData: (0, import_viem8.encodeFunctionData)({
9035
10315
  abi: USDCCredits_default,
9036
10316
  functionName: "purchaseCreditsFor",
9037
10317
  args: [opts.amount, targetAccount]
@@ -9039,9 +10319,9 @@ function createBillingModule(config) {
9039
10319
  });
9040
10320
  const txHash = await executeBatch(
9041
10321
  {
9042
- walletClient,
9043
- publicClient,
9044
- environmentConfig,
10322
+ walletClient: wallet,
10323
+ publicClient: pub,
10324
+ environmentConfig: envConfig,
9045
10325
  executions,
9046
10326
  pendingMessage: "Submitting credit purchase..."
9047
10327
  },
@@ -9141,6 +10421,12 @@ function createBillingModule(config) {
9141
10421
  },
9142
10422
  async purchaseCredits(amountCents, paymentMethodId) {
9143
10423
  return billingApi.purchaseCredits(amountCents, paymentMethodId);
10424
+ },
10425
+ hasBaseSupport() {
10426
+ return !!baseUsdcCreditsAddress && !!baseRPCURL;
10427
+ },
10428
+ async redeemCoupon(code) {
10429
+ return billingApi.redeemCoupon(code);
9144
10430
  }
9145
10431
  };
9146
10432
  return module2;
@@ -9576,6 +10862,44 @@ function transformVerifyResult(raw) {
9576
10862
  };
9577
10863
  }
9578
10864
 
10865
+ // src/client/modules/admin/index.ts
10866
+ function createAdminModule(config) {
10867
+ const { verbose = false, walletClient } = config;
10868
+ if (!walletClient.account) {
10869
+ throw new Error("WalletClient must have an account attached");
10870
+ }
10871
+ const address = walletClient.account.address;
10872
+ const billingEnvConfig = getBillingEnvironmentConfig(getBuildType());
10873
+ const billingApi = new BillingApiClient(billingEnvConfig, walletClient, { verbose });
10874
+ return {
10875
+ address,
10876
+ async createCoupon(amountCents) {
10877
+ return billingApi.createCoupon(amountCents);
10878
+ },
10879
+ async listCoupons(opts) {
10880
+ return billingApi.listCoupons(opts);
10881
+ },
10882
+ async getCoupon(id) {
10883
+ return billingApi.getCoupon(id);
10884
+ },
10885
+ async deactivateCoupon(id) {
10886
+ return billingApi.deactivateCoupon(id);
10887
+ },
10888
+ async redeemCouponForUser(id, userAddress) {
10889
+ return billingApi.redeemCouponForUser(id, userAddress);
10890
+ },
10891
+ async addAdmin(adminAddress) {
10892
+ return billingApi.addAdmin(adminAddress);
10893
+ },
10894
+ async removeAdmin(adminAddress) {
10895
+ return billingApi.removeAdmin(adminAddress);
10896
+ },
10897
+ async listAdmins() {
10898
+ return billingApi.listAdmins();
10899
+ }
10900
+ };
10901
+ }
10902
+
9579
10903
  // src/client/common/auth/keyring.ts
9580
10904
  var import_keyring = require("@napi-rs/keyring");
9581
10905
  var import_accounts2 = require("viem/accounts");
@@ -10114,14 +11438,17 @@ var JwtProvider = class {
10114
11438
  0 && (module.exports = {
10115
11439
  AttestClient,
10116
11440
  AuthRequiredError,
11441
+ BASE_SEPOLIA_CHAIN_ID,
10117
11442
  BUILD_STATUS,
10118
11443
  BadRequestError,
10119
11444
  BillingApiClient,
10120
11445
  BuildError,
10121
11446
  BuildFailedError,
10122
11447
  ConflictError,
11448
+ EMPTY_CONTAINER_POLICY,
10123
11449
  ERC20ABI,
10124
11450
  ForbiddenError,
11451
+ InsufficientGasError,
10125
11452
  JwtProvider,
10126
11453
  NoopClient,
10127
11454
  NotFoundError,
@@ -10131,14 +11458,18 @@ var JwtProvider = class {
10131
11458
  TimeoutError,
10132
11459
  USDCCreditsABI,
10133
11460
  UserApiClient,
11461
+ WATCH_DEFAULT_TIMEOUT_SECONDS,
11462
+ WatchTimeoutError,
10134
11463
  addHexPrefix,
10135
11464
  addMetric,
10136
11465
  addMetricWithDimensions,
11466
+ assertSufficientGas,
10137
11467
  assertValidFilePath,
10138
11468
  assertValidImageReference,
10139
11469
  assertValidPrivateKey,
10140
11470
  calculateAppID,
10141
11471
  checkERC7702Delegation,
11472
+ createAdminModule,
10142
11473
  createApp,
10143
11474
  createAppEnvironment,
10144
11475
  createBillingModule,