Настольный клиент (Lite для рабочего стола)
Ещё не выпущено официально
Настольное приложение пока не поставляется в составе релиза установочным пакетом — его нужно собрать самостоятельно согласно разделу Установка и развёртывание.
Настольное приложение Lite основано на Wails v2, запускает бэкенд на Go в настольном процессе, использует SQLite и локальное файловое хранилище. После запуска можно управлять базами знаний и выполнять поиск с вопросами-ответами без Docker и внешних баз данных. Исходный код находится в cmd/desktop/; базовые возможности совпадают с одиночным бинарником Lite; настольная версия дополнительно предоставляет запуск без входа и локальную песочницу для выполнения команд агента на macOS.
Начиная с v0.8.3, настольная версия для macOS может устанавливать, обновлять и запускать локальные навыки из инструментария, подробнее см. Локальные навыки.
Общая архитектура
Настольное приложение состоит из трёх частей (все — в одном процессе):
- Встроенный бэкенд: в
cmd/desktop/main.goчерезcontainer.BuildContainer()собирается тот же контейнер внедрения зависимостей, что и в серверной версии, а в отдельной goroutine запускаетсяhttp.Server(роутер Gin). - Окно Wails (WebView):
wails.Run()создаёт нативное окно; страницы фронтенда через обратный прокси (httputil.NewSingleHostReverseProxy), смонтированный вassetserver.Options.Handler, проксируются на встроенный бэкенд, поэтому WebView загружает именно SPA, отдаваемый каталогом бэкенда./web. - Слой привязок 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:
wailsThemeSyncJS: синхронизирует светлую/тёмную тему и цвет фона окна по ключуWeKnora_themeвlocalStorage.dragHandlerJS: собственная обработка перетаскивания окна (обходит определение перетаскивания Wails по CSS-переменной и использует обход DOM черезel.closest()+ определение области заголовка высотой 38px сверху, отправляяdragчерез мост сообщений WKWebView); одновременно перехватывает внешние ссылкиhttp(s)иwindow.open, открывая их системным браузером (BrowserOpenURL).- Внедряет
window.__WEKNORA_API_BASE__(реальный корневой путь APIhttp://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_port | int | 0 | Порт прослушивания встроенной службы API; 0 или недопустимое значение (вне 1–65535) означает случайный свободный порт при каждом запуске |
http_bind_public | bool | false | Прослушивать ли 0.0.0.0 (разрешает доступ к встроенному API из локальной сети/интернета) |
project_dirs | string[] | пусто | Одобренные через системный диалог выбора каталога локальные каталоги проектов (абсолютные пути); к сессии можно привязать только каталоги из списка |
approval_mode | string | auto | Режим подтверждений локальной песочницы; неизвестные значения трактуются как 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:
- Источник версии:
desktopAboutVersion()предпочитаетhandler.Version, внедрённую через ldflags при сборке, иначе ищет файлVERSIONв корне репозитория, поднимаясь вверх; если версию определить не удаётся, проверка отменяется. - Проверка: 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с текущей версией. - Выбор артефакта:
findBestAsset()сопоставляет имена файлов артефактов релиза поruntime.GOOS/GOARCH— ключевое слово ОС (mac/win/linux) + ключевое слово архитектуры (amd64/arm64, с поддержкойuniversal/aarch64), с поочерёдным откатом: ОС+архитектура → только ОС →.dmgдля macOS →.exeдля Windows; если совпадений нет, открывается страница релиза. - Загрузка:
downloadAndInstall()загружает во временный каталог системы; в режимеautoDownloadзагрузка идёт тихо, в ручном режиме сначала показывается диалог "Update Available". После завершения загрузки предлагается выбор "Restart Now / Later". - Установка и перезапуск (
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)
{
"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 и т. д.) и шаблон установщика Windowswindows/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):
# Полная сборка (фронтенд + упаковка Wails + сборка .app)
./scripts/package-mac-app.sh
# Пропустить сборку фронтенда (переиспользовать существующий каталог web/)
SKIP_FRONTEND=1 ./scripts/package-mac-app.shПроцесс скрипта:
- Сборка фронтенда:
cd frontend && npm ci && npm run build, затемfrontend/distсинхронизируется вweb/в корне репозитория (бэкенд Lite отдаёт SPA из./web). - Сборка 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, затем выполнить реальную команду сборки:
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.
