clawdforge/.env.example
Kayos 940861f70a v0.2: multi-turn /sessions endpoints backed by ACPX
- Dockerfile: install acpx@latest alongside @anthropic-ai/claude-code
- compose.yml: bind /mnt/user/appdata/clawdforge/acpx-sessions:/root/.acpx/sessions
- DB: additive sessions + session_events tables in store.py SCHEMA
- clawdforge/acpx_runner.py: AcpxManager + AcpxSession, bounded async pool,
  per-invocation subprocess model (acpx CLI itself owns the queue-owner
  lifecycle, so each turn = one fresh `acpx prompt -s <uuid>` call)
- server.py: POST/GET/DELETE /sessions + POST /sessions/{id}/turn + GET /sessions
- Per-app isolation: 404 (not 403) on cross-token session access — no
  existence leak across tokens
- Lifespan-managed TTL sweeper: every 60s soft-closes idle sessions past
  CLAWDFORGE_SESSION_TTL_SECS (1h default), hard-deletes ledger rows past
  CLAWDFORGE_SESSION_HARD_TTL_SECS (24h default)
- session_events audit table parallel to existing runs table
  (events: create, turn, close, sweep_close, hard_delete)
- /healthz now reports acpx_present + acpx_version + open_sessions count
- tests/test_sessions.py: 16 tests covering create/turn/close/list/isolation/
  sweep/pool-full/regression. /run regression test asserts byte-identical
  v0.1 response shape.

ACPX research notes (v0.6.1, openclaw/acpx):
- npm package is `acpx`, not `@openclaw/acpx`
- Sessions are scoped by (agentCommand, cwd, name?). We mint our own UUID
  as `--name` and give every session a unique cwd subdir, so the scope key
  is collision-free across apps.
- session_id source: ours. We pass --name <uuid>, ACPX records it under
  ~/.acpx/sessions/<encoded-id>.json. We never need to parse ACPX's
  acpxRecordId — our UUID is canonical.
- Subprocess lifetime: per-invocation, NOT per-session. The acpx CLI itself
  spawns/maintains a per-session "queue owner" process via local IPC; each
  `acpx prompt` call we make either elects itself owner or enqueues. The
  AcpxSession class is therefore a thin (uuid, cwd, asyncio.Lock) handle,
  not a long-lived stdio pipe. The spec's "owns one stdio pipe pair" model
  was rewritten to match reality — flagged here.
- Close semantics: soft-close via `acpx sessions close <name>`. The
  on-disk record stays (ACPX's `sessions prune` is the hard-delete path,
  not invoked from clawdforge). DELETE /sessions/<id> is documented as
  idempotent (200 with already_closed=true on second call) so SDKs can
  call close() in finally/Drop blocks safely.
- File uploads: ACPX has no file-attach ACP method exposed via the CLI.
  We prepend a [Attached files] header listing absolute paths; the agent
  uses its Read tool to open them. Same behavior as /run --files in v0.1.
- Permissions: --approve-all on the turn invocation since the container is
  unattended and callers are bearer-token-trusted. Future v0.3 may expose
  a per-session permission policy.

/run endpoint unchanged — backwards compat verified by
test_run_endpoint_unchanged + test_run_endpoint_unchanged_error_shape.

Spec: memory/spec-clawdforge-v0.2.md
ACPX CLI ref: https://github.com/openclaw/acpx/blob/main/docs/CLI.md
2026-04-29 06:22:55 -07:00

43 lines
1.7 KiB
Text

# clawdforge — copy to .env on Lucy at /mnt/cache/appdata/secrets/clawdforge.env
# (chmod 600, root:root)
# Bind
BIND_HOST=0.0.0.0
BIND_PORT=8800
# Bootstrap admin token. Used to mint per-app tokens via /admin/tokens.
# Once the SQLite db has any token, this var becomes a "root override" and
# should be rotated or unset.
ADMIN_BOOTSTRAP_TOKEN=change-me-32-bytes-of-entropy
# IP allowlist applied to ALL requests. CIDR list, comma-separated.
# 172.24.0.0/16 = sulkta bridge (where clawdforge sits with peer apps)
# 172.17.0.0/16 = docker0 default (some legacy apps still here)
# 192.168.0.0/24 = LAN clients
# Loopback always allowed.
ALLOW_CIDRS=172.24.0.0/16,172.17.0.0/16,192.168.0.0/24
# Default claude config (per-request override allowed)
CLAUDE_BIN=claude
DEFAULT_MODEL=sonnet
DEFAULT_TIMEOUT_SECS=120
# ACPX (multi-turn /sessions endpoints). Reuses Claude Code auth at /root/.claude.
ACPX_BIN=acpx
# Working directory for each session's CWD (acpx scopes by cwd; we give each session its own subdir).
ACPX_SESSIONS_CWD=/data/acpx-cwds
# Max simultaneously-open (non-closed) sessions across all apps. New /sessions returns 503 if at cap.
CLAWDFORGE_MAX_LIVE_SESSIONS=32
# How long an idle session lives before the sweeper soft-closes it. Counted from last_turn_at (or
# created_at if no turn ever ran).
CLAWDFORGE_SESSION_TTL_SECS=3600
# How long a closed session record stays before hard-delete (ledger row + acpx on-disk metadata).
CLAWDFORGE_SESSION_HARD_TTL_SECS=86400
# Sweep cadence in seconds.
CLAWDFORGE_SWEEP_INTERVAL_SECS=60
# Run-staging area inside the container (don't change unless you also change compose mount)
RUNS_DIR=/data/runs
# SQLite db path (don't change unless you also change compose mount)
DB_PATH=/data/clawdforge.db