Repository: pulumi/esc-sdk
Pull request #77: Adding same functionality as CLI
Author: login: IaroslavTitov
Created: 2025-03-14T23:13:44Z | Merged: 2025-03-18T20:24:47Z

### Summary
- Fixes https://github.com/pulumi/esc-sdk/issues/52
- Adding a dependency on ESC to use ESC workspaces/accounts (had to bump go to 1.21 for this)
- Adding default helper methods to easily setup ESC SDK (all languages now have ability to grab env vars by default, golang will also grab accounts if loggen in via CLI)
- Added https://github.com/pulumi/esc-sdk/issues/78 and https://github.com/pulumi/esc-sdk/issues/79 to add full loading of credentials in other languages one day

### Testing
- Adjusting existing test to use easy new constructors
- Adding tests to verify grabbing env vars works
- Manually tested and verified grabbing access tokens and backend URLs works using accounts logged in ESC CLI


Diff:
diff --git a/.github/workflows/stage-lint.yml b/.github/workflows/stage-lint.yml
index d1bce15..18b95d0 100644
--- a/.github/workflows/stage-lint.yml
+++ b/.github/workflows/stage-lint.yml
@@ -14,10 +14,10 @@ jobs:
     steps:
       - name: Checkout Repo
         uses: actions/checkout@v2
-      - name: Set up Go 1.20
+      - name: Set up Go 1.21
         uses: actions/setup-go@v3
         with:
-          go-version: 1.20.x
+          go-version: 1.21.x
       - run: cd sdk && go mod tidy
       - name: Fail if go mod not tidy
         run: |
@@ -66,10 +66,10 @@ jobs:
         uses: jaxxstorm/action-install-gh-release@v1.5.0
         with:
           repo: pulumi/pulumictl
-      - name: Set up Go 1.20
+      - name: Set up Go 1.21
         uses: actions/setup-go@v3
         with:
-          go-version: 1.20.x
+          go-version: 1.21.x
       - name: Lint
         run: make lint-copyright
   check-sdk-generation-clean:
diff --git a/.github/workflows/stage-test.yml b/.github/workflows/stage-test.yml
index fc54d2b..a81b2dd 100644
--- a/.github/workflows/stage-test.yml
+++ b/.github/workflows/stage-test.yml
@@ -49,7 +49,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        go-version: [1.20.x]
+        go-version: [1.21.x]
         go-stable: [true]
   test_typescript:
     name: Test Typescript
diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md
index 461ff6e..14cbe53 100644
--- a/CHANGELOG_PENDING.md
+++ b/CHANGELOG_PENDING.md
@@ -1,5 +1,10 @@
 ### Improvements
 
