This repository has been archived on 2026-07-17 . You can view files and clone it. You cannot open issues or pull requests or push a commit.
ec54ea44733fb6ff8c152b4562448fd095ddb677
Replace the synchronous useAcpPrompt mutation with a streaming WS
hook. The agent's reply now arrives chunk-by-chunk and the same
agent bubble grows in real time.
- web/src/lib/api/acp.ts:
- Add AcpStreamEvent / AcpStreamStatus types.
- Add useAcpStream(workspaceId) hook: per-prompt WS lifecycle
(idle -> connecting -> open -> closed / error), open(content)
/ close() / onEvent(cb) API. Reuses the reconnection / 1000-1001
/ pinger pattern from useProcessWebSocket.
- Keep useAcpPrompt and useAcpCancel exported for back-compat.
- web/src/components/acp/AcpPanel.tsx:
- Switch to useAcpStream. handleSend pushes an optimistic user
message, opens the stream, sets an inflight agent bubble with
empty text.
- chunk events append to inflight.text; complete clears inflight;
error clears inflight and shows a local error toast.
- renderMessages = coalesced + localUserMessage + inflight.
- Local user message is auto-cleared when the next history poll
surfaces the same text (avoids duplicate render).
- Auto-scroll effect depends on inflight.text so it fires as
chunks stream.
- Cancel button closes the WS (server's read loop triggers
session/cancel).
- Remove the standalone ThinkingIndicator; the in-flight bubble
serves as the streaming affordance.
Conversation: 7B62CB8E-ACC8-4333-BC64-B927C8FDC397
…
codespace
A Go backend skeleton for managing local workspaces and file operations with process orchestration.
V1 scope
- Workspace creation, listing, lookup, and deletion backed by local directories.
- File read/write/list/mkdir/remove/rename/stat through a
FileSysteminterface with path-escape protection. - Workspace root deletion protection — cannot remove the workspace root directory through the file API.
- Configurable per-file write size limit — oversized writes are rejected early before hitting disk.
- OpenCode process start/stop/restart/status per workspace (launched with
opencode acpby default; raw stdio is still piped through the process WebSocket for backward compatibility). - ACP (Agent Client Protocol) client — minimal JSON-RPC over NDJSON against the running opencode process. Exposes
initialize/session/new/session/prompt/session/cancel. Servesfs/read_text_fileandfs/write_text_filefrom the workspace filesystem. The web UI surfaces this as a chat panel alongside the terminal. - HTTP API using Gin for routing with JSON responses.
- Explicit
http.Servercomposition with configurable timeouts and max header bytes. - Structured logging using Go standard library
log/slogwith JSON/text format and level control.
Safety
- File paths are workspace-relative; absolute paths are rejected.
..escape attempts are rejected before filesystem operations.- Deleting the workspace root through the file API is explicitly blocked.
- File content is never logged; structured logging only includes metadata.
- Workspace root paths are not exposed in API responses or logs.
Run
make run
The server listens on :8080 by default.
Configuration
configs/config.yaml:
server:
addr: ":8080"
readTimeout: "15s"
writeTimeout: "15s"
idleTimeout: "60s"
maxHeaderBytes: 1048576
workspace:
root: "./workspaces"
process:
opencodeCommand: "opencode"
args:
- "acp"
shell:
command: "bash"
args:
- "-i"
file:
maxWriteBytes: 1048576
gin:
mode: "release"
log:
level: "info"
format: "json"
Environment overrides
| Variable | Description | Default |
|---|---|---|
CODESPACE_ADDR |
Listen address | :8080 |
CODESPACE_WORKSPACE_ROOT |
Workspace storage root | ./workspaces |
CODESPACE_OPENCODE_COMMAND |
OpenCode binary path | opencode |
CODESPACE_OPENCODE_ARGS |
Comma-separated extra args passed to OpenCode (sets process.args) |
acp |
CODESPACE_SHELL_COMMAND |
Shell binary path | bash |
CODESPACE_SHELL_ARGS |
Comma-separated shell arguments | -i |
CODESPACE_FILE_MAX_WRITE_BYTES |
Max bytes per file write | 1048576 |
CODESPACE_GIN_MODE |
Gin mode (debug, release, test) |
release |
CODESPACE_READ_TIMEOUT |
HTTP read timeout | 15s |
CODESPACE_WRITE_TIMEOUT |
HTTP write timeout | 15s |
CODESPACE_IDLE_TIMEOUT |
HTTP idle timeout | 60s |
CODESPACE_MAX_HEADER_BYTES |
Max HTTP header bytes | 1048576 |
CODESPACE_LOG_LEVEL |
Log level (debug, info, warn, error) |
info |
CODESPACE_LOG_FORMAT |
Log format (json, text) |
json |
Logging
codespace uses the Go standard library log/slog for application logs.
All runtime logging uses slog; fmt, log.Printf, and the Gin default logger are not used.
API
Workspaces
| Method | Path | Description |
|---|---|---|
GET |
/api/workspaces |
List all workspaces |
POST |
/api/workspaces |
Create a workspace ({"id": "name"}) |
GET |
/api/workspaces/:id |
Get workspace by ID |
DELETE |
/api/workspaces/:id |
Delete workspace (stops running process first) |
Files
| Method | Path | Description |
|---|---|---|
GET |
/api/workspaces/:id/files?path=... |
List directory entries |
GET |
/api/workspaces/:id/files/read?path=... |
Read file content |
GET |
/api/workspaces/:id/files/stat?path=... |
Get file metadata (name, path, isDir, size, modTime) |
PUT |
/api/workspaces/:id/files/write |
Write file ({"path": "...", "content": "..."}) |
POST |
/api/workspaces/:id/files/mkdir |
Create directory ({"path": "..."}) |
DELETE |
/api/workspaces/:id/files?path=... |
Remove file or directory recursively |
POST |
/api/workspaces/:id/files/rename |
Rename file or directory ({"oldPath": "...", "newPath": "..."}) |
Process
| Method | Path | Description |
|---|---|---|
POST |
/api/workspaces/:id/process/start |
Start OpenCode process for workspace |
POST |
/api/workspaces/:id/process/stop |
Stop OpenCode process |
POST |
/api/workspaces/:id/process/restart |
Restart OpenCode process |
GET |
/api/workspaces/:id/process/status |
Get process running status and PID |
GET |
/api/workspaces/:id/process/ws |
WebSocket: stream process stdout/stderr, send stdin (text frames, raw bytes) |
Shell
| Method | Path | Description |
|---|---|---|
POST |
/api/workspaces/:id/shell/start |
Start interactive shell for workspace |
POST |
/api/workspaces/:id/shell/stop |
Stop interactive shell |
POST |
/api/workspaces/:id/shell/restart |
Restart interactive shell |
GET |
/api/workspaces/:id/shell/status |
Get shell running status and PID |
GET |
/api/workspaces/:id/shell/ws |
WebSocket: stream shell stdout/stderr, send stdin (text frames, raw bytes) |
ACP (Agent Client Protocol)
Sits on top of the OpenCode process started above. The ACP service starts the process on the first prompt if it isn't already running, performs the initialize + session/new handshake, and accumulates the agent's responses until the session/prompt reply arrives.
| Method | Path | Description |
|---|---|---|
GET |
/api/workspaces/:id/acp/status |
Observational status: {workspaceId, ready, sessionId, running, pid, error}. Does not start the process. |
GET |
/api/workspaces/:id/acp/history |
Accumulated conversation: {sessionId, messages: [{role, text, time}]}, oldest first. In-memory only — cleared when the process restarts. |
POST |
/api/workspaces/:id/acp/prompt |
Body {"content": "..."}. Response {"sessionId, stopReason, text}. Starts the process + session on demand. Synchronous (waits up to 5 min). |
POST |
/api/workspaces/:id/acp/cancel |
Best-effort interrupt of an in-flight prompt. |
terminal/* and other unsupported agent-initiated requests return JSON-RPC Method not found (-32601).
Health
| Method | Path | Description |
|---|---|---|
GET |
/healthz |
Server health check |
Examples
Health check
curl -s http://localhost:8080/healthz
Create a workspace
curl -s -X POST http://localhost:8080/api/workspaces \
-H 'Content-Type: application/json' \
-d '{"id":"user1"}'
List workspaces
curl -s http://localhost:8080/api/workspaces
Write a file
curl -s -X PUT http://localhost:8080/api/workspaces/user1/files/write \
-H 'Content-Type: application/json' \
-d '{"path":"main.go","content":"package main\n"}'
Read a file
curl -s 'http://localhost:8080/api/workspaces/user1/files/read?path=main.go'
Get file stat
curl -s 'http://localhost:8080/api/workspaces/user1/files/stat?path=main.go'
Send a prompt to the agent
curl -s -X POST http://localhost:8080/api/workspaces/user1/acp/prompt \
-H 'Content-Type: application/json' \
-d '{"content":"list the files in this workspace"}'
Test
make test
Languages
Go
52.7%
TypeScript
46%
CSS
1%
JavaScript
0.2%