Skip to content
Open
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
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
# Copy to ~/.applypilot/.env and fill in your values.

# LLM Provider (pick one)
# LLM_PROVIDER=gemini # Explicit provider selection (gemini/openai/local/codex)
GEMINI_API_KEY= # Gemini 2.0 Flash (recommended, cheapest)
# OPENAI_API_KEY= # OpenAI (GPT-4o-mini)
# LLM_URL=http://127.0.0.1:8080/v1 # Local LLM (llama.cpp, Ollama)
# LLM_MODEL= # Override model name
# For Codex: set LLM_PROVIDER=codex and run `codex login`

# Auto-Apply (optional)
CAPSOLVER_API_KEY= # For CAPTCHA solving during auto-apply
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to ApplyPilot will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- **Codex provider support** - added Codex as an LLM provider option for scoring, tailoring,
cover letters, and apply-time automation

## [0.2.0] - 2026-02-17

### Added
Expand Down
54 changes: 54 additions & 0 deletions src/applypilot/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""Helpers for selecting and launching the autonomous agent backend."""

from __future__ import annotations

import json
import shutil
from dataclasses import dataclass

from applypilot import config


@dataclass(frozen=True)
class AgentBackend:
"""Execution backend for the auto-apply agent."""

name: str
binary: str
model: str


def get_agent_backend(preferred: str | None = None, model: str | None = None) -> AgentBackend:
"""Resolve the backend and default model to use."""
backend = (preferred or config.get_agent_backend()).strip().lower()
if backend not in config.AGENT_BACKENDS:
backend = config.get_agent_backend()

if backend == "codex":
binary = shutil.which("codex") or "codex"
resolved_model = model or "gpt-5.4-mini"
else:
binary = shutil.which("claude") or "claude"
resolved_model = model or "haiku"

return AgentBackend(name=backend, binary=binary, model=resolved_model)


def codex_login_ok() -> tuple[bool, str]:
"""Check Codex login status."""
return config.codex_login_status()


def build_playwright_override_args(cdp_port: int) -> list[str]:
"""Build `codex exec -c` overrides for the Playwright MCP server."""
args = [
"@playwright/mcp@latest",
f"--cdp-endpoint=http://localhost:{cdp_port}",
f"--viewport-size={config.DEFAULTS['viewport']}",
]
return [
"-c",
'mcp_servers.playwright.command="npx"',
"-c",
f"mcp_servers.playwright.args={json.dumps(args)}",
]
Loading