Files
CST-agent/Makefile
T
WHC ef63f6d612 OpenCode-style TUI, model probing, sandboxed auto approval
- TUI: centered home screen with logo, boxed input with mode line, sidebar
  (>=120 cols), Ctrl+P palette, Tab Build/Plan, Shift+Tab approval cycle,
  mouse (cards, cursor, pickers, wheel), /sidebar /mouse /approval
- /models probing: key check on entry (401 re-asks), API models in /model
- Shell sandbox in C: Linux Landlock (writes limited to workspace/tmp,
  ABI>=4 blocks TCP), macOS sandbox_init, rules fallback elsewhere
- Approval modes ask/auto/yolo (default auto); read-only command whitelist;
  ask to rerun outside the sandbox when a command looks blocked
- Upgradable bootstrap scripts (git pull --ff-only, rebuild, reinstall) and
  cst-agent-update; cst-agent --version
- tests: test_sandbox, test_models
2026-10-05 12:34:46 +08:00

173 lines
5.2 KiB
Makefile

CC ?= gcc
CFLAGS ?= -std=c11 -Wall -Wextra -O2 -g
SD_DIR := vendor/sashidown
CJSON_DIR := vendor/cjson
PKG_CONFIG ?= pkg-config
PREFIX ?= /usr/local
DESTDIR ?=
UNAME_S := $(shell uname -s 2>/dev/null)
UNAME_O := $(shell uname -o 2>/dev/null)
# Android (Termux): pthreads live in libc.
PTHREAD_LIBS := -lpthread
ifeq ($(UNAME_O),Android)
PTHREAD_LIBS :=
endif
# macOS: Homebrew ncurses/curl are keg-only, so point pkg-config at them.
# (macOS ships GNU make 3.81, whose $(shell) ignores exported variables.)
PKG_ENV :=
ifeq ($(UNAME_S),Darwin)
BREW_PREFIX := $(shell brew --prefix 2>/dev/null)
ifneq ($(BREW_PREFIX),)
PKG_ENV := PKG_CONFIG_PATH="$(BREW_PREFIX)/opt/ncurses/lib/pkgconfig:$(BREW_PREFIX)/opt/curl/lib/pkgconfig:$$PKG_CONFIG_PATH"
endif
CPPFLAGS += -D_DARWIN_C_SOURCE
endif
PKGC = $(PKG_ENV) $(PKG_CONFIG)
# Wide-character curses: ncursesw, else ncurses (macOS/BSD build it wide).
CURSES_PC := $(shell for p in ncursesw ncurses; do $(PKGC) --exists $$p 2>/dev/null && { echo $$p; exit 0; }; done)
ifneq ($(CURSES_PC),)
CURSES_CFLAGS := $(shell $(PKGC) --cflags $(CURSES_PC))
CURSES_LIBS := $(shell $(PKGC) --libs $(CURSES_PC))
else ifeq ($(UNAME_S),Darwin)
CURSES_LIBS := -lncurses
else
CURSES_LIBS := -lncursesw
endif
CURL_CFLAGS := $(shell $(PKGC) --cflags libcurl 2>/dev/null)
CURL_LIBS := $(shell $(PKGC) --libs libcurl 2>/dev/null)
ifeq ($(strip $(CURL_LIBS)),)
CURL_LIBS := -lcurl
endif
PKG_CFLAGS := $(CURL_CFLAGS) $(CURSES_CFLAGS)
PKG_LIBS := $(CURL_LIBS) $(CURSES_LIBS)
CPPFLAGS += -Iinclude -I$(SD_DIR)/include -I$(CJSON_DIR) -D_XOPEN_SOURCE_EXTENDED $(PKG_CFLAGS)
LDFLAGS += -L$(SD_DIR)
LDLIBS += -lsashidown -lm $(PKG_LIBS) $(PTHREAD_LIBS)
CORE_SRC = \
$(CJSON_DIR)/cJSON.c \
src/util.c \
src/ctl_parse.c \
src/os_compat.c \
src/sandbox.c \
src/sandbox_exec.c \
src/session.c \
src/tools/tools.c \
src/tools/patch.c \
src/tools/registry.c \
src/tools/fs_tools.c \
src/tools/agent_tools.c \
src/tools/web.c \
src/tools/diff.c \
src/undo.c \
src/permissions.c \
src/settings.c \
src/llm_openai.c \
src/agent_loop.c \
src/agent_store.c \
src/json_baseline.c \
src/creds.c \
src/mm_tool.c \
src/models.c \
src/metrics.c
SRC = $(CORE_SRC) src/tui.c src/main.c
OBJ = $(SRC:.c=.o)
HDRS = $(wildcard include/*.h) $(wildcard src/tools/*.h) $(CJSON_DIR)/cJSON.h
TESTS = \
tests/test_ctl_frontier \
tests/test_ctl_heredoc \
tests/test_json_baseline \
tests/test_replay_starvation \
tests/test_replay_real \
tests/test_mm_tool \
tests/test_llm_body \
tests/test_sse_decode \
tests/test_apply_patch \
tests/test_session_roundtrip \
tests/test_shell \
tests/test_diff \
tests/test_tools_fs \
tests/test_registry \
tests/test_undo \
tests/test_config \
tests/test_models \
tests/test_sandbox
TEST_LIBS = -lm $(CURL_LIBS) $(PTHREAD_LIBS)
.PHONY: all clean check demo sashidown-lib cmake-gcc cmake-clang asan install uninstall
all: sashidown-lib cst-agent
cmake-gcc:
cmake -B build-gcc -DCMAKE_C_COMPILER=gcc -DCST_BUILD_TUI=ON
cmake --build build-gcc
ctest --test-dir build-gcc --output-on-failure
cmake-clang:
cmake -B build-clang -DCMAKE_C_COMPILER=clang -DCST_BUILD_TUI=ON
cmake --build build-clang
sashidown-lib:
$(MAKE) -C $(SD_DIR) libsashidown.a
cst-agent: $(OBJ) $(SD_DIR)/libsashidown.a
$(CC) $(CFLAGS) -o $@ $(OBJ) $(LDFLAGS) $(LDLIBS)
%.o: %.c $(HDRS)
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
src/tui.o: src/tui.c $(HDRS) $(SD_DIR)/include/sashidown.h
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
tests/test_ctl_frontier: tests/test_ctl_frontier.c src/ctl_parse.c include/ctl.h
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ tests/test_ctl_frontier.c src/ctl_parse.c
tests/test_ctl_heredoc: tests/test_ctl_heredoc.c src/ctl_parse.c include/ctl.h
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ tests/test_ctl_heredoc.c src/ctl_parse.c
tests/test_json_baseline: tests/test_json_baseline.c src/json_baseline.c $(CJSON_DIR)/cJSON.c $(HDRS)
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ tests/test_json_baseline.c src/json_baseline.c $(CJSON_DIR)/cJSON.c -lm
tests/test_replay_starvation: tests/test_replay_starvation.c src/ctl_parse.c src/json_baseline.c $(CJSON_DIR)/cJSON.c
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ tests/test_replay_starvation.c src/ctl_parse.c src/json_baseline.c $(CJSON_DIR)/cJSON.c -lm
tests/test_replay_real: tests/test_replay_real.c src/ctl_parse.c src/json_baseline.c $(CJSON_DIR)/cJSON.c
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ tests/test_replay_real.c src/ctl_parse.c src/json_baseline.c $(CJSON_DIR)/cJSON.c -lm
tests/test_mm_tool: tests/test_mm_tool.c src/mm_tool.c include/mm_tool.h
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ tests/test_mm_tool.c src/mm_tool.c
tests/test_%: tests/test_%.c $(CORE_SRC) $(HDRS)
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $< $(CORE_SRC) $(TEST_LIBS)
check: $(TESTS)
@set -e; for t in $(TESTS); do echo "== $$t"; ./$$t; done
# AddressSanitizer + UBSan build of the agent and all tests.
asan:
$(MAKE) clean
$(MAKE) sashidown-lib
$(MAKE) CFLAGS="-std=c11 -Wall -Wextra -O1 -g -fsanitize=address,undefined -fno-sanitize-recover=undefined -fno-omit-frame-pointer" cst-agent check
demo: cst-agent
./cst-agent --demo-ctl --workspace .
clean:
rm -f $(OBJ) cst-agent $(TESTS)
install: cst-agent
install -d "$(DESTDIR)$(PREFIX)/bin"
install -m 0755 cst-agent "$(DESTDIR)$(PREFIX)/bin/cst-agent"
uninstall:
rm -f "$(DESTDIR)$(PREFIX)/bin/cst-agent"