Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ jobs:
build:
timeout-minutes: 15
name: build
permissions:
contents: read
id-token: write
runs-on: ${{ github.repository == 'stainless-sdks/stagehand-java' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork

Expand All @@ -61,6 +64,21 @@ jobs:
- name: Build SDK
run: ./scripts/build

- name: Get GitHub OIDC Token
if: github.repository == 'stainless-sdks/stagehand-java'
id: github-oidc
uses: actions/github-script@v6
with:
script: core.setOutput('github_token', await core.getIDToken());

- name: Build and upload Maven artifacts
if: github.repository == 'stainless-sdks/stagehand-java'
env:
URL: https://pkg.stainless.com/s
AUTH: ${{ steps.github-oidc.outputs.github_token }}
SHA: ${{ github.sha }}
PROJECT: stagehand-java
run: ./scripts/upload-artifacts
test:
timeout-minutes: 15
name: test
Expand Down
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.5.0"
".": "0.6.0"
}
6 changes: 3 additions & 3 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 7
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fstagehand-39cd9547d16412cf0568f6ce2ad8d43805dffe65bde830beeff630b903ae3b38.yml
openapi_spec_hash: 9cd7c9fefa686f9711392782d948470f
config_hash: 3c21550e2c94cad4339d3093d794beb0
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/browserbase%2Fstagehand-419940ce988c43313660d30a5bb5b5c2d89b3b19a0f80fe050331e0f4e8c58d2.yml
openapi_spec_hash: a621ca69697ebba7286cbf9e475c46ad
config_hash: 74111faa0876db6b053526281c444498
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Changelog

## 0.6.0 (2026-01-13)

Full Changelog: [v0.5.0...v0.6.0](https://github.com/browserbase/stagehand-java/compare/v0.5.0...v0.6.0)

### Features

* **client:** allow configuring dispatcher executor service ([b67a0d6](https://github.com/browserbase/stagehand-java/commit/b67a0d62fe7f2e15a73bc033c0a421daf88e6915))
* Removed requiring x-language and x-sdk-version from openapi spec ([2690cf4](https://github.com/browserbase/stagehand-java/commit/2690cf4d826f3a356f6049a5640fd15bf6e962ee))
* Using provider/model syntax in modelName examples within openapi spec ([aae3dce](https://github.com/browserbase/stagehand-java/commit/aae3dce8134b19c0bb3d24b848a73dd8a523aaec))


### Chores

* **internal:** support uploading Maven repo artifacts to stainless package server ([32f6329](https://github.com/browserbase/stagehand-java/commit/32f6329e2ec8e30b1b2f701e9ed2f039e934fd49))

## 0.5.0 (2026-01-07)

Full Changelog: [v0.4.0...v0.5.0](https://github.com/browserbase/stagehand-java/compare/v0.4.0...v0.5.0)
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

<!-- x-release-please-start-version -->

[![Maven Central](https://img.shields.io/maven-central/v/com.browserbase.api/stagehand-java)](https://central.sonatype.com/artifact/com.browserbase.api/stagehand-java/0.5.0)
[![javadoc](https://javadoc.io/badge2/com.browserbase.api/stagehand-java/0.5.0/javadoc.svg)](https://javadoc.io/doc/com.browserbase.api/stagehand-java/0.5.0)
[![Maven Central](https://img.shields.io/maven-central/v/com.browserbase.api/stagehand-java)](https://central.sonatype.com/artifact/com.browserbase.api/stagehand-java/0.6.0)
[![javadoc](https://javadoc.io/badge2/com.browserbase.api/stagehand-java/0.6.0/javadoc.svg)](https://javadoc.io/doc/com.browserbase.api/stagehand-java/0.6.0)

<!-- x-release-please-end -->

Expand All @@ -22,7 +22,7 @@ Use the Stagehand MCP Server to enable AI assistants to interact with this API,

<!-- x-release-please-start-version -->

The REST API documentation can be found on [docs.stagehand.dev](https://docs.stagehand.dev). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.browserbase.api/stagehand-java/0.5.0).
The REST API documentation can be found on [docs.stagehand.dev](https://docs.stagehand.dev). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.browserbase.api/stagehand-java/0.6.0).

<!-- x-release-please-end -->

Expand All @@ -33,7 +33,7 @@ The REST API documentation can be found on [docs.stagehand.dev](https://docs.sta
### Gradle

```kotlin
implementation("com.browserbase.api:stagehand-java:0.5.0")
implementation("com.browserbase.api:stagehand-java:0.6.0")
```

### Maven
Expand All @@ -42,7 +42,7 @@ implementation("com.browserbase.api:stagehand-java:0.5.0")
<dependency>
<groupId>com.browserbase.api</groupId>
<artifactId>stagehand-java</artifactId>
<version>0.5.0</version>
<version>0.6.0</version>
</dependency>
```

Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ repositories {

allprojects {
group = "com.browserbase.api"
version = "0.5.0" // x-release-please-version
version = "0.6.0" // x-release-please-version
}

subprojects {
Expand Down
8 changes: 8 additions & 0 deletions buildSrc/src/main/kotlin/stagehand.publish.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ configure<PublishingExtension> {
}
}
}
repositories {
if (project.hasProperty("publishLocal")) {
maven {
name = "LocalFileSystem"
url = uri("${rootProject.layout.buildDirectory.get()}/local-maven-repo")
}
}
}
}

signing {
Expand Down
96 changes: 96 additions & 0 deletions scripts/upload-artifacts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/usr/bin/env bash

set -euo pipefail

# ANSI Color Codes
GREEN='\033[32m'
RED='\033[31m'
NC='\033[0m' # No Color

log_error() {
local msg="$1"
local headers="$2"
local body="$3"
echo -e "${RED}${msg}${NC}"
[[ -f "$headers" ]] && echo -e "${RED}Headers:$(cat "$headers")${NC}"
echo -e "${RED}Body: ${body}${NC}"
exit 1
}

upload_file() {
local file_name="$1"
local tmp_headers
tmp_headers=$(mktemp)

if [ -f "$file_name" ]; then
echo -e "${GREEN}Processing file: $file_name${NC}"
pkg_file_name="mvn${file_name#./build/local-maven-repo}"

# Get signed URL for uploading artifact file
signed_url_response=$(curl -X POST -G "$URL" \
-sS --retry 5 \
-D "$tmp_headers" \
--data-urlencode "filename=$pkg_file_name" \
-H "Authorization: Bearer $AUTH" \
-H "Content-Type: application/json")

# Validate JSON and extract URL
if ! signed_url=$(echo "$signed_url_response" | jq -e -r '.url' 2>/dev/null) || [[ "$signed_url" == "null" ]]; then
log_error "Failed to get valid signed URL" "$tmp_headers" "$signed_url_response"
fi

# Set content-type based on file extension
local extension="${file_name##*.}"
local content_type
case "$extension" in
jar) content_type="application/java-archive" ;;
md5|sha1|sha256|sha512) content_type="text/plain" ;;
module) content_type="application/json" ;;
pom|xml) content_type="application/xml" ;;
*) content_type="application/octet-stream" ;;
esac

# Upload file
upload_response=$(curl -v -X PUT \
--retry 5 \
-D "$tmp_headers" \
-H "Content-Type: $content_type" \
--data-binary "@${file_name}" "$signed_url" 2>&1)

if ! echo "$upload_response" | grep -q "HTTP/[0-9.]* 200"; then
log_error "Failed upload artifact file" "$tmp_headers" "$upload_response"
fi

# Insert small throttle to reduce rate limiting risk
sleep 0.1
fi
}

walk_tree() {
local current_dir="$1"

for entry in "$current_dir"/*; do
# Check that entry is valid
[ -e "$entry" ] || [ -h "$entry" ] || continue

if [ -d "$entry" ]; then
walk_tree "$entry"
else
upload_file "$entry"
fi
done
}

cd "$(dirname "$0")/.."

echo "::group::Creating local Maven content"
./gradlew publishMavenPublicationToLocalFileSystemRepository -PpublishLocal
echo "::endgroup::"

echo "::group::Uploading to pkg.stainless.com"
walk_tree "./build/local-maven-repo"
echo "::endgroup::"

echo "::group::Generating instructions"
echo "Configure maven or gradle to use the repo located at 'https://pkg.stainless.com/s/${PROJECT}/${SHA}/mvn'"
echo "::endgroup::"
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ import java.net.Proxy
import java.time.Duration
import java.util.concurrent.CancellationException
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutorService
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager
import okhttp3.Call
import okhttp3.Callback
import okhttp3.Dispatcher
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType
Expand Down Expand Up @@ -202,6 +204,7 @@ private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClien

private var timeout: Timeout = Timeout.default()
private var proxy: Proxy? = null
private var dispatcherExecutorService: ExecutorService? = null
private var sslSocketFactory: SSLSocketFactory? = null
private var trustManager: X509TrustManager? = null
private var hostnameVerifier: HostnameVerifier? = null
Expand All @@ -212,6 +215,10 @@ private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClien

fun proxy(proxy: Proxy?) = apply { this.proxy = proxy }

fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply {
this.dispatcherExecutorService = dispatcherExecutorService
}

fun sslSocketFactory(sslSocketFactory: SSLSocketFactory?) = apply {
this.sslSocketFactory = sslSocketFactory
}
Expand All @@ -233,6 +240,8 @@ private constructor(@JvmSynthetic internal val okHttpClient: okhttp3.OkHttpClien
.callTimeout(timeout.request())
.proxy(proxy)
.apply {
dispatcherExecutorService?.let { dispatcher(Dispatcher(it)) }

val sslSocketFactory = sslSocketFactory
val trustManager = trustManager
if (sslSocketFactory != null && trustManager != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import java.time.Clock
import java.time.Duration
import java.util.Optional
import java.util.concurrent.Executor
import java.util.concurrent.ExecutorService
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager
Expand Down Expand Up @@ -46,11 +47,31 @@ class StagehandOkHttpClient private constructor() {
class Builder internal constructor() {

private var clientOptions: ClientOptions.Builder = ClientOptions.builder()
private var dispatcherExecutorService: ExecutorService? = null
private var proxy: Proxy? = null
private var sslSocketFactory: SSLSocketFactory? = null
private var trustManager: X509TrustManager? = null
private var hostnameVerifier: HostnameVerifier? = null

/**
* The executor service to use for running HTTP requests.
*
* Defaults to OkHttp's
* [default executor service](https://github.com/square/okhttp/blob/ace792f443b2ffb17974f5c0d1cecdf589309f26/okhttp/src/commonJvmAndroid/kotlin/okhttp3/Dispatcher.kt#L98-L104).
*
* This class takes ownership of the executor service and shuts it down when closed.
*/
fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply {
this.dispatcherExecutorService = dispatcherExecutorService
}

/**
* Alias for calling [Builder.dispatcherExecutorService] with
* `dispatcherExecutorService.orElse(null)`.
*/
fun dispatcherExecutorService(dispatcherExecutorService: Optional<ExecutorService>) =
dispatcherExecutorService(dispatcherExecutorService.getOrNull())

fun proxy(proxy: Proxy?) = apply { this.proxy = proxy }

/** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */
Expand Down Expand Up @@ -320,6 +341,7 @@ class StagehandOkHttpClient private constructor() {
OkHttpClient.builder()
.timeout(clientOptions.timeout())
.proxy(proxy)
.dispatcherExecutorService(dispatcherExecutorService)
.sslSocketFactory(sslSocketFactory)
.trustManager(trustManager)
.hostnameVerifier(hostnameVerifier)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import java.time.Clock
import java.time.Duration
import java.util.Optional
import java.util.concurrent.Executor
import java.util.concurrent.ExecutorService
import javax.net.ssl.HostnameVerifier
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager
Expand Down Expand Up @@ -46,11 +47,31 @@ class StagehandOkHttpClientAsync private constructor() {
class Builder internal constructor() {

private var clientOptions: ClientOptions.Builder = ClientOptions.builder()
private var dispatcherExecutorService: ExecutorService? = null
private var proxy: Proxy? = null
private var sslSocketFactory: SSLSocketFactory? = null
private var trustManager: X509TrustManager? = null
private var hostnameVerifier: HostnameVerifier? = null

/**
* The executor service to use for running HTTP requests.
*
* Defaults to OkHttp's
* [default executor service](https://github.com/square/okhttp/blob/ace792f443b2ffb17974f5c0d1cecdf589309f26/okhttp/src/commonJvmAndroid/kotlin/okhttp3/Dispatcher.kt#L98-L104).
*
* This class takes ownership of the executor service and shuts it down when closed.
*/
fun dispatcherExecutorService(dispatcherExecutorService: ExecutorService?) = apply {
this.dispatcherExecutorService = dispatcherExecutorService
}

/**
* Alias for calling [Builder.dispatcherExecutorService] with
* `dispatcherExecutorService.orElse(null)`.
*/
fun dispatcherExecutorService(dispatcherExecutorService: Optional<ExecutorService>) =
dispatcherExecutorService(dispatcherExecutorService.getOrNull())

fun proxy(proxy: Proxy?) = apply { this.proxy = proxy }

/** Alias for calling [Builder.proxy] with `proxy.orElse(null)`. */
Expand Down Expand Up @@ -320,6 +341,7 @@ class StagehandOkHttpClientAsync private constructor() {
OkHttpClient.builder()
.timeout(clientOptions.timeout())
.proxy(proxy)
.dispatcherExecutorService(dispatcherExecutorService)
.sslSocketFactory(sslSocketFactory)
.trustManager(trustManager)
.hostnameVerifier(hostnameVerifier)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ private constructor(
) : this(modelName, apiKey, baseUrl, provider, mutableMapOf())

/**
* Model name string without prefix (e.g., 'gpt-5-nano', 'claude-4.5-opus')
* Model name string (e.g., 'openai/gpt-5-nano', 'anthropic/claude-4.5-opus')
*
* @throws StagehandInvalidDataException if the JSON field has an unexpected type or is
* unexpectedly missing or null (e.g. if the server responded with an unexpected value).
Expand Down Expand Up @@ -345,7 +345,7 @@ private constructor(
additionalProperties = modelConfigObject.additionalProperties.toMutableMap()
}

/** Model name string without prefix (e.g., 'gpt-5-nano', 'claude-4.5-opus') */
/** Model name string (e.g., 'openai/gpt-5-nano', 'anthropic/claude-4.5-opus') */
fun modelName(modelName: String) = modelName(JsonField.of(modelName))

/**
Expand Down
Loading
Loading