144 lines
4.7 KiB
Elixir
144 lines
4.7 KiB
Elixir
defmodule BDS.Application do
|
|
@moduledoc false
|
|
|
|
use Application
|
|
|
|
@compiled_env Application.compile_env(:bds, :current_env, Mix.env())
|
|
|
|
# Children that depend on the boot mode (issue #26): desktop mode runs the
|
|
# wx window shell, server mode runs headless behind the SSH daemon. Both
|
|
# start the loopback HTTP endpoint and the CLI sync watcher.
|
|
def mode_children(:server, _env) do
|
|
[{BDS.Desktop.Server, []}, BDS.CliSync.Watcher, BDS.Server]
|
|
end
|
|
|
|
# CLI mode (issue #25): a one-shot command run against the shared database.
|
|
# No HTTP listener, SSH daemon, window, or sync watcher — the CLI is the
|
|
# external writer the watcher in a concurrently running app listens for.
|
|
def mode_children(:cli, _env), do: []
|
|
|
|
# Local TUI: the headless server plus a TUI on the launching terminal, so
|
|
# a GUI (via tunnel) and the local terminal can work in parallel. Quitting
|
|
# this TUI stops the whole VM — the terminal is the app in this mode.
|
|
# SSH-served TUI sessions never get this flag and stay session-local.
|
|
def mode_children(:tui, env),
|
|
do: mode_children(:server, env) ++ [{BDS.TUI, [stop_vm_on_exit: true]}]
|
|
|
|
def mode_children(:desktop, env), do: desktop_children(env)
|
|
|
|
def desktop_children(env \\ nil)
|
|
|
|
def desktop_children(:test) do
|
|
if desktop_automation?() do
|
|
[{BDS.Desktop.Server, []}]
|
|
else
|
|
[]
|
|
end
|
|
end
|
|
|
|
def desktop_children(_env) do
|
|
if Application.get_env(:bds, BDS.Application)[:desktop_adapter] == :desktop do
|
|
[{BDS.Desktop.Server, []}, BDS.CliSync.Watcher | desktop_window_children()]
|
|
else
|
|
[]
|
|
end
|
|
end
|
|
|
|
@impl true
|
|
def start(_type, _args) do
|
|
children =
|
|
[
|
|
{Phoenix.PubSub, name: BDS.PubSub},
|
|
{BDS.Desktop.Endpoint, secret_key_base: desktop_secret_key_base()},
|
|
BDS.Repo,
|
|
BDS.RepoBootstrap,
|
|
BDS.Tasks,
|
|
BDS.AI.InFlight,
|
|
BDS.Preview,
|
|
BDS.Publishing,
|
|
{Task.Supervisor, name: BDS.Tasks.TaskSupervisor},
|
|
{Task.Supervisor, name: BDS.TCP.TaskSupervisor},
|
|
BDS.Scripting.JobStore,
|
|
{Task.Supervisor, name: BDS.Scripting.TaskSupervisor},
|
|
BDS.Scripting.JobSupervisor,
|
|
BDS.Embeddings.Index
|
|
] ++ embedding_children() ++ mode_children(boot_mode(), current_env())
|
|
|
|
opts = [strategy: :one_for_one, name: BDS.Supervisor]
|
|
Supervisor.start_link(children, opts)
|
|
end
|
|
|
|
# The neural embedding backend runs as a supervised, lazily-initialised
|
|
# GenServer (it loads the model only on the first embedding request). Only
|
|
# start it when it is the configured backend.
|
|
defp embedding_children do
|
|
case Application.get_env(:bds, :embeddings, [])[:backend] do
|
|
BDS.Embeddings.Backends.Neural -> [BDS.Embeddings.Backends.Neural]
|
|
_other -> []
|
|
end
|
|
end
|
|
|
|
defp current_env do
|
|
Application.get_env(:bds, :current_env_override) || @compiled_env
|
|
end
|
|
|
|
# Desktop mode without a graphical display boots the TUI instead
|
|
# (issue #33); the log line explains the surprise mode switch.
|
|
defp boot_mode do
|
|
resolved = BDS.Server.mode()
|
|
effective = BDS.Server.effective_mode(resolved)
|
|
|
|
if effective != resolved do
|
|
require Logger
|
|
Logger.info("No graphical display available, starting the terminal UI instead")
|
|
end
|
|
|
|
effective
|
|
end
|
|
|
|
defp desktop_window_children do
|
|
if desktop_automation?() do
|
|
[]
|
|
else
|
|
disable_auto_window_menu()
|
|
|
|
window_opts =
|
|
BDS.Desktop.MainWindow.window_options(
|
|
menubar: BDS.Desktop.MenuBar,
|
|
icon_menu: BDS.Desktop.Menu,
|
|
url: &BDS.Desktop.url/0
|
|
)
|
|
|
|
[
|
|
{Desktop.Window, window_opts},
|
|
Supervisor.child_spec({BDS.Desktop.MainWindow, []}, id: BDS.Desktop.MainWindow.Watcher),
|
|
{BDS.Desktop.DeepLink, []},
|
|
{BDS.Desktop.RemoteConnection, []}
|
|
]
|
|
end
|
|
end
|
|
|
|
# wxOSX injects an untracked "Window" menu into the menubar at install time,
|
|
# which races elixir-desktop's async menu populate and ends up at an
|
|
# unstable position. We disable it and ship our own :window menu group
|
|
# (BDS.UI.MenuBar) placed between Blog and Help.
|
|
defp disable_auto_window_menu do
|
|
if :os.type() == {:unix, :darwin} do
|
|
:wx.set_env(Desktop.Env.wx_env())
|
|
:wxMenuBar.setAutoWindowMenu(false)
|
|
end
|
|
end
|
|
|
|
defp desktop_automation? do
|
|
System.get_env("BDS_DESKTOP_AUTOMATION") in ["1", "true", "TRUE"]
|
|
end
|
|
|
|
# The desktop endpoint binds to loopback only and its sessions do not need
|
|
# to survive restarts, so without explicit config the signing secret is a
|
|
# random per-boot value rather than a static one shipped in the repo.
|
|
defp desktop_secret_key_base do
|
|
Application.get_env(:bds, :desktop)[:secret_key_base] ||
|
|
Base.encode64(:crypto.strong_rand_bytes(48))
|
|
end
|
|
end
|