+- Adding default authorization methods for parity with CLI
+  - All SDK now automatically read in configuration environment variables
+  - Go SDK also automatically picks up configuration from CLI Pulumi accounts
+  [#76](https://github.com/pulumi/esc-sdk/pull/76)
+
 ### Bug Fixes
 
 ### Breaking changes
diff --git a/sdk/go.mod b/sdk/go.mod
index e87577b..bcf1ec5 100644
--- a/sdk/go.mod
+++ b/sdk/go.mod
@@ -1,13 +1,89 @@
 module github.com/pulumi/esc-sdk/sdk
 
-go 1.18
+go 1.21
 
-require github.com/stretchr/testify v1.8.4
+require (
+	github.com/pulumi/esc v0.13.1-0.20250314190530-79238870da74
+	github.com/stretchr/testify v1.10.0
+	gopkg.in/ghodss/yaml.v1 v1.0.0
+)
 
 require (
+	dario.cat/mergo v1.0.0 // indirect
+	github.com/BurntSushi/toml v1.2.1 // indirect
+	github.com/Microsoft/go-winio v0.6.1 // indirect
+	github.com/ProtonMail/go-crypto v1.1.3 // indirect
+	github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
+	github.com/agext/levenshtein v1.2.3 // indirect
+	github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
+	github.com/atotto/clipboard v0.1.4 // indirect
+	github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
+	github.com/blang/semver v3.5.1+incompatible // indirect
+	github.com/charmbracelet/bubbles v0.16.1 // indirect
+	github.com/charmbracelet/bubbletea v0.25.0 // indirect
+	github.com/charmbracelet/lipgloss v0.7.1 // indirect
+	github.com/cheggaaa/pb v1.0.29 // indirect
+	github.com/cloudflare/circl v1.3.7 // indirect
+	github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
+	github.com/cyphar/filepath-securejoin v0.2.5 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/djherbis/times v1.5.0 // indirect
+	github.com/emirpasic/gods v1.18.1 // indirect
+	github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
+	github.com/go-git/go-billy/v5 v5.6.0 // indirect
+	github.com/go-git/go-git/v5 v5.13.0 // indirect
+	github.com/gogo/protobuf v1.3.2 // indirect
+	github.com/golang/glog v1.2.0 // indirect
+	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
+	github.com/hashicorp/errwrap v1.1.0 // indirect
+	github.com/hashicorp/go-multierror v1.1.1 // indirect
+	github.com/hashicorp/hcl/v2 v2.17.0 // indirect
+	github.com/inconshreveable/mousetrap v1.1.0 // indirect
+	github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
+	github.com/kevinburke/ssh_config v1.2.0 // indirect
+	github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
+	github.com/mattn/go-isatty v0.0.20 // indirect
+	github.com/mattn/go-localereader v0.0.1 // indirect
+	github.com/mattn/go-runewidth v0.0.15 // indirect
+	github.com/mitchellh/go-ps v1.0.0 // indirect
+	github.com/mitchellh/go-wordwrap v1.0.1 // indirect
+	github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
+	github.com/muesli/cancelreader v0.2.2 // indirect
+	github.com/muesli/reflow v0.3.0 // indirect
+	github.com/muesli/termenv v0.15.2 // indirect
+	github.com/opentracing/basictracer-go v1.1.0 // indirect
+	github.com/opentracing/opentracing-go v1.2.0 // indirect
+	github.com/pgavlin/fx v0.1.6 // indirect
+	github.com/pjbgf/sha1cd v0.3.0 // indirect
+	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
-	gopkg.in/ghodss/yaml.v1 v1.0.0
+	github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 // indirect
+	github.com/pulumi/pulumi/sdk/v3 v3.137.0 // indirect
+	github.com/rivo/uniseg v0.4.4 // indirect
+	github.com/rogpeppe/go-internal v1.12.0 // indirect
+	github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect
+	github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 // indirect
+	github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
+	github.com/skeema/knownhosts v1.3.0 // indirect
+	github.com/spf13/cobra v1.8.0 // indirect
+	github.com/spf13/pflag v1.0.5 // indirect
+	github.com/texttheater/golang-levenshtein v1.0.1 // indirect
+	github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect
+	github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
+	github.com/xanzy/ssh-agent v0.3.3 // indirect
+	github.com/zclconf/go-cty v1.13.2 // indirect
+	go.uber.org/atomic v1.9.0 // indirect
+	golang.org/x/crypto v0.31.0 // indirect
+	golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
+	golang.org/x/mod v0.19.0 // indirect
+	golang.org/x/net v0.33.0 // indirect
+	golang.org/x/sync v0.10.0 // indirect
+	golang.org/x/sys v0.28.0 // indirect
+	golang.org/x/term v0.27.0 // indirect
+	golang.org/x/text v0.21.0 // indirect
+	golang.org/x/tools v0.23.0 // indirect
+	gopkg.in/warnings.v0 v0.1.2 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
+	lukechampine.com/frand v1.4.2 // indirect
 )
diff --git a/sdk/go.sum b/sdk/go.sum
index a6d64ea..1f0b3b0 100644
--- a/sdk/go.sum
+++ b/sdk/go.sum
@@ -1,14 +1,268 @@
+dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
+dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
+github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
+github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM=
+github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
+github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
+github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
+github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
+github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk=
+github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
+github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
+github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
+github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
+github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
+github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
+github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
+github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
+github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
+github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
+github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
+github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
+github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
+github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
+github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
+github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5WdeuYSY=
+github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc=
+github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
+github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
+github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E=
+github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c=
+github.com/cheggaaa/pb v1.0.29 h1:FckUN5ngEk2LpvuG0fw1GEFx6LtyY2pWI/Z2QgCnEYo=
+github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30=
+github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
+github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
+github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
+github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
+github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/cyphar/filepath-securejoin v0.2.5 h1:6iR5tXJ/e6tJZzzdMc1km3Sa7RRIVBKAK32O2s7AYfo=
+github.com/cyphar/filepath-securejoin v0.2.5/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/djherbis/times v1.5.0 h1:79myA211VwPhFTqUk8xehWrsEO+zcIZj0zT8mXPVARU=
+github.com/djherbis/times v1.5.0/go.mod h1:5q7FDLvbNg1L/KaBmPcWlVR9NmoKo3+ucqUA3ijQhA0=
+github.com/elazarl/goproxy v1.2.1 h1:njjgvO6cRG9rIqN2ebkqy6cQz2Njkx7Fsfv/zIZqgug=
+github.com/elazarl/goproxy v1.2.1/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64=
+github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
+github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
+github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
+github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
+github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
+github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
+github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
+github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
+github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8=
+github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM=
+github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
+github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
+github.com/go-git/go-git/v5 v5.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi5E=
+github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkvVkiXRR/zw=
+github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
+github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
+github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
+github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
+github.com/hashicorp/hcl/v2 v2.17.0 h1:z1XvSUyXd1HP10U4lrLg5e0JMVz6CPaJvAgxM0KNZVY=
+github.com/hashicorp/hcl/v2 v2.17.0/go.mod h1:gJyW2PTShkJqQBKpAmPO3yxMxIuoXkOF2TpqXzrQyx4=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
+github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
+github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
+github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
+github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
+github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
+github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
+github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
+github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
+github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
+github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
+github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
+github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
+github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
+github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
+github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
+github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
+github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
+github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
+github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
+github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
+github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
+github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
+github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
+github.com/opentracing/basictracer-go v1.1.0 h1:Oa1fTSBvAl8pa3U+IJYqrKm0NALwH9OsgwOqDv4xJW0=
+github.com/opentracing/basictracer-go v1.1.0/go.mod h1:V2HZueSJEp879yv285Aap1BS69fQMD+MNP1mRs6mBQc=
+github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
+github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
+github.com/pgavlin/fx v0.1.6 h1:r9jEg69DhNoCd3Xh0+5mIbdbS3PqWrVWujkY76MFRTU=
+github.com/pgavlin/fx v0.1.6/go.mod h1:KWZJ6fqBBSh8GxHYqwYCf3rYE7Gp2p0N8tJp8xv9u9M=
+github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
+github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
-github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 h1:vkHw5I/plNdTr435cARxCW6q9gc0S/Yxz7Mkd38pOb0=
+github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231/go.mod h1:murToZ2N9hNJzewjHBgfFdXhZKjY3z5cYC1VXk+lbFE=
+github.com/pulumi/esc v0.13.1-0.20250314190530-79238870da74 h1:zGMsnl+3caSMyRdRJc4eHj0Czag2+Y+7KE982cPl1vM=
+github.com/pulumi/esc v0.13.1-0.20250314190530-79238870da74/go.mod h1:IIQo6W6Uzajt6f1RW4QvNxIRDlbK3TNQysnrwBHNo3U=
+github.com/pulumi/pulumi/sdk/v3 v3.137.0 h1:bxhYpOY7Z4xt+VmezEpHuhjpOekkaMqOjzxFg/1OhCw=
+github.com/pulumi/pulumi/sdk/v3 v3.137.0/go.mod h1:PvKsX88co8XuwuPdzolMvew5lZV+4JmZfkeSjj7A6dI=
+github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
+github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
+github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI=
+github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs=
+github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 h1:TToq11gyfNlrMFZiYujSekIsPd9AmsA2Bj/iv+s4JHE=
+github.com/santhosh-tekuri/jsonschema/v5 v5.0.0/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0=
+github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
+github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
+github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
+github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
+github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
+github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/texttheater/golang-levenshtein v1.0.1 h1:+cRNoVrfiwufQPhoMzB6N0Yf/Mqajr6t1lOv8GyGE2U=
+github.com/texttheater/golang-levenshtein v1.0.1/go.mod h1:PYAKrbF5sAiq9wd+H82hs7gNaen0CplQ9uvm6+enD/8=
+github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
+github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
+github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
+github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
+github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
+github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/zclconf/go-cty v1.13.2 h1:4GvrUxe/QUDYuJKAav4EYqdM47/kZa672LwmXFmEKT0=
+github.com/zclconf/go-cty v1.13.2/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0=
+go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
+go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
+golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
+golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
+golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
+golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
+golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
+golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
+golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
+golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
+golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
+golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/ghodss/yaml.v1 v1.0.0 h1:JlY4R6oVz+ZSvcDhVfNQ/k/8Xo6yb2s1PBhslPZPX4c=
 gopkg.in/ghodss/yaml.v1 v1.0.0/go.mod h1:HDvRMPQLqycKPs9nWLuzZWxsxRzISLCRORiDpBUOMqg=
+gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
+gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+lukechampine.com/frand v1.4.2 h1:RzFIpOvkMXuPMBb9maa4ND4wjBn71E1Jpf8BzJHMaVw=
+lukechampine.com/frand v1.4.2/go.mod h1:4S/TM2ZgrKejMcKMbeLjISpJMO+/eZ1zu3vYX9dtj3s=
+pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA=
+pgregory.net/rapid v0.5.5/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
diff --git a/sdk/go/api_esc_extensions.go b/sdk/go/api_esc_extensions.go
index 71b3660..a4f1f4c 100644
--- a/sdk/go/api_esc_extensions.go
+++ b/sdk/go/api_esc_extensions.go
@@ -5,8 +5,12 @@ package esc_sdk
 import (
 	"context"
 	"errors"
+	"fmt"
 	"io"
+	"net/url"
+	"os"
 
+	esc_workspace "github.com/pulumi/esc/cmd/esc/cli/workspace"
 	"gopkg.in/ghodss/yaml.v1"
 )
 
@@ -29,6 +33,30 @@ func NewAuthContext(accessToken string) context.Context {
 	)
 }
 
