Skip to content

Настольный клиент (Lite для рабочего стола) ​

Ещё не выпущено официально

Настольное приложение пока не поставляется в составе релиза установочным пакетом — его нужно собрать самостоятельно согласно разделу Установка и развёртывание.

Настольное приложение Lite основано на Wails v2, запускает бэкенд на Go в настольном процессе, использует SQLite и локальное файловое хранилище. После запуска можно управлять базами знаний и выполнять поиск с вопросами-ответами без Docker и внешних баз данных. Исходный код находится в cmd/desktop/; базовые возможности совпадают с одиночным бинарником Lite; настольная версия дополнительно предоставляет запуск без входа и локальную песочницу для выполнения команд агента на macOS.

Начиная с v0.8.3, настольная версия для macOS может устанавливать, обновлять и запускать локальные навыки из инструментария, подробнее см. Локальные навыки.

Общая архитектура ​

Настольное приложение состоит из трёх частей (все — в одном процессе):

  1. Встроенный бэкенд: в cmd/desktop/main.go через container.BuildContainer() собирается тот же контейнер внедрения зависимостей, что и в серверной версии, а в отдельной goroutine запускается http.Server (роутер Gin).
  2. Окно Wails (WebView): wails.Run() создаёт нативное окно; страницы фронтенда через обратный прокси (httputil.NewSingleHostReverseProxy), смонтированный в assetserver.Options.Handler, проксируются на встроенный бэкенд, поэтому WebView загружает именно SPA, отдаваемый каталогом бэкенда ./web.
  3. Слой привязок Go: структура App в cmd/desktop/app.go через Bind предоставляется фронтенд-JS (window.go.main.App.*).

Порты и прослушивание ​

Определяется функцией desktopBackendListenAddr() в main.go:

  • По умолчанию привязка к 127.0.0.1, порт берётся из сохранённого в desktop-prefs.json значения http_port; если оно не задано (равно 0), используется случайный свободный порт :0 с повторными попытками по экспоненциальной задержке (listenWithRetry, максимум 10 раз).
  • Если параметр http_bind_public равен true, прослушивание переключается на 0.0.0.0, а функция desktopPreferredLANIPv4() подбирает не loopback-адрес IPv4 (предпочитая приватные адреса) и формирует http://<LAN-IP>:<port>/api/v1 для вызова с других устройств в локальной сети.
  • Обратный прокси и API-вызовы WebView всегда используют loopback-адрес http://127.0.0.1:<port> и никогда не используют 0.0.0.0 в качестве адреса подключения.

Расположение данных (при работе из macOS.app) ​

Функция configureDesktopStorage() в main.go, обнаружив запуск из .app/Contents/MacOS:

  • Каталог данных задаётся как ~/Library/Application Support/WeKnora Lite/ (имя берётся из имени.app bundle).
  • База данных SQLite: .../data/weknora.db (внедряется через переменную окружения DB_PATH).
  • Локальное файловое хранилище: .../data/files (LOCAL_STORAGE_BASE_DIR).
  • migrateLegacyDesktopData() однократно переносит данные старых версий, хранившиеся в .app/Contents/Resources/data, в Application Support.
  • Рабочий каталог переключается на .app/Contents/Resources, чтобы прочитать упакованные config/config.yaml, .env, migrations/sqlite и ресурсы фронтенда web/.

Основные исходные файлы ​

