Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
# SPDX-License-Identifier: Apache-2.0
CODESPACE_HTTP_HOST=127.0.0.1
CODESPACE_HTTP_PORT=8787
# This file is a reference; CodeSpace does not load it automatically.
# Optional static Bearer for HTTP experiments. Leave empty to disable.
# Never commit a real token. The server never logs this value.
CODESPACE_HTTP_TOKEN=

# JSON workspace registry (see docs/workspaces.example.json).
# CODESPACE_CONFIG=
# Absolute path to persistent patch and instruction records, outside managed roots.
# CODESPACE_OPERATIONS_DB=/absolute/path/to/data/operations.sqlite
# Required unless the patch helper is beside codespace-mcp.
# CODESPACE_PATCH_BIN=/absolute/path/to/codespace-patch
# Operator-selected command timeout (default 30 seconds).
# CODESPACE_PROCESS_TIMEOUT_SECS=300
# Optional same-host worker; does not itself sandbox commands.
# CODESPACE_RUNNER=uds
# CODESPACE_RUNTIME_BIN=/absolute/path/to/codespace-codex-runtime
# Linux command isolation requires a successful helper probe.
# CODESPACE_LINUX_SANDBOX_BIN=/absolute/path/to/codespace-linux-sandbox
103 changes: 41 additions & 62 deletions README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,68 +2,47 @@

[English](README.md) | [한국어](README.ko.md)

개인용 **실행 도구 MCP 서버**입니다. ChatGPT, Cursor 또는 다른 MCP
클라이언트가 무엇을 할지 판단합니다. 이 프로세스는 워크스페이스 파일을
읽고, 고정된 Rust `codex-apply-patch` 엔진으로 Codex 형식 패치를 적용하며,
등록된 워크스페이스에서 관리형 명령을 실행합니다.

