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.
tao.chen ec54ea4473 feat(web): AcpPanel streaming over WebSocket
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
2026-07-06 17:17:47 +08:00
2026-07-02 19:53:00 +08:00

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 FileSystem interface 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 acp by 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. Serves fs/read_text_file and fs/write_text_file from 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.Server composition with configurable timeouts and max header bytes.
  • Structured logging using Go standard library log/slog with 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
S
Description
No description provided
Readme
359 KiB
Languages
Go 52.7%
TypeScript 46%
CSS 1%
JavaScript 0.2%