+// DefaultAuthContext creates a new context, retrieving Pulumi Access Token
+// from either PULUMI_ACCESS_TOKEN environment variable, or from
+// currently logged in account in Pulumi CLI or ESC CLI
+//
+// This context can be used to authenticate requests to the ESC API.
+func NewDefaultAuthContext() (context.Context, error) {
+	accessToken := os.Getenv("PULUMI_ACCESS_TOKEN")
+	if accessToken != "" {
+		return NewAuthContext(accessToken), nil
+	}
+
+	workspace := esc_workspace.New(esc_workspace.DefaultFS(), esc_workspace.DefaultPulumiWorkspace())
+	account, _, err := workspace.GetCurrentAccount(false)
+	if err != nil {
+		return nil, fmt.Errorf("Error grabbing current account: %w", err)
+	}
+	if account != nil {
+		return NewAuthContext(account.AccessToken), nil
+	}
+
+	return nil, errors.New("No default Pulumi Access Token found. Either export PULUMI_ACCESS_TOKEN " +
+		"environment variable, or login using Pulumi or ESC CLI.")
+}
+
 // NewClient creates a new ESC client with the given configuration.
 func NewClient(cfg *Configuration) *EscClient {
 	client := &EscClient{rawClient: NewRawAPIClient(cfg)}
@@ -36,6 +64,63 @@ func NewClient(cfg *Configuration) *EscClient {
 	return client
 }
 
+// NewCustomBackendConfiguration creates a new Configuration object,
+// but replaces default API endpoint with a given custom backend URL
+func NewCustomBackendConfiguration(customBackendURL url.URL) (*Configuration, error) {
+	appendedUrl, err := url.Parse(fmt.Sprintf("%s://%s/api/esc", customBackendURL.Scheme, customBackendURL.Hostname()))
+	if err != nil {
+		return nil, fmt.Errorf("failed to normalize backend url: ")
+	}
+	cfg := &Configuration{
+		DefaultHeader: make(map[string]string),
+		UserAgent:     "esc-sdk",
+		Debug:         false,
+		Servers: ServerConfigurations{
+			{
+				URL:         appendedUrl.String(),
+				Description: "Pulumi Cloud Custom Backend API",
+			},
+		},
+		OperationServers: map[string]ServerConfigurations{},
+	}
+	return cfg, nil
+}
+
+// NewDefaultClient creates a new ESC client with default configuration.
+// Backend URL is automatically detected from either PULUMI_BACKEND_URL environment variable
+// or currently logged in account in Pulumi CLI or ESC CLI
+func NewDefaultClient() (*EscClient, error) {
+	workspace := esc_workspace.New(esc_workspace.DefaultFS(), esc_workspace.DefaultPulumiWorkspace())
+	account, _, err := workspace.GetCurrentAccount(false)
+	if err != nil {
+		return nil, fmt.Errorf("Error grabbing current account: %w", err)
+	}
+	customBackendURL := workspace.GetCurrentCloudURL(account)
+	parsedUrl, err := url.Parse(customBackendURL)
+	if err != nil {
+		return nil, fmt.Errorf("Error parsing custom backend url: %w", err)
+	}
+	config, err := NewCustomBackendConfiguration(*parsedUrl)
+	if err != nil {
+		return nil, err
+	}
+	return NewClient(config), nil
+}
+
+// This is the easiest way to use ESC SDK. DefaultLogin grabs default client
+// and default authorization context, so you can start using SDK right away
+func DefaultLogin() (context.Context, *EscClient, error) {
+	client, err := NewDefaultClient()
+	if err != nil {
+		return nil, nil, err
+	}
+	context, err := NewDefaultAuthContext()
+	if err != nil {
+		return nil, nil, err
+	}
+	return context, client, nil
+}
+
 // ListEnvironments lists all environments in the given organization.
 // If a continuation token is provided, the list will start from that token.
 func (c *EscClient) ListEnvironments(ctx context.Context, org string, continuationToken *string) (*OrgEnvironments, error) {
diff --git a/sdk/go/api_esc_extensions_test.go b/sdk/go/api_esc_extensions_test.go
new file mode 100644
index 0000000..556b6fd
--- /dev/null
+++ b/sdk/go/api_esc_extensions_test.go
@@ -0,0 +1,49 @@
+// Copyright 2025, Pulumi Corporation.  All rights reserved.
+
+/*
+ESC (Environments, Secrets, Config) API
+
+Testing EscAPIService
+
+*/
+
+package esc_sdk
+
+import (
+	"os"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func Test_EscClientLogin(t *testing.T) {
+	t.Run("verify default auth context picks up PULUMI_ACCESS_TOKEN variable", func(t *testing.T) {
+		beforeTest := os.Getenv("PULUMI_ACCESS_TOKEN")
+		os.Setenv("PULUMI_ACCESS_TOKEN", "FAKE_TOKEN")
+
+		authContext, err := NewDefaultAuthContext()
+		require.NoError(t, err)
+
+		auth, ok := authContext.Value(ContextAPIKeys).(map[string]APIKey)
+		require.True(t, ok)
+		token, ok := auth["Authorization"]
+		require.True(t, ok)
+		require.Equal(t, "FAKE_TOKEN", token.Key)
+
+		os.Setenv("PULUMI_ACCESS_TOKEN", beforeTest)
+	})
+
+	t.Run("verify default client picks up PULUMI_BACKEND_URL by default", func(t *testing.T) {
+		beforeTest := os.Getenv("PULUMI_BACKEND_URL")
+		os.Setenv("PULUMI_BACKEND_URL", "https://api.moolumi.com")
+
+		client, err := NewDefaultClient()
+		require.NoError(t, err)
+
+		url, err := client.rawClient.cfg.ServerURL(0, make(map[string]string))
+		require.NoError(t, err)
+		require.Equal(t, "https://api.moolumi.com/api/esc", url)
+
+		os.Setenv("PULUMI_BACKEND_URL", beforeTest)
+	})
+}
diff --git a/sdk/go/api_esc_test.go b/sdk/go/api_esc_test.go
index f50ad32..afa1209 100644
--- a/sdk/go/api_esc_test.go
+++ b/sdk/go/api_esc_test.go
@@ -24,25 +24,15 @@ const PROJECT_NAME = "sdk-go-test"
 const ENV_PREFIX = "env-"
 
 func Test_EscClient(t *testing.T) {
-	configuration := NewConfiguration()
-
-	accessToken := os.Getenv("PULUMI_ACCESS_TOKEN")
-	require.NotEmpty(t, accessToken, "PULUMI_ACCESS_TOKEN must be set")
 	orgName := os.Getenv("PULUMI_ORG")
 	require.NotEmpty(t, orgName, "PULUMI_ORG must be set")
-
-	basePath := os.Getenv("PULUMI_BACKEND_URL")
-	if basePath != "" {
-		configuration.Servers[0].URL = basePath
-	}
-
-	apiClient := NewClient(configuration)
-	auth := NewAuthContext(accessToken)
+	auth, apiClient, err := DefaultLogin()
+	require.NoError(t, err)
 
 	removeAllGoTestEnvs(t, apiClient, auth, orgName)
 
 	baseEnvName := "base-" + time.Now().Format("20060102150405")
-	err := apiClient.CreateEnvironment(auth, orgName, PROJECT_NAME, baseEnvName)
+	err = apiClient.CreateEnvironment(auth, orgName, PROJECT_NAME, baseEnvName)
 	require.Nil(t, err)
 	t.Cleanup(func() {
 		err := apiClient.DeleteEnvironment(auth, orgName, PROJECT_NAME, baseEnvName)
diff --git a/sdk/python/pulumi_esc_sdk/esc_client.py b/sdk/python/pulumi_esc_sdk/esc_client.py
index 8aa7c50..59543e9 100644
--- a/sdk/python/pulumi_esc_sdk/esc_client.py
+++ b/sdk/python/pulumi_esc_sdk/esc_client.py
@@ -10,6 +10,8 @@
 from typing import Mapping, Any, List
 import inspect
 import yaml
+import os
+from urllib.parse import urlparse, urlunparse
 
 
 class EscClient:
@@ -23,6 +25,7 @@ class EscClient:
     def __init__(self, configuration: configuration.Configuration) -> None:
         """Constructor
         """
+        configuration.host = append_esc_to_url(configuration.host)
         self.esc_api = api.EscApi(api_client.ApiClient(configuration))
 
     def list_environments(self, org_name: str, continuation_token: str = None) -> models.OrgEnvironments:
@@ -407,3 +410,59 @@ def convertPropertyToValue(property: Any) -> Any:
 
 def isObject(obj):
     return inspect.isclass(obj) or isinstance(obj, dict)
+
+def append_esc_to_url(custom_backend_url_str):
+    if custom_backend_url_str is None:
+        return None
+    try:
+        custom_backend_url = urlparse(custom_backend_url_str)
+        appended_url = urlunparse((
+            str(custom_backend_url.scheme),
+            str(custom_backend_url.netloc),
+            "/api/esc",
+            None,  # path
+            None,  # query
+            None,  # fragment
+        ))
+        return appended_url
+    except Exception as e:
+        print(f"Error parsing URL: {e}")
+        return None
+
+def default_config(host=None,
+             access_token=None,
+             server_index=None, server_variables=None,
+             server_operation_index=None, server_operation_variables=None,
+             ssl_ca_cert=None,
+             ) -> configuration.Configuration:
+    """Creates default configuration for EscClient.
+    """
+    if not access_token:
+        access_token = os.getenv("PULUMI_ACCESS_TOKEN")
+    if not host:
+        host = os.getenv("PULUMI_BACKEND_URL")
+    return configuration.Configuration(
+        host=host,
+        access_token=access_token,
+        server_index=server_index,
+        server_variables=server_variables,
+        server_operation_index=server_operation_index,
+        server_operation_variables=server_operation_variables,
+        ssl_ca_cert=ssl_ca_cert)
+
+def default_client(host=None,
+             access_token=None,
+             server_index=None, server_variables=None,
+             server_operation_index=None, server_operation_variables=None,
+             ssl_ca_cert=None,
+             ) -> configuration.Configuration:
+    """Creates default EscClient.
+    """        
+    return EscClient(default_config(
+        host=host,
+        access_token=access_token,
+        server_index=server_index,
+        server_variables=server_variables,
+        server_operation_index=server_operation_index,
+        server_operation_variables=server_operation_variables,
+        ssl_ca_cert=ssl_ca_cert))
\ No newline at end of file
diff --git a/sdk/python/test/test_esc_api.py b/sdk/python/test/test_esc_api.py
index dcf7e48..d3f5e8b 100644
--- a/sdk/python/test/test_esc_api.py
+++ b/sdk/python/test/test_esc_api.py
@@ -16,16 +16,10 @@ class TestEscApi(unittest.TestCase):
     """EscApi unit test stubs"""
 
     def setUp(self) -> None:
-        self.accessToken = os.getenv("PULUMI_ACCESS_TOKEN")
-        self.assertIsNotNone(self.accessToken, "PULUMI_ACCESS_TOKEN must be set")
-
-        self.orgName = os.getenv("PULUMI_ORG")
+        self.orgName = "pulumi"
         self.assertIsNotNone(self.orgName, "PULUMI_ORG must be set")
 
-        self.host = os.getenv("PULUMI_BACKEND_URL")
-
-        configuration = esc.Configuration(access_token=self.accessToken, host=self.host)
-        self.client = esc.EscClient(configuration)
+        self.client = esc.esc_client.default_client()
 
         self.remove_all_python_test_envs()
 
diff --git a/sdk/typescript/esc/index.ts b/sdk/typescript/esc/index.ts
index 02f3daa..857b4ca 100644
--- a/sdk/typescript/esc/index.ts
+++ b/sdk/typescript/esc/index.ts
@@ -79,6 +79,13 @@ export class EscApi {
     rawApi: EscRawApi;
     config: Configuration;
     constructor(config: Configuration) {
+        // Normalize backend url
+        if (config.basePath) {
+            const url = new URL(config.basePath)
+            const appendedUrl = new URL(`/api/esc`, `${url.protocol}//${url.hostname}`);
+            config.basePath = appendedUrl.toString();
+        }
+
         this.config = config;
         this.rawApi = new EscRawApi(config);
     }
@@ -814,3 +821,16 @@ function convertPropertyToValue(property: any): any {
 
     return value;
 }
+
+export function DefaultConfiguration(config?: Configuration): Configuration {
+    if (!config) {
+        config = new Configuration()
+    }
+    config.accessToken ??= process.env.PULUMI_ACCESS_TOKEN;
+    config.basePath ??= process.env.PULUMI_BACKEND_URL;
+    return config;
+}
+
+export function DefaultClient(config?: Configuration) {
+    return new EscApi(DefaultConfiguration(config));
+}
\ No newline at end of file
diff --git a/sdk/typescript/test/client.spec.ts b/sdk/typescript/test/client.spec.ts
index 630aa3c..a7bec8c 100644
--- a/sdk/typescript/test/client.spec.ts
+++ b/sdk/typescript/test/client.spec.ts
@@ -19,18 +19,11 @@ import * as esc from "../esc";
 const PROJECT_NAME = "sdk-ts-test";
 const ENV_PREFIX = "env";
 describe("ESC", async () => {
-    const PULUMI_ACCESS_TOKEN = process.env.PULUMI_ACCESS_TOKEN;
     const PULUMI_ORG = process.env.PULUMI_ORG;
-    const PULUMI_BASE_PATH = process.env.PULUMI_BACKEND_URL;
-    if (!PULUMI_ACCESS_TOKEN) {
-        throw new Error("PULUMI_ACCESS_TOKEN not set");
-    }
     if (!PULUMI_ORG) {
         throw new Error("PULUMI_ORG not set");
     }
-    const basePathConfig = PULUMI_BASE_PATH ? { basePath: PULUMI_BASE_PATH } : {};
-    const config = new esc.Configuration({ accessToken: PULUMI_ACCESS_TOKEN, ...basePathConfig });
-    const client = new esc.EscApi(config);
+    const client = esc.DefaultClient();
     const baseEnvName = `${ENV_PREFIX}-base-${Date.now()}`;
 
     before(async () => {


Reviews:
user: login: dschaller
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-17T17:27:46Z
user: login: dschaller
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-17T17:31:46Z
user: login: IaroslavTitov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T18:07:54Z
user: login: seanyeh
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T19:31:16Z
user: login: seanyeh
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T19:32:43Z
user: login: dschaller
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-17T20:48:17Z
user: login: dschaller
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-17T20:52:01Z
user: login: dschaller
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-17T20:52:16Z
user: login: IaroslavTitov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T20:56:43Z
user: login: seanyeh
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T21:05:00Z
user: login: dschaller
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-17T23:26:59Z
user: login: dschaller
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-17T23:28:09Z
user: login: dschaller
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-17T23:30:26Z
user: login: IaroslavTitov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T16:04:49Z
user: login: IaroslavTitov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T16:08:09Z
user: login: IaroslavTitov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T16:11:01Z
user: login: seanyeh
body: looks good to me. I would make sure to test the different config options in each language
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T20:06:20Z
user: login: seanyeh
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T20:07:51Z
user: login: IaroslavTitov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-18T20:11:20Z

Review comments:
diff_hunk: @@ -29,13 +33,97 @@ func NewAuthContext(accessToken string) context.Context {
 	)
 }
 
+// DefaultAuthContext creates a new context, retrieving Pulumi Access Token
+// from either PULUMI_ACCESS_TOKEN environment variable, or from
+// currently logged in account in Pulumi CLI or ESC CLI
+//
+// This context can be used to authenticate requests to the ESC API.
+func NewDefaultAuthContext() (context.Context, error) {
+	accessToken := os.Getenv("PULUMI_ACCESS_TOKEN")
+	if accessToken != "" {
+		return NewAuthContext(accessToken), nil
+	}
+
+	workspace := esc_workspace.New(esc_workspace.DefaultFS(), esc_workspace.DefaultPulumiWorkspace())
+	account, _, err := workspace.GetCurrentAccount(false)
+	if err != nil {
+		return nil, fmt.Errorf("Error grabbing current account: %w", err)
+	}
+	if account != nil {
+		return NewAuthContext(account.AccessToken), nil
+	}
+
+	return nil, errors.New("No default Pulumi Access Token found. Either export PULUMI_ACCESS_TOKEN " +
+		"environment variable, or login using Pulumi or ESC CLI.")
+}
+
 // NewClient creates a new ESC client with the given configuration.
 func NewClient(cfg *Configuration) *EscClient {
 	client := &EscClient{rawClient: NewRawAPIClient(cfg)}
 	client.EscAPI = client.rawClient.EscAPI
 	return client
 }
 
+// NewCustomBackendConfiguration creates a new Configuration object,
+// but replaces default API endpoint with a given custom backend URL
+func NewCustomBackendConfiguration(customBackendURL url.URL) *Configuration {
+	appendedURL := customBackendURL.JoinPath("api", "esc")
path: sdk/go/api_esc_extensions.go
user: login: dschaller
body: Should we ensure that `/api/esc` is not already on the URL path before appending these on? Probably just want the scheme and hostname from the existing url
created_at: 2025-03-17T17:27:46Z
author_association: MEMBER
diff_hunk: @@ -29,13 +33,97 @@ func NewAuthContext(accessToken string) context.Context {
 	)
 }
 
+// DefaultAuthContext creates a new context, retrieving Pulumi Access Token
+// from either PULUMI_ACCESS_TOKEN environment variable, or from
+// currently logged in account in Pulumi CLI or ESC CLI
+//
+// This context can be used to authenticate requests to the ESC API.
+func NewDefaultAuthContext() (context.Context, error) {
+	accessToken := os.Getenv("PULUMI_ACCESS_TOKEN")
+	if accessToken != "" {
+		return NewAuthContext(accessToken), nil
+	}
+
+	workspace := esc_workspace.New(esc_workspace.DefaultFS(), esc_workspace.DefaultPulumiWorkspace())
+	account, _, err := workspace.GetCurrentAccount(false)
+	if err != nil {
+		return nil, fmt.Errorf("Error grabbing current account: %w", err)
+	}
+	if account != nil {
+		return NewAuthContext(account.AccessToken), nil
+	}
+
+	return nil, errors.New("No default Pulumi Access Token found. Either export PULUMI_ACCESS_TOKEN " +
+		"environment variable, or login using Pulumi or ESC CLI.")
+}
+
 // NewClient creates a new ESC client with the given configuration.
 func NewClient(cfg *Configuration) *EscClient {
 	client := &EscClient{rawClient: NewRawAPIClient(cfg)}
 	client.EscAPI = client.rawClient.EscAPI
 	return client
 }
 
+// NewCustomBackendConfiguration creates a new Configuration object,
+// but replaces default API endpoint with a given custom backend URL
+func NewCustomBackendConfiguration(customBackendURL url.URL) *Configuration {
+	appendedURL := customBackendURL.JoinPath("api", "esc")
+	cfg := &Configuration{
+		DefaultHeader: make(map[string]string),
+		UserAgent:     "esc-sdk",
+		Debug:         false,
+		Servers: ServerConfigurations{
+			{
+				URL:         appendedURL.String(),
+				Description: "Pulumi Cloud Custom Backend API",
+			},
+		},
+		OperationServers: map[string]ServerConfigurations{},
+	}
+	return cfg
+}
+
+// NewDefaultClient creates a new ESC client with default configuration.
+// Backend URL is automatically detected from either PULUMI_BACKEND_URL environment variable
+// or currently logged in account in Pulumi CLI or ESC CLI
+func NewDefaultClient() (*EscClient, error) {
+	customBackendURL := os.Getenv("PULUMI_BACKEND_URL")
+	if customBackendURL == "" {
+		workspace := esc_workspace.New(esc_workspace.DefaultFS(), esc_workspace.DefaultPulumiWorkspace())
+		account, _, err := workspace.GetCurrentAccount(false)
+		if err != nil {
+			return nil, fmt.Errorf("Error grabbing current account: %w", err)
+		}
+		if account != nil {
+			customBackendURL = account.BackendURL
+		}
+	}
+
+	if customBackendURL != "" {
+		parsedUrl, err := url.Parse(customBackendURL)
+		if err != nil {
+			return nil, fmt.Errorf("Error parsing custom backend url: %w", err)
+		}
+		return NewClient(NewCustomBackendConfiguration(*parsedUrl)), nil
+	}
+
+	return NewClient(NewConfiguration()), nil
+}
+
+// This is the easiest way to use ESC SDK. DefaultLogin grabs default client
+// and default authorization context, so you can start using SDK right away
+func DefaultLogin() (*EscClient, context.Context, error) {
path: sdk/go/api_esc_extensions.go
user: login: dschaller
body: nit: should the context be the first returned value?

Also, is there a reason to return the auth context outside of the client? Would the primary spot we use this be for the client?
created_at: 2025-03-17T17:31:46Z
author_association: MEMBER
diff_hunk: @@ -29,13 +33,97 @@ func NewAuthContext(accessToken string) context.Context {
 	)
 }
 
+// DefaultAuthContext creates a new context, retrieving Pulumi Access Token
+// from either PULUMI_ACCESS_TOKEN environment variable, or from
+// currently logged in account in Pulumi CLI or ESC CLI
+//
+// This context can be used to authenticate requests to the ESC API.
+func NewDefaultAuthContext() (context.Context, error) {
+	accessToken := os.Getenv("PULUMI_ACCESS_TOKEN")
+	if accessToken != "" {
+		return NewAuthContext(accessToken), nil
+	}
+
+	workspace := esc_workspace.New(esc_workspace.DefaultFS(), esc_workspace.DefaultPulumiWorkspace())
+	account, _, err := workspace.GetCurrentAccount(false)
+	if err != nil {
+		return nil, fmt.Errorf("Error grabbing current account: %w", err)
+	}
+	if account != nil {
+		return NewAuthContext(account.AccessToken), nil
+	}
+
+	return nil, errors.New("No default Pulumi Access Token found. Either export PULUMI_ACCESS_TOKEN " +
+		"environment variable, or login using Pulumi or ESC CLI.")
+}
+
 // NewClient creates a new ESC client with the given configuration.
 func NewClient(cfg *Configuration) *EscClient {
 	client := &EscClient{rawClient: NewRawAPIClient(cfg)}
 	client.EscAPI = client.rawClient.EscAPI
 	return client
 }
 
+// NewCustomBackendConfiguration creates a new Configuration object,
+// but replaces default API endpoint with a given custom backend URL
+func NewCustomBackendConfiguration(customBackendURL url.URL) *Configuration {
+	appendedURL := customBackendURL.JoinPath("api", "esc")
path: sdk/go/api_esc_extensions.go
user: login: IaroslavTitov
body: Good idea, will do
created_at: 2025-03-17T18:07:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -78,7 +78,16 @@ type KeyValueMap = { [key: string]: string };
 export class EscApi {
     rawApi: EscRawApi;
     config: Configuration;
-    constructor(config: Configuration) {
+    constructor(config: Configuration|undefined = undefined) {
path: sdk/typescript/esc/index.ts
user: login: seanyeh
body: nit: `constructor(config?: Configuration) {` should be equivalent
created_at: 2025-03-17T19:21:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -78,7 +78,16 @@ type KeyValueMap = { [key: string]: string };
 export class EscApi {
     rawApi: EscRawApi;
     config: Configuration;
-    constructor(config: Configuration) {
+    constructor(config: Configuration|undefined = undefined) {
+        if (!config) {
+            const PULUMI_ACCESS_TOKEN = process.env.PULUMI_ACCESS_TOKEN;
path: sdk/typescript/esc/index.ts
user: login: seanyeh
body: I'm thinking maybe we want to read env vars even if a config is given? I'm picturing you pass in a config with a base path, but no access token. then you can read that from the env vars. (I think we want config to take precedence over env vars though)

(same for python)
created_at: 2025-03-17T19:24:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -29,13 +33,97 @@ func NewAuthContext(accessToken string) context.Context {
 	)
 }
 
+// DefaultAuthContext creates a new context, retrieving Pulumi Access Token
+// from either PULUMI_ACCESS_TOKEN environment variable, or from
+// currently logged in account in Pulumi CLI or ESC CLI
+//
+// This context can be used to authenticate requests to the ESC API.
+func NewDefaultAuthContext() (context.Context, error) {
+	accessToken := os.Getenv("PULUMI_ACCESS_TOKEN")
+	if accessToken != "" {
+		return NewAuthContext(accessToken), nil
+	}
+
+	workspace := esc_workspace.New(esc_workspace.DefaultFS(), esc_workspace.DefaultPulumiWorkspace())
+	account, _, err := workspace.GetCurrentAccount(false)
+	if err != nil {
+		return nil, fmt.Errorf("Error grabbing current account: %w", err)
+	}
+	if account != nil {
+		return NewAuthContext(account.AccessToken), nil
+	}
+
+	return nil, errors.New("No default Pulumi Access Token found. Either export PULUMI_ACCESS_TOKEN " +
+		"environment variable, or login using Pulumi or ESC CLI.")
+}
+
 // NewClient creates a new ESC client with the given configuration.
 func NewClient(cfg *Configuration) *EscClient {
 	client := &EscClient{rawClient: NewRawAPIClient(cfg)}
 	client.EscAPI = client.rawClient.EscAPI
 	return client
 }
 
+// NewCustomBackendConfiguration creates a new Configuration object,
+// but replaces default API endpoint with a given custom backend URL
+func NewCustomBackendConfiguration(customBackendURL url.URL) *Configuration {
+	appendedURL := customBackendURL.JoinPath("api", "esc")
path: sdk/go/api_esc_extensions.go
user: login: seanyeh
body: 😆 I actually prefer not having this logic - it's "magic" and not clear to the user. but up to you
created_at: 2025-03-17T19:32:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -29,13 +34,90 @@ func NewAuthContext(accessToken string) context.Context {
 	)
 }
 
+// DefaultAuthContext creates a new context, retrieving Pulumi Access Token
+// from either PULUMI_ACCESS_TOKEN environment variable, or from
+// currently logged in account in Pulumi CLI or ESC CLI
+//
+// This context can be used to authenticate requests to the ESC API.
+func NewDefaultAuthContext() (context.Context, error) {
+	accessToken := os.Getenv("PULUMI_ACCESS_TOKEN")
+	if accessToken != "" {
+		return NewAuthContext(accessToken), nil
+	}
+
+	workspace := esc_workspace.New(esc_workspace.DefaultFS(), esc_workspace.DefaultPulumiWorkspace())
+	account, _, err := workspace.GetCurrentAccount(false)
+	if err != nil {
+		return nil, fmt.Errorf("Error grabbing current account: %w", err)
+	}
+	if account != nil {
+		return NewAuthContext(account.AccessToken), nil
+	}
+
+	return nil, errors.New("No default Pulumi Access Token found. Either export PULUMI_ACCESS_TOKEN " +
+		"environment variable, or login using Pulumi or ESC CLI.")
+}
+
 // NewClient creates a new ESC client with the given configuration.
 func NewClient(cfg *Configuration) *EscClient {
 	client := &EscClient{rawClient: NewRawAPIClient(cfg)}
 	client.EscAPI = client.rawClient.EscAPI
 	return client
 }
 
+// NewCustomBackendConfiguration creates a new Configuration object,
+// but replaces default API endpoint with a given custom backend URL
+func NewCustomBackendConfiguration(customBackendURL url.URL) *Configuration {
+	if !strings.HasSuffix(customBackendURL.String(), "/api/esc") {
+		appendedURL := customBackendURL.JoinPath("api", "esc")
+		customBackendURL = *appendedURL
+	}
path: sdk/go/api_esc_extensions.go
user: login: dschaller
body: nit but I think this would be safer than do check for the suffix
```suggestion
	appendedUrl, err := url.Parse(fmt.Sprintf("%s://%s/api/esc", customBackendURL.Scheme, customBackendURL.Hostname()))
	if err != nil {
	    //....
```
created_at: 2025-03-17T20:48:16Z
author_association: MEMBER
diff_hunk: @@ -78,7 +78,26 @@ type KeyValueMap = { [key: string]: string };
 export class EscApi {
     rawApi: EscRawApi;
     config: Configuration;
-    constructor(config: Configuration) {
+    constructor(config?: Configuration) {
+        if (!config) {
+            config = new Configuration()
+        }
+
+        if (!config.accessToken) {
+            const PULUMI_ACCESS_TOKEN = process.env.PULUMI_ACCESS_TOKEN;
path: sdk/typescript/esc/index.ts
user: login: dschaller
body: super nit
```suggestion
            const pulumiAccessToken = process.env.PULUMI_ACCESS_TOKEN;
```
created_at: 2025-03-17T20:52:01Z
author_association: MEMBER
diff_hunk: @@ -78,7 +78,26 @@ type KeyValueMap = { [key: string]: string };
 export class EscApi {
     rawApi: EscRawApi;
     config: Configuration;
-    constructor(config: Configuration) {
+    constructor(config?: Configuration) {
+        if (!config) {
+            config = new Configuration()
+        }
+
+        if (!config.accessToken) {
+            const PULUMI_ACCESS_TOKEN = process.env.PULUMI_ACCESS_TOKEN;
+            if (!PULUMI_ACCESS_TOKEN) {
+                throw new Error("PULUMI_ACCESS_TOKEN not set");
+            }
+            config.accessToken = PULUMI_ACCESS_TOKEN
+        }
+
+        if (!config.basePath) {
+            const PULUMI_BASE_PATH = process.env.PULUMI_BACKEND_URL;
path: sdk/typescript/esc/index.ts
user: login: dschaller
body: super nit
```suggestion
            const pulumiBasePath = process.env.PULUMI_BACKEND_URL;
```
created_at: 2025-03-17T20:52:15Z
author_association: MEMBER
diff_hunk: @@ -29,13 +34,90 @@ func NewAuthContext(accessToken string) context.Context {
 	)
 }
 
+// DefaultAuthContext creates a new context, retrieving Pulumi Access Token
+// from either PULUMI_ACCESS_TOKEN environment variable, or from
+// currently logged in account in Pulumi CLI or ESC CLI
+//
+// This context can be used to authenticate requests to the ESC API.
+func NewDefaultAuthContext() (context.Context, error) {
+	accessToken := os.Getenv("PULUMI_ACCESS_TOKEN")
+	if accessToken != "" {
+		return NewAuthContext(accessToken), nil
+	}
+
+	workspace := esc_workspace.New(esc_workspace.DefaultFS(), esc_workspace.DefaultPulumiWorkspace())
+	account, _, err := workspace.GetCurrentAccount(false)
+	if err != nil {
+		return nil, fmt.Errorf("Error grabbing current account: %w", err)
+	}
+	if account != nil {
+		return NewAuthContext(account.AccessToken), nil
+	}
+
+	return nil, errors.New("No default Pulumi Access Token found. Either export PULUMI_ACCESS_TOKEN " +
+		"environment variable, or login using Pulumi or ESC CLI.")
+}
+
 // NewClient creates a new ESC client with the given configuration.
 func NewClient(cfg *Configuration) *EscClient {
 	client := &EscClient{rawClient: NewRawAPIClient(cfg)}
 	client.EscAPI = client.rawClient.EscAPI
 	return client
 }
 
+// NewCustomBackendConfiguration creates a new Configuration object,
+// but replaces default API endpoint with a given custom backend URL
+func NewCustomBackendConfiguration(customBackendURL url.URL) *Configuration {
+	if !strings.HasSuffix(customBackendURL.String(), "/api/esc") {
+		appendedURL := customBackendURL.JoinPath("api", "esc")
+		customBackendURL = *appendedURL
+	}
path: sdk/go/api_esc_extensions.go
user: login: IaroslavTitov
body: This is nice - ensures there's nothing extra - no more no less. Will do!
created_at: 2025-03-17T20:56:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -29,13 +33,97 @@ func NewAuthContext(accessToken string) context.Context {
 	)
 }
 
+// DefaultAuthContext creates a new context, retrieving Pulumi Access Token
+// from either PULUMI_ACCESS_TOKEN environment variable, or from
+// currently logged in account in Pulumi CLI or ESC CLI
+//
+// This context can be used to authenticate requests to the ESC API.
+func NewDefaultAuthContext() (context.Context, error) {
+	accessToken := os.Getenv("PULUMI_ACCESS_TOKEN")
+	if accessToken != "" {
+		return NewAuthContext(accessToken), nil
+	}
+
+	workspace := esc_workspace.New(esc_workspace.DefaultFS(), esc_workspace.DefaultPulumiWorkspace())
+	account, _, err := workspace.GetCurrentAccount(false)
+	if err != nil {
+		return nil, fmt.Errorf("Error grabbing current account: %w", err)
+	}
+	if account != nil {
+		return NewAuthContext(account.AccessToken), nil
+	}
+
+	return nil, errors.New("No default Pulumi Access Token found. Either export PULUMI_ACCESS_TOKEN " +
+		"environment variable, or login using Pulumi or ESC CLI.")
+}
+
 // NewClient creates a new ESC client with the given configuration.
 func NewClient(cfg *Configuration) *EscClient {
 	client := &EscClient{rawClient: NewRawAPIClient(cfg)}
 	client.EscAPI = client.rawClient.EscAPI
 	return client
 }
 
+// NewCustomBackendConfiguration creates a new Configuration object,
+// but replaces default API endpoint with a given custom backend URL
+func NewCustomBackendConfiguration(customBackendURL url.URL) *Configuration {
+	appendedURL := customBackendURL.JoinPath("api", "esc")
path: sdk/go/api_esc_extensions.go
user: login: seanyeh
body: on a second thought, I think we need this logic (in all langs). Currently the esc-sdk expects the given backend url to end in `api/esc` but this is inconsistent with pulumi/esc CLI, which expects the base url. So I'm thinking we need to support both types of URLs (base url or url ending in `api/esc`)
created_at: 2025-03-17T21:04:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -24,15 +25,21 @@ class EscClient:
     def __init__(self, config: configuration.Configuration=None) -> None:
         """Constructor
         """
-        if config is None or 'Authorization' not in config.api_key:
+        if config is None:
+            config = configuration.Configuration()
+        if 'Authorization' not in config.api_key:
             self.accessToken = os.getenv("PULUMI_ACCESS_TOKEN")
             if not self.accessToken:
                 raise ValueError(
                     "PULUMI_ACCESS_TOKEN is neither set in environment nor passed in as configuration") 
-            config = configuration.Configuration(access_token=self.accessToken)
-        if config.host is None:
+            config.api_key["Authorization"] = self.accessToken
+        if config.host == "https://api.pulumi.com/api/esc":
             self.host = os.getenv("PULUMI_BACKEND_URL")
-            config.host = self.host
+            if self.host:
+                self.host = append_esc_to_url(self.host)
+            if self.host:
+                config.host = self.host
path: sdk/python/pulumi_esc_sdk/esc_client.py
user: login: dschaller
body: edit because I see what you're doing - why not just parse the os env variable in the helper? It doesn't look like we use it anywhere else here
created_at: 2025-03-17T23:26:58Z
author_association: MEMBER
diff_hunk: @@ -24,15 +25,21 @@ class EscClient:
     def __init__(self, config: configuration.Configuration=None) -> None:
         """Constructor
         """
-        if config is None or 'Authorization' not in config.api_key:
+        if config is None:
+            config = configuration.Configuration()
+        if 'Authorization' not in config.api_key:
             self.accessToken = os.getenv("PULUMI_ACCESS_TOKEN")
             if not self.accessToken:
                 raise ValueError(
                     "PULUMI_ACCESS_TOKEN is neither set in environment nor passed in as configuration") 
-            config = configuration.Configuration(access_token=self.accessToken)
-        if config.host is None:
+            config.api_key["Authorization"] = self.accessToken
+        if config.host == "https://api.pulumi.com/api/esc":
path: sdk/python/pulumi_esc_sdk/esc_client.py
user: login: dschaller
body: This is likely just me not understanding but we only want to read in the env variable if the configs host is already set to `api.pulumi.com`?
created_at: 2025-03-17T23:28:09Z
author_association: MEMBER
diff_hunk: @@ -78,7 +78,32 @@ type KeyValueMap = { [key: string]: string };
 export class EscApi {
     rawApi: EscRawApi;
     config: Configuration;
-    constructor(config: Configuration) {
+    constructor(config?: Configuration) {
+        if (!config) {
+            config = new Configuration()
+        }
+
+        if (!config.accessToken) {
+            const pulumiAccessToken = process.env.PULUMI_ACCESS_TOKEN;
+            if (!pulumiAccessToken) {
+                throw new Error("PULUMI_ACCESS_TOKEN not set");
+            }
+            config.accessToken = pulumiAccessToken
+        }
+
+        if (!config.basePath) {
+            const pulumiBasePath = process.env.PULUMI_BACKEND_URL;
+            if (pulumiBasePath) {
path: sdk/typescript/esc/index.ts
user: login: dschaller
body: Should we error if there is no base path or are we ok with this being unset?
created_at: 2025-03-17T23:30:26Z
author_association: MEMBER
diff_hunk: @@ -24,15 +25,21 @@ class EscClient:
     def __init__(self, config: configuration.Configuration=None) -> None:
         """Constructor
         """
-        if config is None or 'Authorization' not in config.api_key:
+        if config is None:
+            config = configuration.Configuration()
+        if 'Authorization' not in config.api_key:
             self.accessToken = os.getenv("PULUMI_ACCESS_TOKEN")
             if not self.accessToken:
                 raise ValueError(
                     "PULUMI_ACCESS_TOKEN is neither set in environment nor passed in as configuration") 
-            config = configuration.Configuration(access_token=self.accessToken)
-        if config.host is None:
+            config.api_key["Authorization"] = self.accessToken
+        if config.host == "https://api.pulumi.com/api/esc":
             self.host = os.getenv("PULUMI_BACKEND_URL")
-            config.host = self.host
+            if self.host:
+                self.host = append_esc_to_url(self.host)
+            if self.host:
+                config.host = self.host
path: sdk/python/pulumi_esc_sdk/esc_client.py
user: login: IaroslavTitov
body: This is intentional - `append_esc_to_url` can error out and return None, so we have to check the output. We potentially can check _only_ after it, but I figured we should error early
created_at: 2025-03-18T16:04:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -24,15 +25,21 @@ class EscClient:
     def __init__(self, config: configuration.Configuration=None) -> None:
         """Constructor
         """
-        if config is None or 'Authorization' not in config.api_key:
+        if config is None:
+            config = configuration.Configuration()
+        if 'Authorization' not in config.api_key:
             self.accessToken = os.getenv("PULUMI_ACCESS_TOKEN")
             if not self.accessToken:
                 raise ValueError(
                     "PULUMI_ACCESS_TOKEN is neither set in environment nor passed in as configuration") 
-            config = configuration.Configuration(access_token=self.accessToken)
-        if config.host is None:
+            config.api_key["Authorization"] = self.accessToken
+        if config.host == "https://api.pulumi.com/api/esc":
path: sdk/python/pulumi_esc_sdk/esc_client.py
user: login: IaroslavTitov
body: Upon construction of the object, it is set [here](https://github.com/pulumi/esc-sdk/blob/7c5892667de220a5a17bd0a0063f6643e8e928ee/sdk/python/pulumi_esc_sdk/configuration.py#L83) - `host` is just a getter/setter function. So it's never empty, either passed in or default. 

So in the lines above, I'm checking if it's default, and then reading env vars to potentially update to non-default. The Configuration python class is unnecessarily confusing :/
created_at: 2025-03-18T16:08:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -78,7 +78,32 @@ type KeyValueMap = { [key: string]: string };
 export class EscApi {
     rawApi: EscRawApi;
     config: Configuration;
-    constructor(config: Configuration) {
+    constructor(config?: Configuration) {
+        if (!config) {
+            config = new Configuration()
+        }
+
+        if (!config.accessToken) {
+            const pulumiAccessToken = process.env.PULUMI_ACCESS_TOKEN;
+            if (!pulumiAccessToken) {
+                throw new Error("PULUMI_ACCESS_TOKEN not set");
+            }
+            config.accessToken = pulumiAccessToken
+        }
+
+        if (!config.basePath) {
+            const pulumiBasePath = process.env.PULUMI_BACKEND_URL;
+            if (pulumiBasePath) {
path: sdk/typescript/esc/index.ts
user: login: IaroslavTitov
body: That is ok/normal usage to not set the basePath, it will be set to default [here](https://github.com/pulumi/esc-sdk/blob/7c5892667de220a5a17bd0a0063f6643e8e928ee/sdk/typescript/esc/raw/base.ts#L55)
created_at: 2025-03-18T16:11:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -407,3 +410,45 @@ def convertPropertyToValue(property: Any) -> Any:
 
 def isObject(obj):
     return inspect.isclass(obj) or isinstance(obj, dict)
+
+def append_esc_to_url(custom_backend_url_str):
+    if custom_backend_url_str is None:
+        return None
+    try:
+        custom_backend_url = urlparse(custom_backend_url_str)
+        appended_url = urlunparse((
+            str(custom_backend_url.scheme),
+            str(custom_backend_url.netloc),
+            "/api/esc",
+            None,  # path
+            None,  # query
+            None,  # fragment
+        ))
+        return appended_url
+    except Exception as e:
+        print(f"Error parsing URL: {e}")
+        return None
+
+def default_config(host=None,
+             access_token=None,
+             server_index=None, server_variables=None,
+             server_operation_index=None, server_operation_variables=None,
+             ssl_ca_cert=None,
+             ) -> configuration.Configuration:
+    """Creates default configuration for EscClient.
+    """
+    if not access_token:
+        access_token = os.getenv("PULUMI_ACCESS_TOKEN")
+    if not host:
+        host = os.getenv("PULUMI_BACKEND_URL")
+    return configuration.Configuration(host, access_token, server_index, server_variables, server_operation_index, server_operation_variables, ssl_ca_cert)
path: sdk/python/pulumi_esc_sdk/esc_client.py
user: login: seanyeh
body: I would be explicit with the keyword args. `return configuration.Configuration(host=host, access_token=access_token, ...`
created_at: 2025-03-18T20:01:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -407,3 +410,45 @@ def convertPropertyToValue(property: Any) -> Any:
 
 def isObject(obj):
     return inspect.isclass(obj) or isinstance(obj, dict)
+
+def append_esc_to_url(custom_backend_url_str):
+    if custom_backend_url_str is None:
+        return None
+    try:
+        custom_backend_url = urlparse(custom_backend_url_str)
+        appended_url = urlunparse((
+            str(custom_backend_url.scheme),
+            str(custom_backend_url.netloc),
+            "/api/esc",
+            None,  # path
+            None,  # query
+            None,  # fragment
+        ))
+        return appended_url
+    except Exception as e:
+        print(f"Error parsing URL: {e}")
+        return None
+
+def default_config(host=None,
+             access_token=None,
+             server_index=None, server_variables=None,
+             server_operation_index=None, server_operation_variables=None,
+             ssl_ca_cert=None,
+             ) -> configuration.Configuration:
+    """Creates default configuration for EscClient.
+    """
+    if not access_token:
+        access_token = os.getenv("PULUMI_ACCESS_TOKEN")
+    if not host:
+        host = os.getenv("PULUMI_BACKEND_URL")
+    return configuration.Configuration(host, access_token, server_index, server_variables, server_operation_index, server_operation_variables, ssl_ca_cert)
+
+def default_client(host=None,
path: sdk/python/pulumi_esc_sdk/esc_client.py
user: login: seanyeh
body: I don't think we need default_client
created_at: 2025-03-18T20:03:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -407,3 +410,45 @@ def convertPropertyToValue(property: Any) -> Any:
 
 def isObject(obj):
     return inspect.isclass(obj) or isinstance(obj, dict)
+
+def append_esc_to_url(custom_backend_url_str):
+    if custom_backend_url_str is None:
+        return None
+    try:
+        custom_backend_url = urlparse(custom_backend_url_str)
+        appended_url = urlunparse((
+            str(custom_backend_url.scheme),
+            str(custom_backend_url.netloc),
+            "/api/esc",
+            None,  # path
+            None,  # query
+            None,  # fragment
+        ))
+        return appended_url
+    except Exception as e:
+        print(f"Error parsing URL: {e}")
+        return None
+
+def default_config(host=None,
+             access_token=None,
+             server_index=None, server_variables=None,
+             server_operation_index=None, server_operation_variables=None,
+             ssl_ca_cert=None,
+             ) -> configuration.Configuration:
+    """Creates default configuration for EscClient.
+    """
+    if not access_token:
+        access_token = os.getenv("PULUMI_ACCESS_TOKEN")
+    if not host:
+        host = os.getenv("PULUMI_BACKEND_URL")
+    return configuration.Configuration(host, access_token, server_index, server_variables, server_operation_index, server_operation_variables, ssl_ca_cert)
+
+def default_client(host=None,
path: sdk/python/pulumi_esc_sdk/esc_client.py
user: login: seanyeh
body: spoke offline, it's fine if you want to keep this
created_at: 2025-03-18T20:07:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -407,3 +410,45 @@ def convertPropertyToValue(property: Any) -> Any:
 
 def isObject(obj):
     return inspect.isclass(obj) or isinstance(obj, dict)
+
+def append_esc_to_url(custom_backend_url_str):
+    if custom_backend_url_str is None:
+        return None
+    try:
+        custom_backend_url = urlparse(custom_backend_url_str)
+        appended_url = urlunparse((
+            str(custom_backend_url.scheme),
+            str(custom_backend_url.netloc),
+            "/api/esc",
+            None,  # path
+            None,  # query
+            None,  # fragment
+        ))
+        return appended_url
+    except Exception as e:
+        print(f"Error parsing URL: {e}")
+        return None
+
+def default_config(host=None,
+             access_token=None,
+             server_index=None, server_variables=None,
+             server_operation_index=None, server_operation_variables=None,
+             ssl_ca_cert=None,
+             ) -> configuration.Configuration:
+    """Creates default configuration for EscClient.
+    """
+    if not access_token:
+        access_token = os.getenv("PULUMI_ACCESS_TOKEN")
+    if not host:
+        host = os.getenv("PULUMI_BACKEND_URL")
+    return configuration.Configuration(host, access_token, server_index, server_variables, server_operation_index, server_operation_variables, ssl_ca_cert)
path: sdk/python/pulumi_esc_sdk/esc_client.py
user: login: IaroslavTitov
body: Good point, I'll update just to be safe. 

And yeah, I've tested these after the change, on both prod pulumi and my review stack
created_at: 2025-03-18T20:11:19Z
author_association: CONTRIBUTOR

PR comments:
user: login: dschaller
created_at: 2025-03-17T17:49:34Z
body: > (golang only, not really a way to add this logic in others, but maybe can add default grabbing of env vars?)

Yeah I think right now we enforce that users specify this value on their own instead of us inferring it. We could add some utils to the generated sdks for other languages that maps the go logic to the other supported languages
author_association: MEMBER
user: login: IaroslavTitov
created_at: 2025-03-17T18:12:05Z
body: > We could add some utils to the generated sdks for other languages that maps the go logic to the other supported languages

Chatted through this with Sean and agreed to add reading of env vars to all langs, with backlog issues to implement reading of credentials for other languages - let me know if that doesn't make sense to you
author_association: CONTRIBUTOR
user: login: seanyeh
created_at: 2025-03-17T18:16:41Z
body: @IaroslavTitov for consistency I wouldn't mind also only checking env vars in Go too, but seeing that you've already done the work to reuse ESC I'm fine with that as well
author_association: CONTRIBUTOR
user: login: seanyeh
created_at: 2025-03-18T20:07:23Z
body: Also let's update the changelog
author_association: CONTRIBUTOR