Skip to content
Closed
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
16 changes: 16 additions & 0 deletions .fern/replay.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .fernignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,9 @@ elevenlabs-types/.gitignore

# Hand-written live E2E smoke test (tests/wire_test.rs next to it is generated).
tests/e2e_smoke.rs
.fern/replay.lock
.fern/replay.yml
.gitattributes

# Hand-written manual verification walkthrough for the agents-as-code workflow.
scripts/
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.fern/replay.lock linguist-generated=true
78 changes: 75 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -317,22 +317,82 @@ jobs:

gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/*

preflight-distribution:
runs-on: "ubuntu-22.04"
# `release.yml` also runs `dist plan` on pull_request, where secrets are
# unavailable to forks. Without this gate every contributor's PR would
# fail on a secret they cannot be given. Mirrors how `plan` derives its
# own `publishing` output.
if: ${{ !github.event.pull_request }}
steps:
- name: Verify the Homebrew tap / Scoop bucket App credentials
env:
APP_ID: ${{ secrets.PUBLISH_APP_ID }}
PRIVATE_KEY: ${{ secrets.PUBLISH_APP_PRIVATE_KEY }}
shell: bash
run: |
set -uo pipefail

# Normalized through `:-` before anything reads them. `set -u` aborts
# on an *unset* variable, and this job exists to explain a missing
# secret — dying with "APP_ID: unbound variable" instead of the
# message below would defeat the whole point.
APP_ID="${APP_ID:-}"
PRIVATE_KEY="${PRIVATE_KEY:-}"

status=0

if [ -z "${APP_ID}" ]; then
echo "::error title=Missing secret::secrets.PUBLISH_APP_ID is empty. Add it under Settings > Secrets and variables > Actions > Secrets. An Actions *variable* of the same name is not readable as secrets.PUBLISH_APP_ID. The value is the App ID (or Client ID) shown on your GitHub App's settings page."
status=1
fi

if [ -z "${PRIVATE_KEY}" ]; then
echo "::error title=Missing secret::secrets.PUBLISH_APP_PRIVATE_KEY is empty. Add it as an Actions secret containing the entire contents of the .pem file you downloaded when generating the App's private key."
status=1
else
case "${PRIVATE_KEY}" in
*"BEGIN"*"PRIVATE KEY"*) ;;
*)
echo "::error title=Malformed private key::secrets.PUBLISH_APP_PRIVATE_KEY does not contain a PEM header. Paste the whole .pem file, including its BEGIN and END lines."
status=1
;;
esac
if [ "$(printf '%s\n' "${PRIVATE_KEY}" | wc -l | tr -d ' ')" -lt 3 ]; then
echo "::error title=Malformed private key::secrets.PUBLISH_APP_PRIVATE_KEY is a single line. The PEM must keep its line breaks - re-paste it from the file without collapsing it. This is the most common cause of 'secretOrPrivateKey must be an asymmetric key'."
status=1
fi
fi

if [ "${status}" -eq 0 ]; then
echo "Homebrew tap / Scoop bucket App credentials are present and well-formed."
fi
exit "${status}"

publish-homebrew-formula:
needs:
- plan
- host
- preflight-distribution
runs-on: "ubuntu-22.04"
env:
GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
PLAN: ${{ needs.plan.outputs.val }}
GITHUB_USER: "github-actions[bot]"
GITHUB_EMAIL: "41898282+github-actions[bot]@users.noreply.github.com"
if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }}
steps:
- name: Mint a tap token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.PUBLISH_APP_ID }}
private-key: ${{ secrets.PUBLISH_APP_PRIVATE_KEY }}
owner: elevenlabs
repositories: homebrew-tap
- uses: actions/checkout@v6
with:
repository: "elevenlabs/homebrew-tap"
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
token: ${{ steps.app-token.outputs.token }}
# Credentials must persist — the final step pushes the formula back.
persist-credentials: true
# So we have access to the formula
Expand All @@ -353,6 +413,8 @@ jobs:
# This is extra complex because you can make your Formula name not match your app name
# so we need to find the formula file based on the app name
- name: Commit formula files
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
git config --global user.name "${GITHUB_USER}"
git config --global user.email "${GITHUB_EMAIL}"
Expand Down Expand Up @@ -394,6 +456,7 @@ jobs:
needs:
- plan
- host
- preflight-distribution
runs-on: "ubuntu-22.04"
# The same expression cargo-dist uses for its own publish jobs. A Scoop
# bucket has no prerelease channel — a manifest simply *is* the version
Expand Down Expand Up @@ -466,11 +529,20 @@ jobs:
echo "autoupdate-url=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/releases/download/${TAG_TEMPLATE}/${ASSET_NAME}"
} >> "$GITHUB_OUTPUT"

- name: Mint a bucket token
id: app-token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.PUBLISH_APP_ID }}
private-key: ${{ secrets.PUBLISH_APP_PRIVATE_KEY }}
owner: elevenlabs
repositories: scoop-bucket

- name: Check out the Scoop bucket
uses: actions/checkout@v6
with:
repository: "elevenlabs/scoop-bucket"
token: ${{ secrets.SCOOP_BUCKET_TOKEN }}
token: ${{ steps.app-token.outputs.token }}
path: scoop-bucket
# Credentials must persist — the next step pushes the manifest back.
persist-credentials: true
Expand Down
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,3 @@
**/*.rs.bk
.DS_Store
*.swp