ФайлНазначение
cmd/desktop/main.goГлавная точка входа (//go:build !bindings): запускает встроенный бэкенд Gin, строит меню macOS, настраивает окно Wails и обратный прокси, внедряет DomReady JS
cmd/desktop/main_bindings.goТочка входа генерации привязок (//go:build bindings): на этапе генерации привязок фронтенда wails build компилируется отдельно с -tags bindings, только Bind, без запуска Gin/базы данных
cmd/desktop/app.goСтруктура App и все методы привязок Wails
cmd/desktop/prefs.goЧтение и запись настольных настроек (desktop-prefs.json), включая одобренные каталоги проектов локальной песочницы
cmd/desktop/signing_key.goПри первом запуске создаёт и сохраняет ключ подписи
cmd/desktop/update.goЛогика проверки обновлений / загрузки / установки и перезапуска на основе GitHub Releases
cmd/desktop/wails.jsonКонфигурация сборки Wails
cmd/desktop/build/Ресурсы упаковки: appicon.png (иконка приложения), darwin/Info.plist (шаблон bundle macOS), windows/installer/project.nsi (шаблон установщика Windows NSIS)

Конфигурация окна и внедрение во фронтенд ​

Ключевые настройки wails.Run(&options.App{...}) (см. cmd/desktop/main.go):

  • Заголовок WeKnora Lite, начальный размер 1280 × 800, изменяемый размер, отображение при запуске.
  • AssetServer.Handler использует обратный прокси на встроенный бэкенд — ресурсы фронтенда не встроены через Go embed, а отдаются как SPA из каталога бэкенда ./web (упакован в .app/Contents/Resources/web).
  • Только для macOS: mac.TitleBarHiddenInset() — скрытая строка заголовка, WebView непрозрачен.
  • Меню приложения: About WeKnora (с кнопкой "Open GitHub", ведущей на https://aihelpme.ru), Check for Updates..., Quit (Cmd+Q), стандартное меню Edit, View > Reload (Cmd+R, отправляет фронтенду событие app:reload).

При OnDomReady в WebView внедряются три фрагмента JS:

  1. wailsThemeSyncJS: синхронизирует светлую/тёмную тему и цвет фона окна по ключу WeKnora_theme в localStorage.
  2. dragHandlerJS: собственная обработка перетаскивания окна (обходит определение перетаскивания Wails по CSS-переменной и использует обход DOM через el.closest() + определение области заголовка высотой 38px сверху, отправляя drag через мост сообщений WKWebView); одновременно перехватывает внешние ссылки http(s) и window.open, открывая их системным браузером (BrowserOpenURL).
  3. Внедряет window.__WEKNORA_API_BASE__ (реальный корневой путь API http://127.0.0.1:<port>/api/v1) и необязательный window.__WEKNORA_API_LAN_BASE__ (адрес доступа в локальной сети).

Методы привязок Wails (доступные фронтенду) ​

Структура App (cmd/desktop/app.go) предоставляется через Bind; фронтенд вызывает её как window.go.main.App.<имя метода>, сгенерированные привязки TypeScript находятся в frontend/src/wailsjs/go/main/App.d.ts:

МетодПодпись (на стороне JS)Описание
GetAPIBaseURL(): Promise<string>Возвращает корневой адрес локального REST API, например http://127.0.0.1:PORT/api/v1 (window.location.origin в WebView не является хостом API, нужно использовать это значение)
GetAPILanBaseURL(): Promise<string>Возвращает адрес API, рекомендуемый для других устройств в локальной сети (…/api/v1); пусто в режиме без bind-public или при неудачном определении IP
GetDesktopHTTPPortSetting(): Promise<number>Читает сохранённую настройку порта локального API (0 = случайный порт при каждом запуске)
SetDesktopHTTPPortSetting(port: number): Promise<void>Сохраняет настройку порта; требуется перезапуск приложения
GetDesktopHTTPBindPublicSetting(): Promise<boolean>Читает настройку прослушивания на всех сетевых интерфейсах (0.0.0.0)
SetDesktopHTTPBindPublicSetting(v: boolean): Promise<void>Сохраняет настройку прослушивания LAN/публичного; требуется перезапуск приложения
GetDesktopListenPublicActive(): Promise<boolean>Прослушивает ли текущая сессия фактически все сетевые интерфейсы (состояние времени выполнения, а не сохранённая настройка)
CheckForUpdates(): Promise<void>Запускает проверку обновлений вручную (с диалоговым окном «Установлена последняя версия» и т. п.)
AutoCheckForUpdates(): Promise<void>Тихо проверяет обновления и автоматически загружает их в фоне
GetAutoSetupToken(): Promise<string>Возвращает учётные данные для входа без регистрации для текущего процесса; фронтенд использует их для вызова POST /api/v1/auth/auto-setup
PickProjectDir(): Promise<string>Открывает системный диалог выбора каталога, добавляет выбранный каталог в список одобренных локальной песочницы и возвращает его; пусто при отмене
GetProjectDirs(): Promise<string[]>Читает одобренные каталоги проектов
RemoveProjectDir(dir: string): Promise<void>Удаляет каталог из списка одобренных
GetApprovalMode / SetApprovalMode(): Promise<string> / (mode: string): Promise<void>Читает и пишет режим подтверждений локальной песочницы; сейчас принимается только auto

Хранилище настроек (cmd/desktop/prefs.go) ​

Настройки сохраняются в JSON-файл desktop-prefs.json по пути os.UserConfigDir()/WeKnora Lite/desktop-prefs.json:

  • macOS: ~/Library/Application Support/WeKnora Lite/desktop-prefs.json
  • Windows: %AppData%\WeKnora Lite\desktop-prefs.json
  • Linux: ~/.config/WeKnora Lite/desktop-prefs.json

Права доступа к файлу — 0600, поля следующие:

ПолеТипЗначение по умолчаниюОписание
http_portint0Порт прослушивания встроенной службы API; 0 или недопустимое значение (вне 1–65535) означает случайный свободный порт при каждом запуске
http_bind_publicboolfalseПрослушивать ли 0.0.0.0 (разрешает доступ к встроенному API из локальной сети/интернета)
project_dirsstring[]пустоОдобренные через системный диалог выбора каталога локальные каталоги проектов (абсолютные пути); к сессии можно привязать только каталоги из списка
approval_modestringautoРежим подтверждений локальной песочницы; неизвестные значения трактуются как auto; ask и full пока не предоставляются и при сохранении отклоняются

Точки чтения и записи: LoadDesktopPrefsHTTPPort() / LoadDesktopHTTPBindPublic() / SaveDesktopHTTPPortPreference() / SaveDesktopHTTPBindPublicPreference(), а также LoadProjectDirs() / LoadApprovalMode() для локальной песочницы; при ошибке чтения или разбора происходит тихий откат к нулевым значениям. project_dirs можно дополнять только через диалог выбора каталога (PickProjectDir или POST /api/v1/system/host-project-dir); введённые вручную пути не считаются авторизацией.

Запуск без входа и ключ подписи ​

Настольной версии при запуске не требуется регистрация или вход:

  • При каждом запуске генерируются случайные учётные данные, которые передаются встроенному фронтенду только через привязку Wails GetAutoSetupToken. Фронтенд передаёт их в заголовке запроса X-WeKnora-Desktop-Token, вызывая POST /api/v1/auth/auto-setup; первый вызов создаёт пользователя по умолчанию admin@weknora.local и пространство, после чего сразу выдаётся токен входа. Запросы без этих учётных данных (включая другие устройства в локальной сети и обычные браузеры) отклоняются.
  • Если не задан действующий SYSTEM_SIGNING_KEY (или SYSTEM_AES_KEY со значением, отличным от значения по умолчанию), при первом запуске в каталоге настроек создаётся signing.key (права 0600), который используется как SYSTEM_SIGNING_KEY для подписи ссылок с предварительной подписью файлов, сессий веб-встраивания и т. п. и остаётся действительным после перезапуска. Он используется только для подписи и не заменяет AES-ключ, шифрующий сохранённые учётные данные.

Механизм автоматического обновления (cmd/desktop/update.go) ​

checkUpdate(ctx, currentVersion, showUpToDate, autoDownload) выполняется в goroutine:

  1. Источник версии: desktopAboutVersion() предпочитает handler.Version, внедрённую через ldflags при сборке, иначе ищет файл VERSION в корне репозитория, поднимаясь вверх; если версию определить не удаётся, проверка отменяется.
  2. Проверка: GET https://api.github.com/repos/Tencent/WeKnora/releases/latest (тайм-аут 10s, с User-Agent: WeKnora-Lite-Desktop-App; если задана переменная окружения GITHUB_TOKEN, добавляется заголовок Authorization для повышения лимита запросов). Функция golang.org/x/mod/semver сравнивает tag_name с текущей версией.
  3. Выбор артефакта: findBestAsset() сопоставляет имена файлов артефактов релиза по runtime.GOOS/GOARCH — ключевое слово ОС (mac/win/linux) + ключевое слово архитектуры (amd64/arm64, с поддержкой universal/aarch64), с поочерёдным откатом: ОС+архитектура → только ОС → .dmg для macOS → .exe для Windows; если совпадений нет, открывается страница релиза.
  4. Загрузка: downloadAndInstall() загружает во временный каталог системы; в режиме autoDownload загрузка идёт тихо, в ручном режиме сначала показывается диалог "Update Available". После завершения загрузки предлагается выбор "Restart Now / Later".
  5. Установка и перезапуск (applyUpdateAndRestart(), различия по платформам):
  • Windows: создаёт временный weknora_update.bat (задержка 2 секунды → тихий запуск установочного пакета /S → перезапуск исходной программы → самоудаление), выполняется через cmd.exe /C start /b, после чего приложение завершается.
  • macOS (.dmg): hdiutil attach монтирует во временную точку монтирования, находится .app внутри, создаётся временный weknora_update.sh: rm -rf старый bundle и cp -a новый bundle (при ошибке — повторная попытка с повышением прав через osascript … with administrator privileges) → hdiutil detach → open нового приложения → самоудаление; если это не .dmg или произошла ошибка, выполняется откат к open загруженного файла.
  • Linux: xdg-open открывает загруженный файл, после чего приложение завершается.

Точки входа: пункт меню macOS Check for Updates... (вручную, показывает результат), методы привязок CheckForUpdates() (вручную) и AutoCheckForUpdates() (тихо + автоматическая загрузка; фронтенд frontend/src/App.vue вызывает его при наличии window.go.main.App.AutoCheckForUpdates).

Конфигурация сборки Wails (cmd/desktop/wails.json) ​

json
{
  "name": "aihelpme.ru Lite",
  "outputfilename": "aihelpme.ru Lite",
  "frontend:dir": "../../frontend",
  "wailsjsdir": "../../frontend/src",
  "build:tags": "desktop",
  "info": { "companyName": "aihelpme.ru", "productName": "aihelpme.ru Lite", "productVersion": "1.0.0" },
  "mac": { "category": "public.app-category.productivity", "titlebar": "hiddenInset" }
}

Ключевые моменты:

  • build:tags равно desktop: только программы, собранные с этим тегом, включают локальную песочницу Lite; теги, переданные в wails build -tags ..., объединяются с ним.
  • frontend:dir указывает на frontend/ репозитория; wailsjsdir указывает на frontend/src, поэтому автоматически сгенерированные привязки Wails выводятся в frontend/src/wailsjs/ (go/main/App.js, App.d.ts и runtime/).
  • Сборка фронтенда: сборочный скрипт собирает фронтенд отдельно; в конфигурации Wails frontend:build не задаётся. WebView обращается к встроенному бэкенду через обратный прокси.
  • cmd/desktop/build/ содержит appicon.png (иконку приложения), darwin/Info.plist (Go template bundle macOS, объявляющий CFBundleIdentifier: com.wails.WeKnora Lite, минимальную версию системы 10.13, поддержку Retina и т. д.) и шаблон установщика Windows windows/installer/project.nsi (дополнительно устанавливает файлы сторонних лицензий); результат wails build выводится в cmd/desktop/build/bin/.

Как фронтенд определяет настольное окружение ​

  • dragHandlerJS добавляет к document.documentElement класс wails-desktop, по которому CSS фронтенда адаптирует настольные стили.
  • Внедряемые Wails window.go.main.App.* (сгенерированные привязки см. в frontend/src/wailsjs/go/main/) и window.runtime (frontend/src/wailsjs/runtime/, например BrowserOpenURL, EventsEmit) существуют только в настольном окружении; фронтенд определяет это через проверку возможностей: например, frontend/src/composables/useApiBaseUrlDisplay.ts опрашивает window.__WEKNORA_API_BASE__ или вызывает window.go.main.App.GetAPIBaseURL() для получения реального адреса API (в браузере — откат к значению конфигурации / window.location.origin); frontend/src/App.vue при наличии window.go.main.App.AutoCheckForUpdates запускает тихую проверку обновлений.
  • Страницы настроек frontend/src/views/settings/GeneralSettings.vue и frontend/src/views/integrations/ApiIntegrationSettings.vue также используют эти привязки для отображения/изменения настольных опций, таких как порт и прослушивание LAN.

Способ сборки ​

Сборочный скрипт для macOS — scripts/package-mac-app.sh (в корневом Makefile нет целей, связанных с desktop):

bash
# Полная сборка (фронтенд + упаковка Wails + сборка .app)
./scripts/package-mac-app.sh

# Пропустить сборку фронтенда (переиспользовать существующий каталог web/)
SKIP_FRONTEND=1 ./scripts/package-mac-app.sh

Процесс скрипта:

  1. Сборка фронтенда: cd frontend && npm ci && npm run build, затем frontend/dist синхронизируется в web/ в корне репозитория (бэкенд Lite отдаёт SPA из ./web).
  2. Сборка Wails: сначала нужно установить Wails CLI (go install github.com/wailsapp/wails/v2/cmd/wails@latest), задать переменные окружения EDITION=lite, GOLANG_PROTOBUF_REGISTRATION_CONFLICT=warn (во избежание конфликта регистрации дескриптора common.proto в сгенерированном gRPC-коде Milvus и Qdrant) и др., получить номер версии из scripts/get_version.sh для внедрения в ldflags, затем выполнить реальную команду сборки:
bash
  cd cmd/desktop && wails build -clean -tags "sqlite_fts5" -ldflags="$LDFLAGS" -o "aihelpme.ru Lite"

Фактически действующие теги сборки — desktop из wails.json плюс sqlite_fts5 из командной строки. На этапе «генерации привязок» этой команды main_bindings.go компилируется отдельно с -tags bindings (без подключения к базе данных) и обновляются файлы привязок в frontend/src/wailsjs/. 3. Сборка артефакта: cmd/desktop/build/bin/WeKnora Lite.app копируется в dist/, а в .app/Contents/Resources/ помещаются сторонние лицензии (scripts/copy-licenses.sh), .env (из .env.lite.example), config/, migrations/sqlite/ и ресурсы фронтенда web/.

Итоговый артефакт — dist/WeKnora Lite.app, запускается двойным щелчком. Для Windows/Linux также можно собрать самостоятельно с помощью wails build в cmd/desktop (механизм обновления адаптирован под платформы через .exe / xdg-open), но репозиторий сейчас предоставляет только скрипт упаковки для macOS и ресурсы build/darwin.

Версия v0.8.3 · MIT License