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.
f5a6ff8b0de6f217d2bdfd81809a6fcec1567e79
The PTY was hardcoded to 80x24 at start, so full-screen programs
(htop, vim, less, tmux, top) drew themselves for 80x24 regardless
of the actual xterm window. Add a path for the frontend to push the
real cols/rows to the backend, which calls pty.Setsize on the pty
file.
Backend:
- internal/shell/manager.go: Resize(workspaceID, cols, rows) added to
the Manager interface and implemented on LocalManager. Looks up the
session, type-asserts sess.Stdin.(*os.File), calls pty.Setsize.
Validates cols/rows in [1, 10000] and returns CodeBadRequest on
bad input, CodeNotFound when no session.
- internal/service/shell_service.go: ShellService.Resize wrapper that
checks workspace existence first.
- internal/api/shell_handler.go: resize handler, 204 on success.
- internal/api/router.go: register POST /workspaces/:id/shell/resize.
- internal/model/shell.go: ShellResizeRequest{Cols, Rows}.
- internal/shell/manager_test.go: 3 new tests (valid resize via
pty.Getsize round-trip, invalid size, missing session).
Frontend:
- web/src/lib/api/process.ts: useShellResize mutation hook.
- web/src/components/terminal/TerminalPanel.tsx: terminal.onResize
subscription with 100ms debounce; filters 0x0; only fires when
workspaceId is set; cleanup clears timeout + disposes listener.
E2E: 'stty size' after POST /shell/resize {cols:200, rows:50} returns
'50 200'. 0x40 → 400, missing workspace → 404, valid → 204.
Conversation: 019f360a-5eba-7c81-94d3-e5d58ad3c026
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%