서버는 [`rmcp`](https://github.com/modelcontextprotocol/rust-sdk)로 만든
**Cargo 워크스페이스**입니다(stdio와 Streamable HTTP). TypeScript
게이트웨이와 폐기된 `native/patch-worker`는 없습니다. 게이트웨이는 JSON
stdin/stdout으로 Rust `codespace-patch` 헬퍼와 대화합니다. 그 헬퍼
프로세스는 Codex를 **프로세스 내부에서** 호출합니다. `exec_command`는
현재 워크스페이스를 cwd로 하는 **호스트** 프로세스
(`tokio::process::Command`)를 띄웁니다. 격리된 Linux 디스패치가 목표
러너 경계이며, 현재 exec 경로는 아닙니다. 선택적 `CODESPACE_RUNNER=uds`는
CodeSpace JSON으로 `codespace-codex-runtime`과 대화합니다. 그것은 Linux
격리가 아닙니다.

다음이 **아닙니다**.

- CoS 또는 cokacremote의 포크
- Codex 에이전트 래퍼
- 내부에서 모델을 호출하는 호스트
- 완료된 Linux 샌드박스 러너

내부 모델 호출은 없습니다. MCP 클라이언트가 판단을 소유하고, CodeSpace는
실행 계약을 소유합니다. 경로 정책, 작업 멱등성, 패치 적용/롤백 보고,
프로세스 수명입니다.

## 상태

실제 MCP 도구는 `workspace_info`, `read`, `find`, `apply_patch`,
`operation_status`, `exec_command`, `write_stdin`, `read_process`,
`terminate_process`, `work_open`, `steer_status`, `steer_claim_next`,
`steer_complete`, `work_finish`입니다. Codex V4A 적용은
`codespace-patch` 헬퍼 안의 `crates/patch`입니다
([docs/upstream-lock.md](docs/upstream-lock.md) 참고).
Codex 제품 런타임은 제외합니다. 프리미티브 재사용:
[docs/codex-reuse.md](docs/codex-reuse.md).
실행 전용(Responses API 없음):
[docs/execution-substrate.md](docs/execution-substrate.md).
지연된 사용자 의도는 MCP가 아니라 HTTP `/inbox`에서 편집합니다.

현재와 목표 프로세스 배치:
[docs/architecture.md](docs/architecture.md).
설치, HTTP/stdio, 로그, 복구:
[docs/operations.md](docs/operations.md).
기능을 `main`에 직접 커밋하지 마세요.

## 실행

```bash
cargo run -p codespace-server --bin codespace-mcp
# Streamable HTTP at /mcp (default 127.0.0.1:8787); user inbox at /inbox:
cargo run -p codespace-server --bin codespace-mcp -- --http
```

시험: `cargo test --workspace`와
`cargo test --manifest-path crates/patch/Cargo.toml`와
`cargo test --manifest-path crates/codex-runtime/Cargo.toml`. ChatGPT Custom
Connector 절차와 **검증하지 않은** 항목:
[docs/chatgpt-connector.md](docs/chatgpt-connector.md).
운영자 설치: [docs/operations.md](docs/operations.md).
CodeSpace는 외부 코딩 에이전트가 작업 공간의 파일을 읽고 수정하며 명령을 실행할 수 있게 하는 MCP(Model Context Protocol) 서버입니다. 에이전트가 작업을 계획하고 결과를 해석하면, CodeSpace는 접근 권한을 확인하고 작업을 실행하며 실행 상태를 관리합니다. 자체적으로 모델을 호출하거나 에이전트 루프를 실행하지 않습니다.

<a id="실행"></a>
<a id="실행"></a>

## 작업 공간을 등록하고 시작하기

[설치와 첫 연결](docs/ko/operations.md)에서 서버·패치 도우미 빌드, 프로젝트 디렉터리 등록, stdio 또는 Streamable HTTP 연결 순서를 확인하세요. 작업 공간을 등록하지 않고 서버만 시작하면 접근할 수 있는 프로젝트가 없습니다.

이후 [Agent Loop 연동 가이드](docs/ko/agent-integration.md)에 따라 읽기 → 수정 → 실행 → 결과 확인 흐름을 구성하세요. 취소와 결과가 불확실한 경우의 처리도 설명합니다. 상세 문서는 [문서 안내](docs/ko/index.md)에서 찾을 수 있습니다.

## 제공하는 도구

| 목적 | 도구 |
| --- | --- |
| 실행 환경과 파일 확인 | `workspace_info`, `find`, `read` |
| 패치 적용과 기록된 상태 조회 | `apply_patch`, `operation_status` |
| 프로세스 실행과 제어 | `exec_command`, `read_process`, `write_stdin`, `terminate_process` |
| 작업과 사용자 추가 지시 관리 | `work_open`, `steer_status`, `steer_claim_next`, `steer_complete`, `work_finish` |

두 전송 방식에서 같은 도구를 사용할 수 있습니다. HTTP `/inbox` API는 사용자용 클라이언트가 지시 초안을 관리하는 JSON API입니다. 브라우저에서 사용하는 받은 편지함 화면은 제공하지 않습니다.

<a id="상태"></a>
<a id="상태"></a>

## 실행 방식과 현재 제약

기본 러너는 서버 호스트에서 실행합니다. 선택적으로 Unix 소켓을 사용하는 별도 프로세스에 실행을 맡길 수 있지만, 실행 위치는 같은 호스트입니다. Linux에서는 샌드박스 도우미의 사용 가능 검사가 성공하면 명령 격리와 네트워크 정책이 적용됩니다. UDS 사용과 샌드박스 활성화는 별개입니다. 자세한 조건은 [러너 격리](docs/ko/runner-isolation.md)를 참고하세요.

패치, 터미널 세션, 파일 시스템 접근, Linux 샌드박스에는 특정 버전에 고정한 Codex 실행 라이브러리를 사용합니다. [Codex 재사용 범위](docs/ko/codex-reuse.md)에서 실제 연결된 구성 요소와 CodeSpace가 담당하는 책임을 설명합니다.

에이전트를 연동할 때는 다음 제약을 반영해야 합니다.

- 프로세스 결과에는 출력과 EOF가 있지만 종료 코드는 없습니다. EOF만으로 테스트 성공을 판단할 수 없습니다.
- 프로세스 출력의 보관 크기가 제한되어 있으며, 유실된 출력을 알리는 별도 필드는 없습니다.
- 명령이 실행 중이면 같은 작업 공간에서 다른 명령이나 패치를 실행할 수 없습니다. 개발 서버를 켜 둔 채 같은 작업 공간을 수정하는 흐름에는 제약이 있습니다.
- 서버를 재시작하면 프로세스 핸들이 사라집니다. 패치 작업 기록은 데이터베이스 경로를 설정한 경우에만 유지됩니다.
- 컨테이너 실행과 완전한 OAuth 서버는 구현되어 있지 않습니다. 실제 ChatGPT 계정 연결도 아직 검증되지 않았습니다.

<a id="라이선스"></a>

## 라이선스

Apache License 2.0. [LICENSE](LICENSE)와 [NOTICE](NOTICE)를 보세요.
Apache License 2.0을 따릅니다. [LICENSE](LICENSE)와 [의존성 출처 고지](NOTICE)를 확인하세요.
98 changes: 37 additions & 61 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,67 +2,43 @@

[English](README.md) | [한국어](README.ko.md)

Personal **execution-tools MCP server**. ChatGPT, Cursor, or another MCP
client decides what to do. This process reads workspace files, applies
Codex-format patches through a pinned Rust `codex-apply-patch` engine,
and runs managed commands in the registered workspace.

The server is a **Cargo workspace** built with [`rmcp`](https://github.com/modelcontextprotocol/rust-sdk)
(stdio and Streamable HTTP). There is no TypeScript gateway and no legacy
`native/patch-worker`. The gateway talks to a Rust `codespace-patch`
helper over JSON stdin/stdout. That helper process calls Codex
**in-process**. `exec_command` currently spawns a **host** process
(`tokio::process::Command`) with the workspace as cwd. Isolated Linux
dispatch is the target runner boundary, not the current exec path.
Opt-in `CODESPACE_RUNNER=uds` talks CodeSpace JSON to
`codespace-codex-runtime`; that is not Linux isolation.

This is **not**:

- a fork of CoS or cokacremote
- a Codex agent wrapper
- a host that calls a model internally
- a completed Linux sandbox runner

There are no internal model calls. The MCP client owns judgment; CodeSpace
owns execution contracts: path policy, operation idempotency, patch
apply/rollback reporting, and process lifetime.

## Status

Live MCP tools include `workspace_info`, `read`, `find`, `apply_patch`,
`operation_status`, `exec_command`, `write_stdin`, `read_process`,
`terminate_process`, `work_open`, `steer_status`, `steer_claim_next`,
`steer_complete`, and `work_finish`. Codex V4A apply is `crates/patch`
inside the `codespace-patch` helper
(see [docs/upstream-lock.md](docs/upstream-lock.md)).
Codex product runtime stays out; primitive reuse:
[docs/codex-reuse.md](docs/codex-reuse.md).
Execution-only (no Responses API):
[docs/execution-substrate.md](docs/execution-substrate.md).
Deferred user intent is edited on HTTP `/inbox` (not MCP).

Current vs target process layout:
[docs/architecture.md](docs/architecture.md).
Install, HTTP/stdio, logs, and recovery:
[docs/operations.md](docs/operations.md).
Do not commit features directly to `main`.

## Run

```bash
cargo run -p codespace-server --bin codespace-mcp
# Streamable HTTP at /mcp (default 127.0.0.1:8787); user inbox at /inbox:
cargo run -p codespace-server --bin codespace-mcp -- --http
```

Tests: `cargo test --workspace` and
`cargo test --manifest-path crates/patch/Cargo.toml` and
`cargo test --manifest-path crates/codex-runtime/Cargo.toml`. ChatGPT Custom
Connector steps and what is **not** verified:
[docs/chatgpt-connector.md](docs/chatgpt-connector.md).
Operator install: [docs/operations.md](docs/operations.md).
CodeSpace is an MCP (Model Context Protocol) server. It gives an external coding agent a workspace it can inspect, edit, and run commands in through MCP. Your agent plans the work and interprets results; CodeSpace checks workspace permissions, performs operations, and keeps their execution state. It does not call a model or run an agent loop.

<a id="run"></a>

## Start with a registered workspace

Follow [installation and first connection](docs/operations.md) to build the server and patch helper, register a project directory, and connect over stdio or Streamable HTTP. Starting the binary without a workspace registry leaves it with no accessible projects.

Then use the [Agent Loop integration guide](docs/agent-integration.md) for the read → patch → run → inspect cycle, including cancellation and uncertain results. The [documentation overview](docs/index.md) points to reference material.

## Available tools

| Purpose | Tools |
| --- | --- |
| Inspect the environment and files | `workspace_info`, `find`, `read` |
| Apply a patch and retrieve its recorded state | `apply_patch`, `operation_status` |
| Run and control a process | `exec_command`, `read_process`, `write_stdin`, `terminate_process` |
| Track a logical job and queued user instructions | `work_open`, `steer_status`, `steer_claim_next`, `steer_complete`, `work_finish` |

Both transports expose the same tools. The HTTP `/inbox` API lets a user-facing client manage instruction drafts; it is a JSON API, not a browser inbox application.

<a id="status"></a>

## Execution and current limits

The default runner executes on the server host. An optional Unix-socket worker moves execution into a separate process on that same host. On Linux, a successful sandbox-helper probe enables command isolation and network enforcement. These are separate choices: UDS alone does not provide sandboxing. See [runner isolation](docs/runner-isolation.md).

CodeSpace reuses pinned Codex execution libraries for patches, terminal sessions, filesystem operations, and Linux sandboxing. [Codex reuse](docs/codex-reuse.md) explains which components are connected and which responsibilities stay in CodeSpace.

For agent integrations, account for these limits:

- Process results expose output and EOF, but no exit code. EOF alone cannot establish that a test passed.
- Process output is bounded; dropped output has no explicit flag in the MCP result.
- A live command blocks another command or patch in the same workspace. A development server cannot remain running while that workspace is patched.
- Process handles do not survive server restart. Patch-operation records persist only when a database path is configured.
- Container dispatch and a full OAuth server are not implemented. A live ChatGPT account connection remains unverified.

## License

Apache License 2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE).
Apache License 2.0. See [LICENSE](LICENSE) and [dependency attribution](NOTICE).
17 changes: 11 additions & 6 deletions deploy/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
# Deploy examples
# Container isolation fixture

See [docs/operations.md](../docs/operations.md) for install, env vars,
recovery, and what is unverified.
This fixture starts a non-root `sleep infinity` process. It does not install
CodeSpace, start an MCP server, or receive Runner commands. Registering a
`linux-container` environment does not make it an implemented backend.

- `Dockerfile` / `compose.yml`: unprivileged Linux runner. The only bind
mount is `${CODESPACE_WORKSPACE}` → `/workspace`. Do not add host home,
SSH agent, Docker socket, or gateway secrets.
`Dockerfile` / `compose.yml` mount only `${CODESPACE_WORKSPACE}` at `/workspace`.
Do not add host home directories, SSH agent or Docker sockets, or gateway secrets.

For an operational MCP server and the optional Linux sandbox helper, follow
[installation and operations](../docs/operations.md). The distinction between
host execution, the UDS worker, and command isolation is explained in
[runner isolation](../docs/runner-isolation.md).
2 changes: 1 addition & 1 deletion docs-site/.vitepress/theme/PageTools.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ async function copyPage() {
if (!response.ok) throw new Error('Markdown is unavailable');
const text = await response.text();
await navigator.clipboard.writeText(text);
if (route.path === current) status.value = ko.value ? '원문을 복사했습니다.' : 'Original Markdown copied.';
if (route.path === current) status.value = ko.value ? '이 페이지의 Markdown을 복사했습니다.' : 'This page’s Markdown was copied.';
} catch {
if (route.path === current) status.value = ko.value ? '복사하지 못했습니다. 브라우저 권한을 확인하세요.' : 'Copy failed. Check browser permissions.';
} finally { busy.value = false; }
Expand Down
6 changes: 3 additions & 3 deletions docs-site/.vitepress/theme/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ export default {
},
'home-features-after': () => {
const ko = lang.value.startsWith('ko');
const labels = ko ? ['빌드', '검증', '산출물', '게시'] : ['Build', 'Verify', 'Artifact', 'Publish'];
return h('section', { class: 'deployment-path', 'aria-label': ko ? '배포 순서' : 'Deployment steps' }, [
h('p', { class: 'deployment-caption' }, ko ? '한 번 검증한 산출물로 게시합니다.' : 'Publish the artifact you verified.'),
const labels = ko ? ['작업 공간 등록', 'MCP 연결', '읽기·수정·실행', '결과 확인'] : ['Register a workspace', 'Connect MCP', 'Read, edit, run', 'Inspect results'];
return h('section', { class: 'deployment-path', 'aria-label': ko ? 'CodeSpace 사용 순서' : 'CodeSpace workflow' }, [
h('p', { class: 'deployment-caption' }, ko ? '에이전트가 작업을 판단하고, CodeSpace가 실행합니다.' : 'Your agent directs the work. CodeSpace executes it.'),
h('ol', labels.map((text, index) => h('li', [h('span', { class: 'step-number', 'aria-hidden': 'true' }, String(index + 1).padStart(2, '0')), text]))),
]);
},
Expand Down
32 changes: 22 additions & 10 deletions docs-site/scripts/site.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,25 @@ def output_markdown(route):
return route.lstrip('/') + '.md'


def home_features(inventory, locale):
descriptions = {
'en': [
('getting-started', 'Get started', 'Understand what CodeSpace provides, its limits, and how to connect your first workspace.'),
('agent-integration', 'Connect an Agent Loop', 'Follow tool calls from reading and patching through execution, cancellation, and recovery.'),
('operations', 'Operate and troubleshoot', 'Build the helpers, register workspaces, configure transports, and diagnose failures.'),
],
'ko': [
('getting-started', '시작하기', 'CodeSpace의 기능과 한계를 살펴보고 첫 작업 공간을 연결하는 방법을 알아봅니다.'),
('agent-integration', 'Agent Loop 연동', '읽기와 패치부터 명령 실행, 취소, 복구까지 도구 호출 순서를 따라갑니다.'),
('operations', '설치·운영·문제 해결', '도우미 빌드, 작업 공간 등록, 전송 방식 설정과 오류 확인 방법을 설명합니다.'),
],
}
return [{'title': title, 'details': details,
'link': next(page['route'] for page in inventory
if page['id'] == ident and page['locale'] == locale)}
for ident, title, details in descriptions[locale]]


def prepare(root=ROOT):
root = root.resolve()
commit = git(root, 'rev-parse', 'HEAD')
Expand All @@ -229,19 +248,12 @@ def prepare(root=ROOT):
copy = source / 'public' / page['copy']
copy.parent.mkdir(parents=True, exist_ok=True)
copy.write_bytes(raw)
group_rank = {group['id']: index for index, group in enumerate(groups)}
en_guides = [page for page in inventory if page['locale'] == 'en']
en_guides.sort(key=lambda page: (group_rank[page['section']], page['order']))
feature_sources = en_guides[:3]
for locale, tagline, start in (
('en', 'The client decides. This process reads, patches, and runs.', 'Start here'),
('ko', '클라이언트가 판단합니다. 이 프로세스는 읽고, 패치하고, 실행합니다.', '시작하기'),
('en', 'Workspace tools for an external coding agent: inspect files, apply patches, and run commands.', 'Start here'),
('ko', '외부 코딩 에이전트가 파일을 읽고, 패치를 적용하고, 명령을 실행하는 작업 공간 도구입니다.', '시작하기'),
):
prefix = '/ko' if locale == 'ko' else ''
features = []
for page in feature_sources:
mate = next(item for item in inventory if item['id'] == page['id'] and item['locale'] == locale)
features.append({'title': mate['title'], 'details': tagline, 'link': mate['route']})
features = home_features(inventory, locale)
data = {
'layout': 'home', 'docLocale': locale,
'hero': {
Expand Down
Loading
Loading