Merge pull request 'fix: vision(#623): scope Claude chat working directory to project staging checkout (#1027)' (#1089) from fix/issue-1027-1 into main
This commit is contained in:
commit
65df00ea6a
3 changed files with 64 additions and 4 deletions
|
|
@ -20,6 +20,12 @@ OAuth flow:
|
|||
6. Redirects to /chat/
|
||||
|
||||
The claude binary is expected to be mounted from the host at /usr/local/bin/claude.
|
||||
|
||||
Workspace access:
|
||||
- CHAT_WORKSPACE_DIR environment variable: bind-mounted project working tree
|
||||
- Claude invocation uses --permission-mode acceptEdits for code modification
|
||||
- CWD is set to workspace directory when configured, enabling Claude to
|
||||
inspect, explain, or modify code scoped to that tree only
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
|
@ -46,6 +52,10 @@ UI_DIR = "/var/chat/ui"
|
|||
STATIC_DIR = os.path.join(UI_DIR, "static")
|
||||
CLAUDE_BIN = "/usr/local/bin/claude"
|
||||
|
||||
# Workspace directory: bind-mounted project working tree for Claude access
|
||||
# Defaults to empty; when set, Claude can read/write to this directory
|
||||
WORKSPACE_DIR = os.environ.get("CHAT_WORKSPACE_DIR", "")
|
||||
|
||||
# OAuth configuration
|
||||
FORGE_URL = os.environ.get("FORGE_URL", "http://localhost:3000")
|
||||
CHAT_OAUTH_CLIENT_ID = os.environ.get("CHAT_OAUTH_CLIENT_ID", "")
|
||||
|
|
@ -491,12 +501,18 @@ class _WebSocketHandler:
|
|||
return
|
||||
|
||||
try:
|
||||
# Build claude command with permission mode (acceptEdits allows file edits)
|
||||
claude_args = [CLAUDE_BIN, "--print", "--output-format", "stream-json", "--permission-mode", "acceptEdits", message]
|
||||
|
||||
# Spawn claude --print with stream-json for streaming output
|
||||
# Set cwd to workspace directory if configured, allowing Claude to access project code
|
||||
cwd = WORKSPACE_DIR if WORKSPACE_DIR else None
|
||||
proc = subprocess.Popen(
|
||||
[CLAUDE_BIN, "--print", "--output-format", "stream-json", message],
|
||||
claude_args,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True,
|
||||
cwd=cwd,
|
||||
bufsize=1,
|
||||
)
|
||||
|
||||
|
|
@ -1040,12 +1056,18 @@ class ChatHandler(BaseHTTPRequestHandler):
|
|||
# Save user message to history
|
||||
_write_message(user, conv_id, "user", message)
|
||||
|
||||
# Build claude command with permission mode (acceptEdits allows file edits)
|
||||
claude_args = [CLAUDE_BIN, "--print", "--output-format", "stream-json", "--permission-mode", "acceptEdits", message]
|
||||
|
||||
# Spawn claude --print with stream-json for token tracking (#711)
|
||||
# Set cwd to workspace directory if configured, allowing Claude to access project code
|
||||
cwd = WORKSPACE_DIR if WORKSPACE_DIR else None
|
||||
proc = subprocess.Popen(
|
||||
[CLAUDE_BIN, "--print", "--output-format", "stream-json", message],
|
||||
claude_args,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True,
|
||||
cwd=cwd,
|
||||
bufsize=1, # Line buffered
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -705,6 +705,9 @@ COMPOSEEOF
|
|||
- chat-config:/var/chat/config
|
||||
# Chat history persistence: per-user NDJSON files on bind-mounted host volume
|
||||
- ${CHAT_HISTORY_DIR:-./state/chat-history}:/var/lib/chat/history
|
||||
# Workspace directory: bind-mounted project working tree for Claude access (#1027)
|
||||
# Mounted when CHAT_WORKSPACE_DIR is set (defaults to ./workspace)
|
||||
- ${CHAT_WORKSPACE_DIR:-./workspace}:/var/workspace
|
||||
environment:
|
||||
CHAT_HOST: "0.0.0.0"
|
||||
CHAT_PORT: "8080"
|
||||
|
|
@ -718,6 +721,8 @@ COMPOSEEOF
|
|||
# Shared secret for Caddy forward_auth verify endpoint (#709)
|
||||
FORWARD_AUTH_SECRET: ${FORWARD_AUTH_SECRET:-}
|
||||
# Rate limiting removed (#1084)
|
||||
# Workspace directory for Claude code access (#1027)
|
||||
CHAT_WORKSPACE_DIR: ${CHAT_WORKSPACE_DIR:-./workspace}
|
||||
healthcheck:
|
||||
test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8080/health')"]
|
||||
interval: 30s
|
||||
|
|
|
|||
|
|
@ -21,8 +21,17 @@
|
|||
# FORWARD_AUTH_SECRET from kv/disinto/shared/chat
|
||||
# - Seeded on fresh boxes by tools/vault-seed-chat.sh
|
||||
#
|
||||
# Host volume:
|
||||
# Host volumes:
|
||||
# - chat-history → /var/lib/chat/history (persists conversation history)
|
||||
# - workspace → /var/workspace (project working tree for Claude access, #1027)
|
||||
#
|
||||
# Client-side host_volume registration (operator prerequisite):
|
||||
# In nomad/client.hcl on each Nomad node:
|
||||
# host_volume "chat-workspace" {
|
||||
# path = "/var/disinto/chat-workspace"
|
||||
# read_only = false
|
||||
# }
|
||||
# Nodes without the host_volume registered will not schedule the workspace mount.
|
||||
#
|
||||
# Not the runtime yet: docker-compose.yml is still the factory's live stack
|
||||
# until cutover. This file exists so CI can validate it and S5.2 can wire
|
||||
|
|
@ -61,6 +70,21 @@ job "chat" {
|
|||
read_only = false
|
||||
}
|
||||
|
||||
# Workspace volume: bind-mounted project working tree for Claude access (#1027)
|
||||
# Source is a fixed logical name resolved by client-side host_volume registration.
|
||||
volume "workspace" {
|
||||
type = "host"
|
||||
source = "chat-workspace"
|
||||
read_only = false
|
||||
}
|
||||
|
||||
# ── Metadata (per-dispatch env var via NOMAD_META_*) ──────────────────────
|
||||
# CHAT_WORKSPACE_DIR: project working tree path, injected into task env
|
||||
# as NOMAD_META_CHAT_WORKSPACE_DIR for the workspace volume mount target.
|
||||
meta {
|
||||
CHAT_WORKSPACE_DIR = "/var/workspace"
|
||||
}
|
||||
|
||||
# ── Restart policy ───────────────────────────────────────────────────────
|
||||
restart {
|
||||
attempts = 3
|
||||
|
|
@ -115,11 +139,20 @@ job "chat" {
|
|||
read_only = false
|
||||
}
|
||||
|
||||
# Mount workspace directory for Claude code access (#1027)
|
||||
# Binds project working tree so Claude can inspect/modify code
|
||||
volume_mount {
|
||||
volume = "workspace"
|
||||
destination = "/var/workspace"
|
||||
read_only = false
|
||||
}
|
||||
|
||||
# ── Environment: secrets from Vault (S5.2) ──────────────────────────────
|
||||
# CHAT_OAUTH_CLIENT_ID, CHAT_OAUTH_CLIENT_SECRET, FORWARD_AUTH_SECRET
|
||||
# rendered from kv/disinto/shared/chat via template stanza.
|
||||
env {
|
||||
FORGE_URL = "http://forgejo:3000"
|
||||
FORGE_URL = "http://forgejo:3000"
|
||||
CHAT_WORKSPACE_DIR = "${NOMAD_META_CHAT_WORKSPACE_DIR}"
|
||||
}
|
||||
|
||||
# ── Vault-templated secrets (S5.2, issue #989) ─────────────────────────
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue