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
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,16 @@ sources:
codex: { timeout: 300 } # argv/prompt also overridable, e.g. sources.codex.argv
```

Agents run **safely by default** (no approval-bypass). For unattended runs on
sources you trust, **`--yolo`** flips on the danger opt-ins for that ingest --
codex `--dangerously-bypass-approvals-and-sandbox`, hermes `--yolo`, and the
`command` shell (equivalent to setting `sources.codex.unsafe_bypass` /
`sources.hermes.unsafe_yolo` / `sources.command.allow_shell`):

```bash
doxa ingest https://target.example --via codex --yolo
```

**Choose how to scrape** with `--mode` / `--prompt` (agent + `command` fetchers).
`--prompt` is free-form, so the agent can use whatever it has -- SERP search,
browser automation, structured extraction, platform-specific endpoints -- to
Expand Down
17 changes: 17 additions & 0 deletions doxa/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,8 +322,20 @@ def cb(index: int, total: int) -> None:
return cb


def _apply_yolo(config: dict[str, Any]) -> None:
"""doxa's yolo mode: let agent/command fetchers run unattended for this ingest --
codex bypass-approvals, hermes --yolo, and the command shell. Trusted sources only."""
sources = config.setdefault("sources", {})
sources.setdefault("codex", {})["unsafe_bypass"] = True
sources.setdefault("hermes", {})["unsafe_yolo"] = True
sources.setdefault("command", {})["allow_shell"] = True


def cmd_ingest(args: argparse.Namespace) -> int:
config = load_config(args.config, allow_demo_default=False)
if getattr(args, "yolo", False):
_apply_yolo(config)
_hint("yolo: agent fetchers run with bypass/--yolo + shell enabled for this ingest -- trusted sources only.")
store = JsonlStore(config)
source_args = args.source if isinstance(args.source, list) else [args.source]
stdin_text = sys.stdin.read() if "-" in source_args else None
Expand Down Expand Up @@ -744,6 +756,11 @@ def build_parser() -> argparse.ArgumentParser:
"--prompt",
help="Fetch/extract instruction for agent (claude/codex/hermes) or command fetchers, e.g. 'extract product name, price, reviews as JSON'.",
)
ingest.add_argument(
"--yolo",
action="store_true",
help="Run agent/command fetchers unattended: codex --dangerously-bypass-approvals, hermes --yolo, command shell. Trusted sources only.",
)
ingest.set_defaults(func=cmd_ingest)

index = subparsers.add_parser("index", help="Build optional Postgres/pgvector semantic index.")
Expand Down
17 changes: 17 additions & 0 deletions tests/test_sources_fetchers.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,20 @@ def test_command_fetcher_substitutes_prompt() -> None:
config = {"sources": {"command": {"argv": ["python3", "-c", "import sys;print('# Cmd\\n\\n'+sys.argv[1])", "{prompt}"]}}}
rec = load_url("https://x", fetcher="command", config=config, fetch_prompt="do the thing")
assert "do the thing" in rec.text


def test_yolo_enables_unattended_agent_fetching() -> None:
from doxa.cli import _apply_yolo, build_parser
from doxa.sources.fetchers import _AGENT_PRESETS, _CODEX_BYPASS_FLAG, _agent_argv

assert build_parser().parse_args(["ingest", "-", "--yolo"]).yolo is True

config: dict = {}
_apply_yolo(config)
s = config["sources"]
assert s["codex"]["unsafe_bypass"] is True
assert s["hermes"]["unsafe_yolo"] is True
assert s["command"]["allow_shell"] is True
# the agent argv builder honors the flipped opt-ins
assert _CODEX_BYPASS_FLAG in _agent_argv("codex", _AGENT_PRESETS["codex"]["argv"], s["codex"])
assert "--yolo" in _agent_argv("hermes", _AGENT_PRESETS["hermes"]["argv"], s["hermes"])
Loading