Skip to content

desktop_tools

desktop_tools

Desktop automation tools — open URI/apps, place Chrome, paste to frontmost.

Classes

OpenAnythingTool

Bases: BaseTool

Open any app, URL, file, or web search on the user's computer.

OpenUriTool

Bases: BaseTool

Open a file, URL, or Spotify URI with the OS default handler.

FocusAppTool

Bases: BaseTool

Bring a desktop application to the foreground (best-effort fullscreen).

OpenBrowserOnMonitorTool

Bases: BaseTool

Open a URL in Chrome on a given 1-based monitor index.

PasteToFrontmostTool

Bases: BaseTool

Copy text to clipboard and paste into the frontmost application.

PlayAudioFileTool

Bases: BaseTool

Play a local audio file through the system player.

RunVoiceCommandTool

Bases: BaseTool

Parse a spoken phrase and open an app or URL when it looks like a command.

Functions:

resolve_mac_app_name

resolve_mac_app_name(name: str) -> str | None

Resolve a spoken/typed app name to an .app bundle name on macOS.

Source code in src/diapason/tools/desktop_tools.py
def resolve_mac_app_name(name: str) -> str | None:
    """Resolve a spoken/typed app name to an ``.app`` bundle name on macOS."""
    from diapason.desktop.app_index import APP_INDEX

    return APP_INDEX.resolve(name)

open_in_browser

open_in_browser(
    url: str, *, browser: str = ""
) -> ToolResult

Open a URL in a preferred browser or the system default.

Source code in src/diapason/tools/desktop_tools.py
def open_in_browser(url: str, *, browser: str = "") -> ToolResult:
    """Open a URL in a preferred browser or the system default."""
    url = normalize_url(url)
    browser = (browser or "").strip()
    # Deliberately NOT guarded by local_only: handing a user-commanded URL to
    # the user's own browser is the user browsing, with Diapason as the hand
    # on the keyboard — the same boundary that already lets dictation paste
    # the user's words into cloud-backed apps under local-only. Blocking it
    # broke « ouvre youtube » for the very users the mode is meant to serve.
    # The boundary is documented at length in core/local_mode.py.
    try:
        if sys.platform == "darwin" and browser:
            app = resolve_mac_app_name(browser) or browser
            r = _run(["open", "-a", app, url], timeout=8.0)
            if r.returncode != 0:
                r = _run(["open", url], timeout=8.0)
        elif sys.platform == "darwin":
            r = _run(["open", url], timeout=8.0)
        elif sys.platform == "win32":
            if browser:
                _run(["cmd", "/c", "start", "", browser, url])
            else:
                os.startfile(url)  # type: ignore[attr-defined]
            return ToolResult(
                tool_name="open_anything",
                content=f"Opened {url}",
                success=True,
                metadata={"kind": "url", "url": url, "browser": browser},
            )
        else:
            r = _run(["xdg-open", url])
        if r.returncode != 0:
            return ToolResult(
                tool_name="open_anything",
                content=(r.stderr or r.stdout or "Failed to open URL").strip(),
                success=False,
            )
        return ToolResult(
            tool_name="open_anything",
            content=f"Opened {url}",
            success=True,
            metadata={"kind": "url", "url": url, "browser": browser},
        )
    except (OSError, subprocess.TimeoutExpired) as exc:
        return ToolResult(tool_name="open_anything", content=str(exc), success=False)

open_application

open_application(
    app_name: str, *, attente_s: float = 0.35
) -> ToolResult

Launch or focus an application by name.

Regarder avant, vérifier après (demandé le 23 août 2026) : une app déjà au premier plan se CONSTATE (« elle est déjà devant toi »), une app en marche se REMET devant, une app éteinte se LANCE — et la mise au premier plan se vérifie au lieu de se proclamer. Le content est écrit pour être dit tel quel ; l'état précis vit dans metadata.

Source code in src/diapason/tools/desktop_tools.py
def open_application(app_name: str, *, attente_s: float = 0.35) -> ToolResult:
    """Launch or focus an application by name.

    Regarder avant, vérifier après (demandé le 23 août 2026) : une app
    déjà au premier plan se CONSTATE (« elle est déjà devant toi »), une
    app en marche se REMET devant, une app éteinte se LANCE — et la mise
    au premier plan se vérifie au lieu de se proclamer. Le content est
    écrit pour être dit tel quel ; l'état précis vit dans metadata.
    """
    name = (app_name or "").strip()
    if not name:
        return ToolResult(
            tool_name="open_anything", content="No app name.", success=False
        )
    try:
        if sys.platform == "darwin":
            from diapason.desktop import etat_bureau

            resolved = resolve_mac_app_name(name) or name

            etat = etat_bureau.etat_du_bureau()
            bas = resolved.casefold()
            deja_devant = etat is not None and etat.premier_plan.casefold() == bas
            en_marche = etat is not None and any(
                p.casefold() == bas for p in etat.en_marche
            )
            if deja_devant:
                return ToolResult(
                    tool_name="open_anything",
                    content=f"{resolved} est déjà devant toi.",
                    success=True,
                    metadata={"kind": "app", "app": resolved, "etat": "deja_devant"},
                )

            r = _run(["open", "-a", resolved], timeout=8.0)
            if r.returncode != 0:
                # Activate via AppleScript as fallback
                r2 = _run(
                    ["osascript", "-e", f'tell application "{resolved}" to activate'],
                    timeout=8.0,
                )
                if r2.returncode != 0:
                    return ToolResult(
                        tool_name="open_anything",
                        content=(
                            r.stderr or r2.stderr or f"Could not open app '{name}'"
                        ).strip(),
                        success=False,
                    )

            # La vérification : deux regards espacés, puis l'honnêteté.
            verifie = False
            for _ in range(2):
                if attente_s > 0:
                    time.sleep(attente_s)
                if etat_bureau.premier_plan().casefold() == bas:
                    verifie = True
                    break

            if en_marche:
                contenu = (
                    f"{resolved} est devant toi."
                    if verifie
                    else (
                        f"{resolved} est ouvert, mais une autre fenêtre est "
                        "restée devant."
                    )
                )
                situation = "remise_devant"
            else:
                contenu = (
                    f"{resolved} est lancé et devant toi."
                    if verifie
                    else f"Je lance {resolved}, il arrive."
                )
                situation = "lancee"
            return ToolResult(
                tool_name="open_anything",
                content=contenu,
                success=True,
                metadata={
                    "kind": "app",
                    "app": resolved,
                    "etat": situation,
                    "verifie": verifie,
                },
            )
        if sys.platform == "win32":
            r = _run(["cmd", "/c", "start", "", name])
            return ToolResult(
                tool_name="open_anything",
                content=f"Started {name}",
                success=r.returncode == 0,
                metadata={"kind": "app", "app": name},
            )
        # Linux: try binary then gtk-launch
        binary = shutil.which(name.lower().replace(" ", "-")) or shutil.which(
            name.lower()
        )
        if binary:
            subprocess.Popen(
                [binary],
                stdin=subprocess.DEVNULL,
                stdout=subprocess.DEVNULL,
                stderr=subprocess.DEVNULL,
            )
            return ToolResult(
                tool_name="open_anything",
                content=f"Launched {binary}",
                success=True,
                metadata={"kind": "app", "app": binary},
            )
        return ToolResult(
            tool_name="open_anything",
            content=f"Cannot find application '{name}'.",
            success=False,
        )
    except (OSError, subprocess.TimeoutExpired) as exc:
        return ToolResult(tool_name="open_anything", content=str(exc), success=False)