#!/usr/bin/env bash
# Dagon Inference in a boat sandbox: adds Dagon as a custom provider to pi, Prime Agent, OpenCode, Mistral Vibe, Claude
# Code and Codex, with every model GET /v1/models lists. Kimi Code needs no file: it takes the KIMI_MODEL_* variables.
#
#   boat new --setup-file dagon-setup.sh          # once, at create (runs as the user "user")
#   bash dagon-setup.sh                           # or by hand in a running sandbox; running it again changes nothing
#
# The key is never written to disk: each config names the DAGON_API_KEY variable, which the boat environment supplies.
# DAGON_BASE_URL, if set, replaces the default base URL. Existing configs are kept, and a provider named "dagon" that
# is already there is left as it is.
set -euo pipefail
python3 - <<'PY'
import json, os, urllib.request

base = os.environ.get("DAGON_BASE_URL", "https://inference.dagon.so/v1").rstrip("/")
home = os.path.expanduser("~")
with urllib.request.urlopen(f"{base}/models", timeout=30) as r:
    models = [m for m in json.load(r)["data"] if not m.get("deprecated")]
if not models:
    raise SystemExit(f"no models at {base}/models")

def path(*parts):
    p = os.path.join(home, *parts)
    os.makedirs(os.path.dirname(p), exist_ok=True)
    return p

def add_json(file, key, name, value):
    data = {}
    if os.path.exists(file):
        with open(file) as f:
            data = json.load(f)
    section = data.setdefault(key, {})
    if name in section:
        return print(f"{file}: {key}.{name} already set, left as it is")
    section[name] = value
    with open(file, "w") as f:
        json.dump(data, f, indent=2)
        f.write("\n")
    print(f"{file}: added {key}.{name}")

MARK = "# Dagon Inference provider (dagon-setup.sh)"

def add_toml(file, text):
    old = open(file).read() if os.path.exists(file) else ""
    tables = [l for l in text.splitlines() if l.startswith("[") and not l.startswith("[[")]
    if MARK in old or any(t in old.splitlines() for t in tables):
        return print(f"{file}: Dagon provider already set, left as it is")
    with open(file, "a") as f:
        f.write(("\n" if old and not old.endswith("\n") else "") + ("\n" if old else "") + MARK + "\n" + text)
    print(f"{file}: added the Dagon provider")

q = json.dumps  # a JSON string is a valid TOML basic string for these ids and URLs

# pi: ~/.pi/agent/models.json, the key read from $DAGON_API_KEY ("$NAME" interpolation).
pi_models = [{"id": m["id"], "name": m.get("name", m["id"]), "contextWindow": m["context_length"]} for m in models]
add_json(path(".pi", "agent", "models.json"), "providers", "dagon",
         {"baseUrl": base, "api": "openai-completions", "apiKey": "$DAGON_API_KEY", "models": pi_models})

# Prime Agent: the same file format, but apiKey names the variable without "$".
add_json(path(".prime", "agent", "models.json"), "providers", "dagon",
         {"baseUrl": base, "api": "openai-completions", "apiKey": "DAGON_API_KEY", "models": pi_models})

# OpenCode 1.x: a custom provider on the OpenAI-compatible AI SDK package; {env:NAME} reads the key.
add_json(path(".config", "opencode", "opencode.json"), "provider", "dagon",
         {"npm": "@ai-sdk/openai-compatible", "name": "Dagon Inference",
          "options": {"baseURL": base, "apiKey": "{env:DAGON_API_KEY}"},
          "models": {m["id"]: {"name": m.get("name", m["id"])} for m in models}})

# Mistral Vibe: an OpenAI-style provider and one model alias "dagon/<id>" per model. Pick one with VIBE_ACTIVE_MODEL.
add_toml(path(".vibe", "config.toml"),
         f'[[providers]]\nname = "dagon"\napi_base = {q(base)}\napi_key_env_var = "DAGON_API_KEY"\napi_style = "openai"\n'
         + "".join(f'\n[[models]]\nname = {q(m["id"])}\nprovider = "dagon"\nalias = {q("dagon/" + m["id"])}\n' for m in models))

def first(endpoint):
    return next((m for m in models if endpoint in m.get("endpoints", [])), models[0])

# Claude Code: ~/.claude/settings.json. apiKeyHelper prints $DAGON_API_KEY, so no key is written; the Anthropic base
# URL has no /v1. "[1m]" tells Claude Code a model's window is 1M tokens; it drops the suffix before sending.
claude = path(".claude", "settings.json")
settings = json.load(open(claude)) if os.path.exists(claude) else {}
if "apiKeyHelper" in settings or "ANTHROPIC_BASE_URL" in settings.get("env", {}):
    print(f"{claude}: a key helper or base URL already set, left as it is")
else:
    m = first("/v1/messages")
    cc_model = m["id"] + ("[1m]" if m["context_length"] >= 1_000_000 else "")
    settings["apiKeyHelper"] = 'printf %s "$DAGON_API_KEY"'
    settings.setdefault("env", {}).update({"ANTHROPIC_BASE_URL": base.removesuffix("/v1"),
                                           "ANTHROPIC_MODEL": cc_model, "ANTHROPIC_DEFAULT_HAIKU_MODEL": cc_model})
    with open(claude, "w") as f:
        json.dump(settings, f, indent=2)
        f.write("\n")
    print(f"{claude}: added the Dagon base URL, key helper and model {cc_model}")

# Codex: a profile file, ~/.codex/dagon.config.toml, layered over config.toml by codex --profile dagon. It holds a
# Responses provider (the only API Codex speaks to a custom provider); env_key names the variable. Web search is off:
# the gateway does not run it. features.multi_agent=false disables the collaboration namespace (Codex config reference).
add_toml(path(".codex", "dagon.config.toml"),
         f'model_provider = "dagon"\nmodel = {q(first("/v1/responses")["id"])}\nweb_search = "disabled"\n'
         f'\n[features]\nmulti_agent = false\n'
         f'\n[model_providers.dagon]\nname = "Dagon Inference"\nbase_url = {q(base)}\nenv_key = "DAGON_API_KEY"\nwire_api = "responses"\n')
PY