# Scratch project written by scripts/verify-agents-as-code.sh
/.verify-project
100 changes: 50 additions & 50 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 38 additions & 11 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "fern-cli-sdk"
name = "elevenlabs-cli"
version = "1.0.0"
edition = "2021"
description = "CLI for the ElevenLabs API Documentation"
Expand All @@ -21,18 +21,28 @@ path = "cli/elevenlabs/main.rs"
[features]
# TLS backend selection.
#
# default = ["native-tls"]
# Uses the OS's native TLS stack (Secure Transport on macOS, SChannel on
# The backend is chosen per target by the `[target.'cfg(...)'.dependencies]`
# tables below, so a plain `cargo build` produces a working binary for every
# target we distribute: musl targets get rustls (a static binary cannot link
# the system OpenSSL), everything else gets the platform's native stack.
# `default` is therefore empty — a non-empty default would re-enable
# native-tls on musl and break the build, including under build systems that
# offer no way to pass per-target cargo flags (cargo-dist).
#
# The two features remain as explicit overrides:
#
# native-tls (cargo build --features native-tls)
# The OS's native TLS stack (Secure Transport on macOS, SChannel on
# Windows, OpenSSL on Linux). Honors the OS keychain / cert store —
# what users typically expect for an interactive CLI.
#
# ["rustls"] (cargo build --no-default-features --features rustls)
# Uses the pure-Rust rustls crate with Mozilla's bundled webpki roots.
# Produces self-contained static binaries that don't depend on system
# OpenSSL — preferred for distribution to varied Linux servers, scratch
# Docker images, and cross-compiled musl/ARM builds. Does NOT read the
# OS keychain; users must use `<NAME>_CA_BUNDLE` for custom roots.
default = ["native-tls"]
# rustls (cargo build --features rustls)
# The pure-Rust rustls crate. Produces self-contained binaries that
# don't depend on system OpenSSL — preferred for distribution to varied
# Linux servers, scratch Docker images, and cross-compiled musl/ARM
# builds. Does NOT read the OS keychain; users must use
# `<NAME>_CA_BUNDLE` for custom roots.
default = []
native-tls = ["reqwest/native-tls", "tokio-tungstenite/native-tls"]
rustls = ["reqwest/rustls-tls-native-roots", "tokio-tungstenite/rustls-tls-native-roots"]

Expand Down Expand Up @@ -91,7 +101,6 @@ secrecy = "0.10"
serde_qs = "1.1.1"
sha2 = "0.10"
thiserror = "2"
keyring = { version = "3", features = ["apple-native", "windows-native", "sync-secret-service", "vendored"] }
webbrowser = "1"
rand = "0.8"
tokio = { version = "1", features = ["full"] }
Expand All @@ -104,6 +113,24 @@ unicode-normalization = "0.1.25"
form_urlencoded = "1"
jmespath = "0.3"

# Per-target TLS backend (see [features] above). Cargo unions the features
# requested here with those in [dependencies], so each table only names the
# TLS feature; the rest of the reqwest/tungstenite feature set stays in one
# place.
[target.'cfg(target_env = "musl")'.dependencies]
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-native-roots"] }
tokio-tungstenite = { version = "0.24", default-features = false, features = ["rustls-tls-native-roots"] }

[target.'cfg(not(target_env = "musl"))'.dependencies]
reqwest = { version = "0.12", default-features = false, features = ["native-tls"] }
tokio-tungstenite = { version = "0.24", default-features = false, features = ["native-tls"] }
# The OS keyring backend, absent on musl: `sync-secret-service` reaches D-Bus
# through libdbus, which cannot be linked into a static binary. A musl build
# is a static/container build, where secret-service is unreachable anyway, so
# those binaries use the file-backed credential store (see ADR-0008 and the
# matching `cfg(not(target_env = "musl"))` gates in src/auth/keyring_store.rs).
keyring = { version = "3", features = ["apple-native", "windows-native", "sync-secret-service", "vendored"] }

[package.metadata.dist]
dist = true

Expand Down
1 change: 1 addition & 0 deletions cli/elevenlabs/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use fern_cli_sdk::auth::{PkceLoginFlow};

fn main() {
let app = CliApp::new("elevenlabs")
.user_agent_suffix_flag("via")
.login_flow(PkceLoginFlow::new("OAuth").client_id("c9c18126-5718-406a-8a12-21b69e5a888d").authorization_url("https://elevenlabs.io/app/oauth/authorize").token_url("https://api.us.elevenlabs.io/v1/oauth/token").scopes(["text_to_speech", "speech_to_speech", "speech_to_text", "sound_generation", "audio_isolation", "voice_generation", "forced_alignment", "music_generation", "image_video_generation", "flows", "models_read", "voices_read", "voices_write", "speech_history_read", "speech_history_write", "dubbing_read", "dubbing_write", "pronunciation_dictionaries_read", "pronunciation_dictionaries_write", "projects_read", "projects_write", "convai_read", "convai_write", "add_voice_from_voice_library", "create_instant_voice_clone", "create_professional_voice_clone", "user_read", "workspace_read"]).redirect_host("localhost").redirect_ports([8484, 8483, 8482]))
.binding(
OpenApiBinding::new()
Expand Down
2 changes: 1 addition & 1 deletion cli/elevenlabs/openapi0.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist-workspace.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ installers = ["shell", "powershell", "homebrew"]
# Whether to enable GitHub Attestations
github-attestations = true
# Target platforms to build apps for (Rust target-triple syntax)
targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc"]
targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "aarch64-unknown-linux-musl", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc"]
# Which actions to run on pull requests
pr-run-mode = "plan"
# Publish jobs to run (npm publishing deferred until pipeline is validated)
Expand Down
Loading
Loading