Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/cache-warm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ jobs:
os: macos-latest
# - target: x86_64-apple-darwin
# os: macos-latest
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
pam: true
steps:
- uses: actions/checkout@v6

Expand All @@ -57,6 +60,10 @@ jobs:
if: runner.os == 'macOS'
run: brew install protobuf

- name: Install libpam dev headers (PAM build)
if: ${{ matrix.pam }}
run: sudo apt-get update && sudo apt-get install -y libpam0g-dev

- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
Expand All @@ -73,3 +80,9 @@ jobs:
# re-saving), but the restore still refreshes the eviction timer.
- name: Build release dependencies
run: cargo build --release --workspace --bins --target ${{ matrix.target }}

# Warm the PAM-only dependency (pam-sys) so the release run's PAM rebuild
# restores from cache too.
- name: Build PAM dependencies
if: ${{ matrix.pam }}
run: cargo build --release -p agentd-core --features pam --target ${{ matrix.target }}
38 changes: 38 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,44 @@ jobs:
- name: Run Docker integration tests (orchestrator)
run: cargo test -p orchestrator -- --ignored --test-threads=1

pam-feature:
name: PAM feature build (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-latest, ubuntu-latest]
steps:
- uses: actions/checkout@v6

- name: Install protoc and libpam (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler libpam0g-dev

- name: Install protoc (macOS)
if: runner.os == 'macOS'
run: brew install protobuf

- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
components: clippy

- uses: Swatinem/rust-cache@v2
with:
key: pam-feature-${{ matrix.os }}

# The `pam` feature links the system PAM library and builds on both
# Linux (Linux-PAM) and macOS (OpenPAM). Build + clippy keep the
# feature-gated code compiling on each. The verifier's runtime behavior
# needs a live PAM stack and is exercised by the `--ignored` pam_smoke
# tests (see docs/pam-authentication.md).
- name: Build agentd-core with PAM
run: cargo build -p agentd-core --features pam

- name: Clippy agentd-core with PAM
run: cargo clippy -p agentd-core --features pam --all-targets -- -D warnings

audit:
name: Security Audit
runs-on: ubuntu-latest
Expand Down
28 changes: 26 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ jobs:
# (1h+ builds) that cross-compiling beats building natively.
# - target: x86_64-apple-darwin
# os: macos-latest
# Dynamically-linked glibc build with PAM (system-user login) compiled
# in, shipped as a separate `-pam` tarball. PAM `dlopen`s its modules
# at runtime, which the static musl targets above cannot do — so a PAM
# artifact must be a glibc build. The default install stays musl/no-PAM;
# this is opt-in (install.sh AGENTD_PAM=1). glibc reduces portability:
# it requires a glibc at least as new as the runner's.
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
pam: true
steps:
- uses: actions/checkout@v6
with:
Expand All @@ -64,6 +73,11 @@ jobs:
if: runner.os == 'macOS'
run: brew install protobuf

# The `pam` feature links libpam, so the dev headers must be present.
- name: Install libpam dev headers (PAM build)
if: ${{ matrix.pam }}
run: sudo apt-get update && sudo apt-get install -y libpam0g-dev

- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
Expand All @@ -81,6 +95,13 @@ jobs:
- name: Build release binaries
run: cargo build --release --workspace --bins --target ${{ matrix.target }}

# Opt-in: overwrite agentd-core with a PAM-enabled build. Done as a
# separate `-p agentd-core` build (not a workspace `--features`) since the
# `pam` feature only exists on that crate — mirrors `cargo xtask`.
- name: Rebuild agentd-core with PAM support
if: ${{ matrix.pam }}
run: cargo build --release -p agentd-core --features pam --target ${{ matrix.target }}

- name: Download UI assets
uses: actions/download-artifact@v4
with:
Expand All @@ -98,6 +119,9 @@ jobs:
set -euo pipefail
TARGET="${{ matrix.target }}"
VERSION="${{ github.ref_name }}"
# PAM builds ship under a distinct `-pam` suffix so they sit alongside
# the default (musl/no-PAM) artifact rather than replacing it.
SUFFIX="${{ matrix.pam && '-pam' || '' }}"
BINS=(
agentd-ask
agentd-communicate
Expand All @@ -119,12 +143,12 @@ jobs:
cp "target/${TARGET}/release/cli" "stage/agent"
chmod 755 stage/agent stage/agentd-*
mkdir -p dist
tar -czf "dist/agentd-${VERSION}-${TARGET}.tar.gz" -C stage .
tar -czf "dist/agentd-${VERSION}-${TARGET}${SUFFIX}.tar.gz" -C stage .

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: tarball-${{ matrix.target }}
name: tarball-${{ matrix.target }}${{ matrix.pam && '-pam' || '' }}
path: dist/*.tar.gz
if-no-files-found: error

Expand Down
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 60 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,25 @@
# make test Run all tests
# make docker-build-claude Build the Claude Code Docker image locally

.PHONY: help build test clippy fmt fmt-fix docker-build-claude docker-build-claude-multiarch docker-run-claude
.PHONY: help build test clippy fmt fmt-fix \
build-release build-ui \
install-user install-user-pam install-system install-system-pam \
uninstall-user uninstall-system \
docker-build-claude docker-build-claude-multiarch docker-run-claude

# Default image name — matches the DEFAULT_IMAGE constant in crates/wrap/src/docker.rs
CLAUDE_IMAGE ?= agentd-claude:latest

# Set PAM=1 to compile agentd-core with system-user (PAM) login support.
# macOS needs no extra packages; on Linux install the PAM dev headers first
# (libpam0g-dev on Debian/Ubuntu, pam-devel on RHEL/Fedora).
PAM ?= 0

# Freshly-built CLI binary. It is the workspace bin `cli`; the installer renames
# it to `agent` on install. The installed `agent` (on PATH) is used to uninstall.
CLI_BIN := target/release/cli
UI_DIST := ui/dist

help: ## Show this help message
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-24s\033[0m %s\n", $$1, $$2}'
Expand All @@ -32,6 +46,51 @@ fmt: ## Check formatting
fmt-fix: ## Auto-fix formatting
cargo fmt --all

# ── Install ──────────────────────────────────────────────────────────
#
# Four supported flows (all build from source, then run `agent install`):
#
# make install-user # per-user install (intended for macOS dev)
# make install-user-pam # … with PAM login
# make install-system # system-wide install (intended for Linux)
# make install-system-pam # … with PAM login
#
# PAM=1 also works on the base targets, e.g. `make install-user PAM=1`.
#
# PAM caveats:
# - Linux needs the PAM dev headers at build time (see PAM var above).
# - A Linux system install is what lets core verify other users' passwords;
# for real PAM auth its service account must also be in the `shadow` group
# (or use an SSSD stack). See docs/pam-authentication.md.

build-release: ## Build release binaries (PAM=1 compiles agentd-core with PAM)
cargo build --release --workspace --bins
@if [ "$(PAM)" = "1" ]; then \
echo "==> Rebuilding agentd-core with PAM support (libpam dev headers required on Linux)"; \
cargo build --release -p agentd-core --features pam; \
fi

build-ui: ## Build the web UI assets (bun)
cd ui && bun install --frozen-lockfile && bun run build

install-user: build-release build-ui ## Per-user install (macOS dev); PAM=1 for system-user login
$(CLI_BIN) install --user --bin-src target/release --ui-dir $(UI_DIST)

install-system: build-release build-ui ## System-wide install (Linux); PAM=1 for system-user login
$(CLI_BIN) install --system --bin-src target/release --ui-dir $(UI_DIST)

install-user-pam: ## Per-user install with PAM (macOS dev)
$(MAKE) install-user PAM=1

install-system-pam: ## System-wide install with PAM (Linux)
$(MAKE) install-system PAM=1

uninstall-user: ## Remove a per-user install (uses the installed `agent`)
agent uninstall --user

uninstall-system: ## Remove a system-wide install (uses the installed `agent`)
agent uninstall --system

# ── Docker ───────────────────────────────────────────────────────────

docker-build-claude: ## Build the Claude Code agent Docker image locally
Expand Down
32 changes: 31 additions & 1 deletion contrib/scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
# Environment variables:
# AGENTD_VERSION Install a specific version (e.g. "v0.5.0" or "0.5.0")
# instead of the latest release.
# AGENTD_PAM When set to 1/true/yes/on, install the PAM-enabled build
# (system-user login). Only available as a prebuilt artifact
# for Linux x86_64 (a dynamically-linked glibc tarball — the
# static musl default cannot load PAM modules). On any other
# platform, build from source with `--features pam` instead.
# PREFIX Install prefix honoured by `agent install`
# (default: /usr/local on macOS or as root, ~/.local otherwise).
#
Expand All @@ -26,6 +31,15 @@ REPO="geoffjay/agentd"
info() { printf '\033[0;34m==>\033[0m %s\n' "$1"; }
error() { printf '\033[0;31merror:\033[0m %s\n' "$1" >&2; exit 1; }

# Truthy test for opt-in flags, matching the AGENTD_PAM semantics used by the
# Rust installer and config loader (1/true/yes/on, case-insensitive).
is_truthy() {
case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in
1 | true | yes | on) return 0 ;;
*) return 1 ;;
esac
}

# Map uname output to a release target triple.
detect_target() {
os=$(uname -s)
Expand Down Expand Up @@ -109,7 +123,23 @@ verify_checksum() {
main() {
target=$(detect_target)
version=$(resolve_version)
asset="agentd-${version}-${target}.tar.gz"

# Opt-in PAM build. Only Linux x86_64 ships a prebuilt PAM artifact: it is a
# dynamically-linked glibc tarball (`-pam` suffix, target
# x86_64-unknown-linux-gnu), because the static musl default cannot dlopen
# PAM modules. Any other platform must build from source.
suffix=""
if is_truthy "${AGENTD_PAM:-}"; then
if [ "$target" = "x86_64-unknown-linux-musl" ]; then
target="x86_64-unknown-linux-gnu"
suffix="-pam"
else
error "AGENTD_PAM is only available as a prebuilt artifact for Linux x86_64 (glibc).
For this platform ($(uname -s)/$(uname -m)), build from source with: cargo build -p agentd-core --features pam"
fi
fi

asset="agentd-${version}-${target}${suffix}.tar.gz"
base="https://github.com/$REPO/releases/download/$version"

# Explicit template: unlike `mktemp -d` bare, this honours $TMPDIR on
Expand Down
1 change: 0 additions & 1 deletion crates/cli/src/commands/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,6 @@ monitor_url = "http://localhost:17003"
memory_url = "http://localhost:17008"
communicate_url = "http://localhost:17010"
knowledge_url = "http://localhost:17011"
index_url = "http://localhost:17012"

# ---------------------------------------------------------------------------
# [services.mcp] — MCP server (no dedicated port — uses stdio transport)
Expand Down
5 changes: 3 additions & 2 deletions crates/cli/src/commands/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,8 +511,9 @@ async fn list(
]));

for mem in &response.items {
let content_preview = if mem.content.len() > 50 {
format!("{}…", &mem.content[..49])
let content_preview = if mem.content.chars().count() > 50 {
let truncated: String = mem.content.chars().take(49).collect();
format!("{truncated}…")
} else {
mem.content.clone()
};
Expand Down
Loading
Loading