forked from WHC/Hermit
desktop: the window installs itself (launcher entry, icon, default.nix)
app/linux/cst-agent-app.{desktop,svg} plus one CMake component ("app") in
app/CMakeLists.txt: `cmake --install --component app` puts the binary in bin/,
the entry in share/applications/ and the icon in hicolor/scalable/apps/. The
entry's file name stays the app_id Qt sets (QGuiApplication::applicationName),
or the launcher draws a generic icon; only the front end installs, the CLI
keeps coming from scripts/bootstrap_linux.sh.
default.nix is that install for nix users: nixpkgs' Qt 6 (qtwayland for a
Wayland session, qtsvg for Breeze's SVG icons), wrapQtAppsHook so the plugins
and QML modules resolve outside a nix-shell and survive nix-collect-garbage,
a filtered src (third_party/ and the build directories are gigabytes this
build never reads), and the version read from include/cst_version.h. doCheck
runs ctest minus test_sandbox, whose Landlock assertion cannot hold inside the
nix build sandbox (the other 40 pass there; make check runs all 41).
Verified: nix-build, nix profile add --file ., then --selftest and --uitest in
a clean environment (no nix-shell, no XDG_* from the session).
This commit is contained in:
@@ -49,3 +49,6 @@ app/android/local.properties
|
||||
|
||||
# the engine as a static library (app/ links it; built by `make`)
|
||||
libcstcore.a
|
||||
|
||||
# nix-build
|
||||
result
|
||||
|
||||
@@ -119,6 +119,21 @@ cmake -B build-hd -DCST_BUILD_TUI=OFF && cmake --build build-hd
|
||||
```
|
||||
|
||||
依赖:`gcc`、`clang` 或 `tcc`(0.9.27 mob 以上)、`libcurl`、TUI 需 `ncursesw`(Windows 版用打过补丁的 ncurses 6.6,脚本自动编译)。
|
||||
### 桌面 GUI(Qt/QML,可选)
|
||||
|
||||
把引擎放进同一个进程的窗口前端(无端口、无第二个进程):`app/`,细节见 [app/README.md](app/README.md)。
|
||||
|
||||
```bash
|
||||
# NixOS / nix:装进用户 profile(PATH + 菜单项 + 图标),卸载 nix profile remove cst-agent
|
||||
nix profile add --file .
|
||||
|
||||
# 其他发行版(需要 Qt 6 的 Quick/Qml/QuickControls2 与 libcurl)
|
||||
cmake -B build-app -DCST_BUILD_APP=ON -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build-app -j && cmake --install build-app --component app --prefix ~/.local
|
||||
```
|
||||
|
||||
菜单里启动时工作区=启动目录(即 `$HOME`);在项目目录里 `cst-agent-app` 启动则工作区就是那个目录。
|
||||
|
||||
Sashidown(`vendor/sashidown`)与 cJSON(`vendor/cjson`,MIT)已随仓库提供。
|
||||
API Key:TUI 里输入并按 provider 保存到配置目录的 `credentials`(权限 0600);`/key` 更换,`--ask-key` 在命令行输入。
|
||||
`--api-key` 只用于本次进程、不落盘;`DEEPSEEK_API_KEY` 等环境变量和 `CST_API_KEY` 仅作为没有保存 key 时的兜底(方便 CI)。
|
||||
|
||||
@@ -14,6 +14,10 @@ project(cst-agent-app LANGUAGES C CXX VERSION 0.1.0)
|
||||
# Inside the repo build (the parent defines the cstcore target, shared with the CLI):
|
||||
# cmake -B build -DCST_BUILD_APP=ON && cmake --build build --target cst-agent-app
|
||||
#
|
||||
# Desktop install (the window, its launcher entry and the icon; the CLI has
|
||||
# its own installer) -- default.nix installs exactly this component:
|
||||
# cmake --install build --component app --prefix ~/.local
|
||||
#
|
||||
# Extra QML import paths, e.g. a Kirigami installed away from the Qt prefix:
|
||||
# -DCST_APP_QML_IMPORT=... (added to QML2_IMPORT_PATH / the app's runtime)
|
||||
|
||||
@@ -119,6 +123,23 @@ if(CST_APP_GUI)
|
||||
set_target_properties(cst-agent-app PROPERTIES WIN32_EXECUTABLE TRUE)
|
||||
endif()
|
||||
|
||||
# A desktop install. The icon goes where the theme layout says, so the
|
||||
# entry's Icon= name finds it, and the entry's file name is the app_id Qt
|
||||
# sets (QGuiApplication::applicationName) -- rename one and the launcher
|
||||
# loses the icon. All of it in the "app" component: the CLI keeps coming
|
||||
# from scripts/bootstrap_linux.sh, and two `cst-agent` in PATH is how a
|
||||
# version nobody meant to run gets run.
|
||||
if(NOT ANDROID)
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS cst-agent-app
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT app)
|
||||
install(FILES linux/cst-agent-app.desktop
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/applications COMPONENT app)
|
||||
install(FILES linux/cst-agent-app.svg
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps
|
||||
COMPONENT app)
|
||||
endif()
|
||||
|
||||
# --- Android (APK) -----------------------------------------------------
|
||||
# The engine is compiled in (top-level CMakeLists: -DCST_BUILD_TUI=OFF,
|
||||
# -DCST_BUILD_WEBUI=OFF, so src/webui_stub.c stands in for the server and
|
||||
|
||||
@@ -45,6 +45,31 @@ navigation drawer) so swapping in `org.kde.kirigami` later is a QML-only
|
||||
change. Point the app at a Kirigami outside the Qt prefix with
|
||||
`-DCST_APP_QML_IMPORT=/path/to/qml`.
|
||||
|
||||
## Install (Linux desktop)
|
||||
|
||||
The window installs itself: the binary, a launcher entry and the icon the
|
||||
launcher draws for it. All three are one CMake component (`app`), so the CLI
|
||||
keeps coming from `scripts/bootstrap_linux.sh` and the profile never holds two
|
||||
`cst-agent` of different vintages; the files live in `app/linux/`.
|
||||
|
||||
```sh
|
||||
# NixOS / nix users: PATH + menu entry + icon. The Qt apps hook bakes the
|
||||
# plugin and QML module paths into the wrapper, so the app runs outside a
|
||||
# nix-shell and nix-collect-garbage cannot pull Qt out from under it.
|
||||
nix-build # ./result/bin/cst-agent-app, run it in place
|
||||
nix profile add --file . # or: ./result/bin/cst-agent-app
|
||||
nix profile remove cst-agent # uninstall
|
||||
|
||||
# anywhere with Qt 6 (Quick/Qml/QuickControls2) and libcurl:
|
||||
cmake -B build-app -DCST_BUILD_APP=ON -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build-app -j
|
||||
cmake --install build-app --component app --prefix ~/.local
|
||||
```
|
||||
|
||||
Launched from a menu, the workspace is the directory the app starts in
|
||||
(`$HOME` there), so sessions land in `~/.agent/sessions`; pass
|
||||
`--workspace <dir>` to pin another one.
|
||||
|
||||
## Why in one process
|
||||
|
||||
The engine is a library, not a daemon: `libcstcore.a` (Makefile) / the
|
||||
@@ -408,6 +433,7 @@ so nothing about it is visible when the app is idle.
|
||||
file card's open/share through the app's FileProvider
|
||||
- [x] a turn keeps running with the app off screen (Android foreground service
|
||||
+ wake lock; nothing to do on a desktop)
|
||||
- [x] desktop install: launcher entry + icon, `nix profile add --file .`
|
||||
- [ ] a real turn driven through the window (needs a provider key)
|
||||
- [ ] SAF file picker, the back gesture, the icon glyphs on the phone
|
||||
- [ ] Kirigami (the rest of the Breeze look: colors, controls), tray,
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=CST-agent
|
||||
GenericName=Local coding agent
|
||||
Comment=The engine in a window: workspace is the directory it is started in
|
||||
Exec=cst-agent-app
|
||||
TryExec=cst-agent-app
|
||||
Icon=cst-agent-app
|
||||
Terminal=false
|
||||
StartupNotify=true
|
||||
# Qt puts the application name in the Wayland app_id / WM_CLASS, so this has to
|
||||
# stay the file's own name or the launcher draws a generic icon.
|
||||
StartupWMClass=cst-agent-app
|
||||
Categories=Development;Utility;
|
||||
Keywords=agent;ai;assistant;code;
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- The launcher icon: the prompt the CLI shows, on a tile in the window's own
|
||||
palette (app/qml/Theme.qml). Installed under hicolor/scalable, so the
|
||||
desktops that want raster sizes render it themselves. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128">
|
||||
<rect x="6" y="6" width="116" height="116" rx="26" ry="26"
|
||||
fill="#15181a" stroke="#343b41" stroke-width="5"/>
|
||||
<path d="M36 42 L56 64 L36 86" fill="none" stroke="#3daee9" stroke-width="12"
|
||||
stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M69 88 H94" fill="none" stroke="#eff0f1" stroke-width="12"
|
||||
stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 712 B |
+99
@@ -0,0 +1,99 @@
|
||||
# NixOS and other nix users: the desktop front end (Qt Quick) with the engine
|
||||
# it embeds, built and installed the nix way.
|
||||
#
|
||||
# nix-build # ./result/bin/cst-agent-app, run it in place
|
||||
# nix-env -f . -i cst-agent # into the user profile: PATH, menu entry, icon
|
||||
#
|
||||
# The Qt apps hook is what makes the installed binary find Qt's plugins and
|
||||
# QML modules outside a nix-shell (this is also what keeps them out of
|
||||
# nix-collect-garbage's reach). Only the window is installed: the CLI has its
|
||||
# own installer, scripts/bootstrap_linux.sh.
|
||||
{ pkgs ? import <nixpkgs> { } }:
|
||||
|
||||
let
|
||||
# The version lives in one place: the header the engine and the APK read.
|
||||
vLine = re:
|
||||
let
|
||||
hit = pkgs.lib.findFirst
|
||||
(l: builtins.match "#define ${re} ([0-9]+)" l != null) ""
|
||||
(pkgs.lib.splitString "\n" (builtins.readFile ./include/cst_version.h));
|
||||
in
|
||||
builtins.head (builtins.match "#define ${re} ([0-9]+)" hit);
|
||||
in
|
||||
pkgs.stdenv.mkDerivation {
|
||||
pname = "cst-agent";
|
||||
version =
|
||||
"${vLine "CST_VERSION_MAJOR"}.${vLine "CST_VERSION_MINOR"}.${vLine "CST_VERSION_PATCH"}";
|
||||
|
||||
# Not the whole checkout: third_party/ (the static curl/mbedTLS/ncurses
|
||||
# sources, the NDK builds), the build directories and the session store are
|
||||
# gigabytes this build never reads -- curl, ncurses and Qt come from nixpkgs.
|
||||
src = builtins.filterSource
|
||||
(path: type:
|
||||
!(builtins.elem (baseNameOf path) [
|
||||
".agent"
|
||||
".cursor"
|
||||
".gen"
|
||||
".git"
|
||||
"build-android-arm64-v8a"
|
||||
"build-apk-android"
|
||||
"build-win-tui"
|
||||
"dist"
|
||||
"result"
|
||||
"third_party"
|
||||
]))
|
||||
./.;
|
||||
|
||||
nativeBuildInputs = with pkgs; [ cmake pkg-config qt6.wrapQtAppsHook ];
|
||||
# qtwayland so a Wayland session gets a native client, qtsvg for the icon
|
||||
# theme: Breeze's icons are SVG, and without the image format plugin the
|
||||
# window falls back to text glyphs.
|
||||
buildInputs = with pkgs; [
|
||||
qt6.qtbase
|
||||
qt6.qtdeclarative
|
||||
qt6.qtwayland
|
||||
qt6.qtsvg
|
||||
curl
|
||||
curl.dev
|
||||
ncurses
|
||||
ncurses.dev
|
||||
];
|
||||
|
||||
cmakeFlags = [ "-DCST_BUILD_APP=ON" "-DCMAKE_BUILD_TYPE=Release" ];
|
||||
|
||||
# The "app" component is the window, its launcher entry and the icon;
|
||||
# app/CMakeLists.txt is the one place that knows where each goes.
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
cmake --install . --component app --prefix "$out"
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
doCheck = true;
|
||||
# One test cannot pass here: tests/test_sandbox.c has the engine run a
|
||||
# sandboxed command and asserts Landlock *denied* the write outside the
|
||||
# workspace, and Landlock does not work inside the nix build sandbox -- the
|
||||
# command succeeds and the assertion trips. The other 40 run; the full set
|
||||
# is `make check` / `ctest` in the dev tree.
|
||||
checkPhase = ''
|
||||
runHook preCheck
|
||||
ctest --output-on-failure -j"$NIX_BUILD_CORES" -E test_sandbox
|
||||
runHook postCheck
|
||||
'';
|
||||
# The tests write settings, sessions, undo files and a scratch workspace:
|
||||
# give them a home of their own.
|
||||
preCheck = ''
|
||||
export HOME="$TMPDIR/home"
|
||||
export XDG_CONFIG_HOME="$HOME/.config" XDG_CACHE_HOME="$HOME/.cache"
|
||||
export XDG_DATA_HOME="$HOME/.local/share"
|
||||
mkdir -p "$XDG_CONFIG_HOME" "$XDG_CACHE_HOME" "$XDG_DATA_HOME"
|
||||
'';
|
||||
|
||||
meta = {
|
||||
description =
|
||||
"Local coding agent: Qt Quick front end with the engine in-process";
|
||||
homepage = "https://git.whcself.xyz/WHC/CST-agent";
|
||||
platforms = pkgs.lib.platforms.linux;
|
||||
mainProgram = "cst-agent-app";
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user