acp-gateway 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- acp_gateway-0.1.0/.dockerignore +9 -0
- acp_gateway-0.1.0/.env.example +12 -0
- acp_gateway-0.1.0/.gitattributes +3 -0
- acp_gateway-0.1.0/.githooks/pre-commit +6 -0
- acp_gateway-0.1.0/.github/workflows/linux-binary.yml +105 -0
- acp_gateway-0.1.0/.gitignore +40 -0
- acp_gateway-0.1.0/.python-version +1 -0
- acp_gateway-0.1.0/Dockerfile +19 -0
- acp_gateway-0.1.0/LICENSE +21 -0
- acp_gateway-0.1.0/PKG-INFO +309 -0
- acp_gateway-0.1.0/README.md +273 -0
- acp_gateway-0.1.0/config.example.yaml +49 -0
- acp_gateway-0.1.0/deploy/docker/compose.direct.yaml +6 -0
- acp_gateway-0.1.0/deploy/docker/compose.nginx.yaml +18 -0
- acp_gateway-0.1.0/deploy/docker/compose.yaml +40 -0
- acp_gateway-0.1.0/deploy/docker/computer.example.yaml +13 -0
- acp_gateway-0.1.0/deploy/docker/gateway.example.yaml +35 -0
- acp_gateway-0.1.0/deploy/docker/nginx-location.conf.example +19 -0
- acp_gateway-0.1.0/deploy/docker/nginx.conf.example +16 -0
- acp_gateway-0.1.0/deploy/docker/prepare.py +37 -0
- acp_gateway-0.1.0/deploy/windows/wsl-task.ps1 +65 -0
- acp_gateway-0.1.0/docs/architecture.md +536 -0
- acp_gateway-0.1.0/docs/archive/2026-10-06-initial-plan.md +1355 -0
- acp_gateway-0.1.0/docs/items/connector.md +459 -0
- acp_gateway-0.1.0/docs/setup/binary.md +103 -0
- acp_gateway-0.1.0/docs/setup/distribution.md +293 -0
- acp_gateway-0.1.0/docs/setup/gateway.md +132 -0
- acp_gateway-0.1.0/docs/setup/hermes.md +96 -0
- acp_gateway-0.1.0/docs/setup/observability.md +47 -0
- acp_gateway-0.1.0/docs/setup/service.md +236 -0
- acp_gateway-0.1.0/docs/setup/telegram.md +84 -0
- acp_gateway-0.1.0/docs/setup/vps-acceptance.md +277 -0
- acp_gateway-0.1.0/docs/setup/vps-acceptance.ru.md +296 -0
- acp_gateway-0.1.0/docs/setup/work-goose.md +90 -0
- acp_gateway-0.1.0/pyproject.toml +78 -0
- acp_gateway-0.1.0/road-map.md +581 -0
- acp_gateway-0.1.0/road-notes.md +1526 -0
- acp_gateway-0.1.0/scripts/binary_entry.py +9 -0
- acp_gateway-0.1.0/scripts/build_binary.py +96 -0
- acp_gateway-0.1.0/scripts/build_release.py +140 -0
- acp_gateway-0.1.0/scripts/check_secrets.py +153 -0
- acp_gateway-0.1.0/scripts/install.sh +38 -0
- acp_gateway-0.1.0/scripts/install_release.ps1 +48 -0
- acp_gateway-0.1.0/scripts/install_release.sh +60 -0
- acp_gateway-0.1.0/scripts/sanitize_fixture.py +65 -0
- acp_gateway-0.1.0/scripts/smoke_binary.py +346 -0
- acp_gateway-0.1.0/scripts/smoke_connector.py +162 -0
- acp_gateway-0.1.0/scripts/smoke_docker.py +320 -0
- acp_gateway-0.1.0/scripts/smoke_installation.py +245 -0
- acp_gateway-0.1.0/scripts/smoke_platform.py +249 -0
- acp_gateway-0.1.0/scripts/smoke_service.py +138 -0
- acp_gateway-0.1.0/scripts/smoke_wsl_task.ps1 +49 -0
- acp_gateway-0.1.0/scripts/spike_acp.py +562 -0
- acp_gateway-0.1.0/src/acp_gateway/__init__.py +10 -0
- acp_gateway-0.1.0/src/acp_gateway/__main__.py +3 -0
- acp_gateway-0.1.0/src/acp_gateway/agents/__init__.py +39 -0
- acp_gateway-0.1.0/src/acp_gateway/agents/client.py +632 -0
- acp_gateway-0.1.0/src/acp_gateway/agents/errors.py +39 -0
- acp_gateway-0.1.0/src/acp_gateway/agents/events.py +142 -0
- acp_gateway-0.1.0/src/acp_gateway/agents/tls.py +135 -0
- acp_gateway-0.1.0/src/acp_gateway/agents/transport.py +155 -0
- acp_gateway-0.1.0/src/acp_gateway/api/__init__.py +1 -0
- acp_gateway-0.1.0/src/acp_gateway/api/app.py +398 -0
- acp_gateway-0.1.0/src/acp_gateway/api/schemas.py +26 -0
- acp_gateway-0.1.0/src/acp_gateway/api/security.py +90 -0
- acp_gateway-0.1.0/src/acp_gateway/channels/__init__.py +5 -0
- acp_gateway-0.1.0/src/acp_gateway/channels/base.py +53 -0
- acp_gateway-0.1.0/src/acp_gateway/channels/cli.py +43 -0
- acp_gateway-0.1.0/src/acp_gateway/channels/mcp.py +196 -0
- acp_gateway-0.1.0/src/acp_gateway/channels/telegram.py +440 -0
- acp_gateway-0.1.0/src/acp_gateway/cli/__init__.py +0 -0
- acp_gateway-0.1.0/src/acp_gateway/cli/client.py +229 -0
- acp_gateway-0.1.0/src/acp_gateway/cli/computers.py +31 -0
- acp_gateway-0.1.0/src/acp_gateway/cli/main.py +309 -0
- acp_gateway-0.1.0/src/acp_gateway/config.py +384 -0
- acp_gateway-0.1.0/src/acp_gateway/connectors/__init__.py +1 -0
- acp_gateway-0.1.0/src/acp_gateway/connectors/commands.py +113 -0
- acp_gateway-0.1.0/src/acp_gateway/connectors/control.py +472 -0
- acp_gateway-0.1.0/src/acp_gateway/connectors/launch.py +70 -0
- acp_gateway-0.1.0/src/acp_gateway/connectors/policy.py +332 -0
- acp_gateway-0.1.0/src/acp_gateway/connectors/protocol.py +122 -0
- acp_gateway-0.1.0/src/acp_gateway/connectors/relay.py +381 -0
- acp_gateway-0.1.0/src/acp_gateway/core/__init__.py +55 -0
- acp_gateway-0.1.0/src/acp_gateway/core/approvals.py +224 -0
- acp_gateway-0.1.0/src/acp_gateway/core/bus.py +176 -0
- acp_gateway-0.1.0/src/acp_gateway/core/errors.py +27 -0
- acp_gateway-0.1.0/src/acp_gateway/core/gateway.py +404 -0
- acp_gateway-0.1.0/src/acp_gateway/core/policy.py +44 -0
- acp_gateway-0.1.0/src/acp_gateway/daemon.py +139 -0
- acp_gateway-0.1.0/src/acp_gateway/installation.py +318 -0
- acp_gateway-0.1.0/src/acp_gateway/log.py +267 -0
- acp_gateway-0.1.0/src/acp_gateway/maintenance_worker.py +270 -0
- acp_gateway-0.1.0/src/acp_gateway/paths.py +26 -0
- acp_gateway-0.1.0/src/acp_gateway/service.py +226 -0
- acp_gateway-0.1.0/src/acp_gateway/setup.py +69 -0
- acp_gateway-0.1.0/src/acp_gateway/storage/__init__.py +14 -0
- acp_gateway-0.1.0/src/acp_gateway/storage/computers.py +175 -0
- acp_gateway-0.1.0/src/acp_gateway/storage/db.py +435 -0
- acp_gateway-0.1.0/src/acp_gateway/storage/migrations/0001_initial.sql +41 -0
- acp_gateway-0.1.0/src/acp_gateway/storage/migrations/0002_approvals_audit.sql +21 -0
- acp_gateway-0.1.0/src/acp_gateway/storage/migrations/0003_channel_state.sql +7 -0
- acp_gateway-0.1.0/src/acp_gateway/storage/migrations/0004_computers.sql +9 -0
- acp_gateway-0.1.0/src/acp_gateway/storage/migrations/0005_computer_connections.sql +9 -0
- acp_gateway-0.1.0/src/acp_gateway/storage/migrations/__init__.py +0 -0
- acp_gateway-0.1.0/src/acp_gateway/storage/records.py +101 -0
- acp_gateway-0.1.0/tests/conftest.py +21 -0
- acp_gateway-0.1.0/tests/fakes/__init__.py +0 -0
- acp_gateway-0.1.0/tests/fakes/certs.py +64 -0
- acp_gateway-0.1.0/tests/fakes/fake_goose.py +326 -0
- acp_gateway-0.1.0/tests/fakes/hermes_probe.py +60 -0
- acp_gateway-0.1.0/tests/fakes/recordings.py +84 -0
- acp_gateway-0.1.0/tests/fakes/telegram.py +43 -0
- acp_gateway-0.1.0/tests/fixtures/acp/goose-1.53.0/cancel-during-command.jsonl +20 -0
- acp_gateway-0.1.0/tests/fixtures/acp/goose-1.53.0/cancel-during-generation.jsonl +16 -0
- acp_gateway-0.1.0/tests/fixtures/acp/goose-1.53.0/cancel-permission-pending.jsonl +20 -0
- acp_gateway-0.1.0/tests/fixtures/acp/goose-1.53.0/init.jsonl +2 -0
- acp_gateway-0.1.0/tests/fixtures/acp/goose-1.53.0/load-http-hangs.jsonl +16 -0
- acp_gateway-0.1.0/tests/fixtures/acp/goose-1.53.0/load-ws.jsonl +33 -0
- acp_gateway-0.1.0/tests/fixtures/acp/goose-1.53.0/modes.jsonl +8 -0
- acp_gateway-0.1.0/tests/fixtures/acp/goose-1.53.0/permission-reject.jsonl +23 -0
- acp_gateway-0.1.0/tests/fixtures/acp/goose-1.53.0/ping.jsonl +14 -0
- acp_gateway-0.1.0/tests/integration/test_agent_client.py +407 -0
- acp_gateway-0.1.0/tests/integration/test_agent_transport_factory.py +233 -0
- acp_gateway-0.1.0/tests/integration/test_agent_transport_robustness.py +234 -0
- acp_gateway-0.1.0/tests/integration/test_connector_control.py +504 -0
- acp_gateway-0.1.0/tests/integration/test_connector_multinode.py +431 -0
- acp_gateway-0.1.0/tests/integration/test_connector_process.py +196 -0
- acp_gateway-0.1.0/tests/integration/test_connector_relay.py +429 -0
- acp_gateway-0.1.0/tests/integration/test_daemon_cli.py +360 -0
- acp_gateway-0.1.0/tests/integration/test_gateway_approvals.py +172 -0
- acp_gateway-0.1.0/tests/integration/test_gateway_core.py +471 -0
- acp_gateway-0.1.0/tests/integration/test_hermes_compat.py +70 -0
- acp_gateway-0.1.0/tests/integration/test_mcp.py +147 -0
- acp_gateway-0.1.0/tests/integration/test_spike_against_fake_goose.py +177 -0
- acp_gateway-0.1.0/tests/integration/test_telegram.py +361 -0
- acp_gateway-0.1.0/tests/integration/test_tls_validity.py +41 -0
- acp_gateway-0.1.0/tests/integration/test_transport_redirects.py +38 -0
- acp_gateway-0.1.0/tests/unit/test_agent_client_safety.py +163 -0
- acp_gateway-0.1.0/tests/unit/test_approvals.py +289 -0
- acp_gateway-0.1.0/tests/unit/test_bus.py +84 -0
- acp_gateway-0.1.0/tests/unit/test_check_secrets.py +123 -0
- acp_gateway-0.1.0/tests/unit/test_cli.py +78 -0
- acp_gateway-0.1.0/tests/unit/test_cli_stream.py +81 -0
- acp_gateway-0.1.0/tests/unit/test_computer_diagnostics.py +53 -0
- acp_gateway-0.1.0/tests/unit/test_computers.py +166 -0
- acp_gateway-0.1.0/tests/unit/test_config.py +259 -0
- acp_gateway-0.1.0/tests/unit/test_connector_boundaries.py +106 -0
- acp_gateway-0.1.0/tests/unit/test_connector_commands.py +99 -0
- acp_gateway-0.1.0/tests/unit/test_connector_config.py +70 -0
- acp_gateway-0.1.0/tests/unit/test_connector_policy.py +442 -0
- acp_gateway-0.1.0/tests/unit/test_connector_protocol.py +130 -0
- acp_gateway-0.1.0/tests/unit/test_connector_service.py +172 -0
- acp_gateway-0.1.0/tests/unit/test_installation.py +170 -0
- acp_gateway-0.1.0/tests/unit/test_log.py +250 -0
- acp_gateway-0.1.0/tests/unit/test_maintenance_worker.py +130 -0
- acp_gateway-0.1.0/tests/unit/test_mcp_safety.py +81 -0
- acp_gateway-0.1.0/tests/unit/test_owner_api.py +236 -0
- acp_gateway-0.1.0/tests/unit/test_policy.py +29 -0
- acp_gateway-0.1.0/tests/unit/test_recorded_contract.py +78 -0
- acp_gateway-0.1.0/tests/unit/test_sanitize_fixture.py +56 -0
- acp_gateway-0.1.0/tests/unit/test_service.py +146 -0
- acp_gateway-0.1.0/tests/unit/test_service_env_literal.py +30 -0
- acp_gateway-0.1.0/tests/unit/test_storage.py +296 -0
- acp_gateway-0.1.0/tests/unit/test_tls_pin.py +97 -0
- acp_gateway-0.1.0/uv.lock +1857 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Secrets only. Copy to .env (ignored by git), restrict access: chmod 600 .env
|
|
2
|
+
# Names are referenced from config.yaml (secret_env, api_token_env, mcp_token_env).
|
|
3
|
+
|
|
4
|
+
# Same value as GOOSE_SERVER__SECRET_KEY on the agent side
|
|
5
|
+
AGENT_WORK_SECRET=
|
|
6
|
+
|
|
7
|
+
# Bearer tokens for the local HTTP API and MCP endpoint
|
|
8
|
+
ACPGW_API_TOKEN=
|
|
9
|
+
ACPGW_MCP_TOKEN=
|
|
10
|
+
|
|
11
|
+
# Separate Telegram bot for the gateway
|
|
12
|
+
TELEGRAM_BOT_TOKEN=
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
name: Platform release checks
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
publish_image:
|
|
7
|
+
description: 'Publish the tested Docker image to GHCR (manual release)'
|
|
8
|
+
required: false
|
|
9
|
+
type: boolean
|
|
10
|
+
default: false
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
publish-image:
|
|
17
|
+
if: ${{ inputs.publish_image }}
|
|
18
|
+
needs: [build, native]
|
|
19
|
+
runs-on: ubuntu-22.04
|
|
20
|
+
timeout-minutes: 15
|
|
21
|
+
permissions:
|
|
22
|
+
contents: read
|
|
23
|
+
packages: write
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
with:
|
|
27
|
+
persist-credentials: false
|
|
28
|
+
- uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
|
29
|
+
with:
|
|
30
|
+
python-version: '3.12'
|
|
31
|
+
- run: uv sync --frozen
|
|
32
|
+
- name: Select versioned image
|
|
33
|
+
run: |
|
|
34
|
+
release_version=$(uv run --frozen python -c 'from acp_gateway import __version__; print(__version__)')
|
|
35
|
+
echo "ACPGW_RELEASE_IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}:$release_version" >> "$GITHUB_ENV"
|
|
36
|
+
- run: docker build -t "$ACPGW_RELEASE_IMAGE" .
|
|
37
|
+
- run: uv run --frozen python scripts/smoke_docker.py --image "$ACPGW_RELEASE_IMAGE"
|
|
38
|
+
- name: Publish tested image
|
|
39
|
+
env:
|
|
40
|
+
GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
41
|
+
run: |
|
|
42
|
+
printf '%s' "$GHCR_TOKEN" | docker login ghcr.io --username "$GITHUB_ACTOR" --password-stdin
|
|
43
|
+
if docker manifest inspect "$ACPGW_RELEASE_IMAGE" >/dev/null 2>&1; then
|
|
44
|
+
echo 'Version tag already exists; increment the package version instead of replacing it.' >&2
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
docker push "$ACPGW_RELEASE_IMAGE"
|
|
48
|
+
- name: Clear registry credentials
|
|
49
|
+
if: ${{ always() }}
|
|
50
|
+
run: docker logout ghcr.io
|
|
51
|
+
native:
|
|
52
|
+
strategy:
|
|
53
|
+
fail-fast: false
|
|
54
|
+
matrix:
|
|
55
|
+
runner: [windows-2025, macos-15, macos-15-intel, ubuntu-24.04-arm]
|
|
56
|
+
runs-on: ${{ matrix.runner }}
|
|
57
|
+
timeout-minutes: 25
|
|
58
|
+
steps:
|
|
59
|
+
- uses: actions/checkout@v4
|
|
60
|
+
with:
|
|
61
|
+
persist-credentials: false
|
|
62
|
+
- uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
|
63
|
+
with:
|
|
64
|
+
python-version: '3.12'
|
|
65
|
+
- run: uv sync --frozen --group build
|
|
66
|
+
- run: uv build --wheel --out-dir dist/native
|
|
67
|
+
- run: uv run --frozen --group build python scripts/build_binary.py
|
|
68
|
+
- run: uv run --frozen python scripts/smoke_platform.py --wheel-dir dist/native --built-binary
|
|
69
|
+
- uses: actions/upload-artifact@v4
|
|
70
|
+
with:
|
|
71
|
+
name: acpgw-${{ matrix.runner }}
|
|
72
|
+
path: |
|
|
73
|
+
dist/*.tar.gz
|
|
74
|
+
dist/*.sha256
|
|
75
|
+
dist/*.build.json
|
|
76
|
+
if-no-files-found: error
|
|
77
|
+
build:
|
|
78
|
+
runs-on: ubuntu-22.04
|
|
79
|
+
timeout-minutes: 20
|
|
80
|
+
steps:
|
|
81
|
+
- uses: actions/checkout@v4
|
|
82
|
+
with:
|
|
83
|
+
persist-credentials: false
|
|
84
|
+
- uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
|
85
|
+
with:
|
|
86
|
+
python-version: '3.12'
|
|
87
|
+
- run: uv sync --frozen --group build
|
|
88
|
+
- run: uv run --frozen ruff check
|
|
89
|
+
- run: uv run --frozen ruff format --check
|
|
90
|
+
- run: uv run --frozen pytest
|
|
91
|
+
- run: uv run --frozen --group build python scripts/build_binary.py
|
|
92
|
+
- run: uv run --frozen python scripts/smoke_binary.py dist/acpgw-linux-x86_64/acpgw
|
|
93
|
+
- run: uv run --frozen python scripts/smoke_connector.py dist/acpgw-linux-x86_64/acpgw
|
|
94
|
+
- name: Check executable in Ubuntu without Python
|
|
95
|
+
run: >-
|
|
96
|
+
uv run --frozen python scripts/smoke_binary.py
|
|
97
|
+
dist/acpgw-linux-x86_64/acpgw --container-image ubuntu:22.04
|
|
98
|
+
- uses: actions/upload-artifact@v4
|
|
99
|
+
with:
|
|
100
|
+
name: acpgw-linux-x86_64
|
|
101
|
+
path: |
|
|
102
|
+
dist/*.tar.gz
|
|
103
|
+
dist/*.sha256
|
|
104
|
+
dist/*.build.json
|
|
105
|
+
if-no-files-found: error
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Secrets — never commit
|
|
2
|
+
.env
|
|
3
|
+
.env.*
|
|
4
|
+
!.env.example
|
|
5
|
+
config.yaml
|
|
6
|
+
*.pem
|
|
7
|
+
*.key
|
|
8
|
+
|
|
9
|
+
# Runtime data
|
|
10
|
+
data/
|
|
11
|
+
*.db
|
|
12
|
+
*.db-journal
|
|
13
|
+
*.db-wal
|
|
14
|
+
*.db-shm
|
|
15
|
+
*.log
|
|
16
|
+
|
|
17
|
+
# Python
|
|
18
|
+
__pycache__/
|
|
19
|
+
*.py[cod]
|
|
20
|
+
.venv/
|
|
21
|
+
build/
|
|
22
|
+
dist/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
.pytest_cache/
|
|
25
|
+
.ruff_cache/
|
|
26
|
+
.coverage
|
|
27
|
+
htmlcov/
|
|
28
|
+
|
|
29
|
+
# Editors / OS
|
|
30
|
+
.idea/
|
|
31
|
+
.vscode/
|
|
32
|
+
.DS_Store
|
|
33
|
+
Thumbs.db
|
|
34
|
+
|
|
35
|
+
# ACP spike output (may contain work data)
|
|
36
|
+
spike-runs/
|
|
37
|
+
|
|
38
|
+
# Private VPS deployment configuration, credentials and persistent state
|
|
39
|
+
deploy/docker/runtime/
|
|
40
|
+
deploy/docker/state/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
FROM ghcr.io/astral-sh/uv:0.7.6 AS uv
|
|
2
|
+
FROM python:3.12-slim-bookworm AS build
|
|
3
|
+
COPY --from=uv /uv /usr/local/bin/uv
|
|
4
|
+
WORKDIR /app
|
|
5
|
+
ENV UV_LINK_MODE=copy UV_PYTHON_DOWNLOADS=never
|
|
6
|
+
COPY pyproject.toml uv.lock README.md LICENSE ./
|
|
7
|
+
COPY src ./src
|
|
8
|
+
RUN uv sync --frozen --no-dev --no-editable
|
|
9
|
+
|
|
10
|
+
FROM python:3.12-slim-bookworm AS runtime
|
|
11
|
+
RUN groupadd --gid 10001 acpgw && useradd --uid 10001 --gid acpgw --create-home acpgw \
|
|
12
|
+
&& mkdir /data /config && chown acpgw:acpgw /data /config
|
|
13
|
+
COPY --from=build /app/.venv /app/.venv
|
|
14
|
+
ENV PATH=/app/.venv/bin:$PATH PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1 \
|
|
15
|
+
XDG_DATA_HOME=/data
|
|
16
|
+
USER 10001:10001
|
|
17
|
+
WORKDIR /data
|
|
18
|
+
ENTRYPOINT ["acpgw", "--config", "/config/gateway.yaml", "--env-file", "/config/gateway.env"]
|
|
19
|
+
CMD ["serve"]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lujker
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: acp-gateway
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Local gateway that routes messages from external channels (Hermes, Telegram, ...) to ACP agents
|
|
5
|
+
Project-URL: Homepage, https://github.com/Lujker/acp-gateway
|
|
6
|
+
Project-URL: Repository, https://github.com/Lujker/acp-gateway
|
|
7
|
+
Project-URL: Issues, https://github.com/Lujker/acp-gateway/issues
|
|
8
|
+
Author: Lujker
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: acp,agent-client-protocol,gateway,goose,hermes,mcp,telegram
|
|
12
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Communications :: Chat
|
|
19
|
+
Classifier: Topic :: Software Development
|
|
20
|
+
Requires-Python: >=3.12
|
|
21
|
+
Requires-Dist: agent-client-protocol[http]>=0.12
|
|
22
|
+
Requires-Dist: aiogram<4,>=3.22
|
|
23
|
+
Requires-Dist: fastapi>=0.115
|
|
24
|
+
Requires-Dist: filelock>=3.16
|
|
25
|
+
Requires-Dist: httpx>=0.28
|
|
26
|
+
Requires-Dist: mcp<2,>=1.20
|
|
27
|
+
Requires-Dist: packaging>=24
|
|
28
|
+
Requires-Dist: platformdirs>=4.3
|
|
29
|
+
Requires-Dist: pydantic-settings[yaml]>=2.8
|
|
30
|
+
Requires-Dist: pydantic>=2.10
|
|
31
|
+
Requires-Dist: python-dotenv>=1.0
|
|
32
|
+
Requires-Dist: structlog>=25.1
|
|
33
|
+
Requires-Dist: uvicorn>=0.34
|
|
34
|
+
Requires-Dist: websockets>=15
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# ACP Gateway
|
|
38
|
+
|
|
39
|
+
[](LICENSE)
|
|
40
|
+

|
|
41
|
+

|
|
42
|
+
[](https://agentclientprotocol.com)
|
|
43
|
+
|
|
44
|
+
**ACP Gateway** is a local gateway that lets chat channels and other agents —
|
|
45
|
+
Hermes, Telegram, later XMPP, e-mail and a web UI — talk to a remote
|
|
46
|
+
[Agent Client Protocol](https://agentclientprotocol.com) agent such as
|
|
47
|
+
[goose](https://github.com/aaif-goose/goose). It delivers messages to the
|
|
48
|
+
agent and returns answers and tool-approval requests to the channels, with a
|
|
49
|
+
human in the loop for every risky action.
|
|
50
|
+
|
|
51
|
+
The first target is **Work Goose**: `goose serve` on a work laptop. Work MCP
|
|
52
|
+
servers, credentials, files and the model stay on that machine; the gateway
|
|
53
|
+
only knows the agent's address, a shared secret and the fingerprint of its TLS
|
|
54
|
+
certificate.
|
|
55
|
+
|
|
56
|
+
```mermaid
|
|
57
|
+
flowchart LR
|
|
58
|
+
subgraph home["Home host (WSL)"]
|
|
59
|
+
hermes["Hermes"] -- MCP --> gw
|
|
60
|
+
tg["Telegram"] --> gw
|
|
61
|
+
cli["CLI / Web UI"] -- HTTP --> gw
|
|
62
|
+
gw["ACP Gateway<br/>sessions · approvals · policy · audit"]
|
|
63
|
+
end
|
|
64
|
+
subgraph work["Work laptop (WSL)"]
|
|
65
|
+
goose["goose serve<br/>MCP · files · model"]
|
|
66
|
+
end
|
|
67
|
+
gw -- "ACP over WebSocket<br/>TLS pinning + X-Secret-Key" --> goose
|
|
68
|
+
desktop["Goose Desktop"] -. direct .-> goose
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Status
|
|
72
|
+
|
|
73
|
+
Early development. The foundation is done: the connection to a real goose
|
|
74
|
+
1.53.0 over the LAN is verified, along with sessions, approvals, cancellation
|
|
75
|
+
and session restore. The daemon, local HTTP/SSE API and human CLI channel are
|
|
76
|
+
implemented and verified against Work Goose. The Hermes MCP facade is implemented
|
|
77
|
+
and checked with Hermes' installed MCP module against the recorded Goose mock.
|
|
78
|
+
Linux/WSL service lifecycle and Telegram commands/approval buttons are implemented;
|
|
79
|
+
actual reboot and the live human Telegram acceptance remain pending. Connector
|
|
80
|
+
development includes local computer enrollment and a guarded WS/WSS ACP relay
|
|
81
|
+
in `serve`, tested against the recorded agent mock. Live acceptance is next. The plan
|
|
82
|
+
and the work queue are in [`road-map.md`](road-map.md) (in Russian).
|
|
83
|
+
|
|
84
|
+
| Area | State |
|
|
85
|
+
|---|---|
|
|
86
|
+
| Scaffold, config, secret redaction, pre-commit hook | ✅ |
|
|
87
|
+
| ACP over WebSocket with TLS pinning, verified on goose 1.53.0 | ✅ |
|
|
88
|
+
| Agent client (`acp_gateway.agents`) and recorded-traffic mock agent | ✅ `P1.1`, `P1.2` |
|
|
89
|
+
| Core: sessions in SQLite, jobs, event bus, channel contract | ✅ `P1.3` |
|
|
90
|
+
| Approvals: human routing, deadlines, audit; core policy limits | ✅ `P1.4` |
|
|
91
|
+
| Daemon, local API, approvals from the CLI | ✅ `P1.5` |
|
|
92
|
+
| Hermes MCP facade; actions approved through the CLI | ✅ `P2.1`, `P2.2` (Hermes MCP module + mock agent) |
|
|
93
|
+
| Checkout installer, setup, Linux/WSL service CLI, standalone Linux binary recipe | Binary, real user-service/crash recovery and Windows task lifecycle verified; actual reboot pending |
|
|
94
|
+
| Telegram | Commands, private user allowlist and audited approval buttons implemented; live acceptance pending |
|
|
95
|
+
| Runtime diagnostics | Component health/probes, private rotated JSON logs and Telegram throttling/recovery implemented |
|
|
96
|
+
| [VPS connector](docs/items/connector.md) | ACP relay, Linux/WSL services and route diagnostics implemented; two-computer isolation/approvals/history mock-tested; live acceptance next |
|
|
97
|
+
| Snikket/XMPP, e-mail, web UI | 🗓 `P4` |
|
|
98
|
+
|
|
99
|
+
## Principles
|
|
100
|
+
|
|
101
|
+
- **The agent executes, the gateway relays.** The gateway makes no decisions
|
|
102
|
+
for the agent and stores no work MCP credentials.
|
|
103
|
+
- **A human approves.** Agent actions are approved in a channel with a live
|
|
104
|
+
human (CLI, Telegram). LLM channels, Hermes included, never get an "allow"
|
|
105
|
+
button. With no approver connected, the action is rejected.
|
|
106
|
+
- **Secure by default.** The local API listens on loopback only. Unencrypted
|
|
107
|
+
transport to an agent is allowed only on loopback. The secret is sent only
|
|
108
|
+
after the pinned certificate is verified. ACP client capabilities (`fs`,
|
|
109
|
+
`terminal`) are disabled. Secrets are masked in logs and kept out of git.
|
|
110
|
+
- **Channels are thin adapters.** A new channel implements a shared contract
|
|
111
|
+
and does not touch the core.
|
|
112
|
+
|
|
113
|
+
More in [`docs/architecture.md`](docs/architecture.md).
|
|
114
|
+
|
|
115
|
+
## Quick start
|
|
116
|
+
|
|
117
|
+
For a VPS in Docker, two outgoing Linux/WSL connectors and Telegram, follow
|
|
118
|
+
the [live acceptance runbook](docs/setup/vps-acceptance.md)
|
|
119
|
+
([на русском](docs/setup/vps-acceptance.ru.md)). It starts with
|
|
120
|
+
an IP/port route and then adds an exact WSS path through its own nginx without
|
|
121
|
+
publishing the owner API or changing other application services.
|
|
122
|
+
|
|
123
|
+
For installation without a checkout, use the wheel/deployment bundle described
|
|
124
|
+
in [installation and updates](docs/setup/distribution.md). Public PyPI/registry
|
|
125
|
+
publication is a separate manual release step. Internal maintenance commands
|
|
126
|
+
for uv installations are `acpgw version`, `update`, `update --status`,
|
|
127
|
+
`update --rollback` and `uninstall`. Updates take a private recovery snapshot
|
|
128
|
+
and restore it on installation/health-check failure. Native Windows queues
|
|
129
|
+
internal maintenance after the CLI exits; its PowerShell installer waits for
|
|
130
|
+
the verified result. See the installation guide before rolling back a database.
|
|
131
|
+
|
|
132
|
+
For the separate Git/development path, you need [uv](https://docs.astral.sh/uv/) and git; uv installs Python 3.12
|
|
133
|
+
itself. Linux/WSL is the primary platform. Native Windows x86_64, macOS Intel/ARM64
|
|
134
|
+
and Linux ARM64 have wheel lifecycle and binary CLI checks; native Windows/macOS
|
|
135
|
+
services and full agent acceptance remain separate milestones.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
git clone --branch v0.1.0 https://github.com/Lujker/acp-gateway.git
|
|
139
|
+
cd acp-gateway
|
|
140
|
+
uv sync --frozen
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
For an installed `acpgw` command outside the checkout, run
|
|
144
|
+
`sh scripts/install.sh`, then configure the files created by `acpgw setup`.
|
|
145
|
+
The installer keeps existing configuration and tokens. Installation and
|
|
146
|
+
service commands: [`docs/setup/service.md`](docs/setup/service.md).
|
|
147
|
+
|
|
148
|
+
Use a published, reviewed tag as above; before the first release, select the
|
|
149
|
+
reviewed branch or commit explicitly. For development, create a working branch
|
|
150
|
+
from that checkout.
|
|
151
|
+
|
|
152
|
+
To build a standalone executable with no Python required on the runtime
|
|
153
|
+
host, use `uv run --frozen --group build python scripts/build_binary.py`.
|
|
154
|
+
Build artifacts, checksums, compatibility limits and installation:
|
|
155
|
+
[`docs/setup/binary.md`](docs/setup/binary.md).
|
|
156
|
+
Build locally and attach archives/checksums to a manually created GitHub
|
|
157
|
+
release. The optional GitHub Actions build runs only on explicit manual
|
|
158
|
+
dispatch; pushes, PRs, tags and releases do not consume build minutes.
|
|
159
|
+
|
|
160
|
+
Telegram setup and human approvals: [`docs/setup/telegram.md`](docs/setup/telegram.md).
|
|
161
|
+
Health probes and rotating logs: [`docs/setup/observability.md`](docs/setup/observability.md).
|
|
162
|
+
|
|
163
|
+
### 1. Prepare the agent
|
|
164
|
+
|
|
165
|
+
On the agent machine, run `goose serve` with TLS and a secret and open the
|
|
166
|
+
port to the LAN. Step by step, including the Hyper-V firewall rule for WSL:
|
|
167
|
+
[`docs/setup/work-goose.md`](docs/setup/work-goose.md).
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
GOOSE_SERVER__SECRET_KEY='<long random secret>' \
|
|
171
|
+
goose serve --host 0.0.0.0 --port 3284 --tls
|
|
172
|
+
# note the GOOSED_CERT_FINGERPRINT=... line
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### 2. Configure the gateway
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
cp config.example.yaml config.yaml # agents, policy, logging
|
|
179
|
+
cp .env.example .env && chmod 600 .env # secrets only
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Set the agent address and certificate fingerprint in `config.yaml`:
|
|
183
|
+
|
|
184
|
+
```yaml
|
|
185
|
+
agents:
|
|
186
|
+
- alias: work
|
|
187
|
+
title: Work Goose
|
|
188
|
+
kind: goose
|
|
189
|
+
url: https://<agent-address>:3284 # /acp is appended automatically
|
|
190
|
+
secret_env: AGENT_WORK_SECRET
|
|
191
|
+
tls_fingerprint: "<GOOSED_CERT_FINGERPRINT>" # empty = trust on first use
|
|
192
|
+
default_cwd: /home/<user>
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
In `.env`: `AGENT_WORK_SECRET=<the same secret>`.
|
|
196
|
+
Also set `ACPGW_API_TOKEN` to a separate random owner token. It must differ
|
|
197
|
+
from the agent secret and `ACPGW_MCP_TOKEN` (if configured).
|
|
198
|
+
|
|
199
|
+
### 3. Check the connection
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
uv run acpgw config check # config is valid, secrets are present
|
|
203
|
+
uv run python scripts/spike_acp.py init # TLS + ACP handshake
|
|
204
|
+
uv run python scripts/spike_acp.py ping # a short request to the agent
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
The spike script has more scenarios: `modes`, `permission`, `cancel`, `load`,
|
|
208
|
+
`all`. Traffic is recorded to `spike-runs/` (git-ignored). The scenarios
|
|
209
|
+
`permission --permission allow` and `cancel` run harmless `echo` and `sleep`
|
|
210
|
+
commands on the agent machine.
|
|
211
|
+
|
|
212
|
+
### 4. Start the daemon and use the CLI
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
uv run acpgw serve # keep this terminal open
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
In another terminal:
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
uv run acpgw status
|
|
222
|
+
uv run acpgw ask "Remember the code word AMBER. Answer only OK."
|
|
223
|
+
uv run acpgw ask "What was the code word?" # same active session
|
|
224
|
+
uv run acpgw sessions
|
|
225
|
+
uv run acpgw new # start a fresh session
|
|
226
|
+
uv run acpgw switch 1 # gateway session row id from sessions
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
For actions that need approval, keep a third terminal connected:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
uv run acpgw approvals watch # allow once, reject, or skip interactively
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
The daemon alone does not count as a connected human. With no approval
|
|
236
|
+
watcher, requests are rejected. `acpgw approvals` lists pending requests;
|
|
237
|
+
`acpgw approvals approve <id>` and `reject <id>` decide an existing request
|
|
238
|
+
once. Long jobs can be submitted with `ask --no-stream`, retrieved with
|
|
239
|
+
`result <job_id> --wait 30`, and cancelled with `stop`. Ctrl+C during a
|
|
240
|
+
streaming `ask` requests cancellation of that job.
|
|
241
|
+
|
|
242
|
+
The CLI reads the same config and secrets as the daemon. Details and the
|
|
243
|
+
HTTP/SSE contract: [`docs/setup/gateway.md`](docs/setup/gateway.md).
|
|
244
|
+
|
|
245
|
+
### 5. Connect Hermes
|
|
246
|
+
|
|
247
|
+
Set a separate `ACPGW_MCP_TOKEN`, restart the daemon and add its `/mcp` URL
|
|
248
|
+
and bearer header to Hermes' `mcp_servers`. The six tools for each agent
|
|
249
|
+
support sessions, explicit threads, asynchronous jobs and cancellation.
|
|
250
|
+
Actions still require a human in `acpgw approvals watch`.
|
|
251
|
+
Configuration, tool arguments and compatibility checks:
|
|
252
|
+
[`docs/setup/hermes.md`](docs/setup/hermes.md).
|
|
253
|
+
|
|
254
|
+
### 6. Manage autostart on Linux/WSL
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
acpgw service install # install/update the user unit using the selected config
|
|
258
|
+
acpgw service enable # enable autostart and start now
|
|
259
|
+
acpgw service status # service state, including when the daemon is stopped
|
|
260
|
+
acpgw service disable # disable autostart and stop now
|
|
261
|
+
acpgw service --help # start, stop, restart and uninstall too
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
WSL also needs a Windows logon task and a user manager that starts with the
|
|
265
|
+
distro. Instructions and the remaining reboot check:
|
|
266
|
+
[`docs/setup/service.md`](docs/setup/service.md).
|
|
267
|
+
|
|
268
|
+
## Configuration
|
|
269
|
+
|
|
270
|
+
| Source | Contents |
|
|
271
|
+
|---|---|
|
|
272
|
+
| `config.yaml` (`--config`, `ACPGW_CONFIG`, `./`, platform config dir) | structure: `gateway`, `agents`, `policy`, `logging` |
|
|
273
|
+
| `ACPGW_<SECTION>__<FIELD>` variables | overrides, e.g. `ACPGW_LOGGING__LEVEL=DEBUG` |
|
|
274
|
+
| `.env` (`--env-file`, `ACPGW_ENV_FILE`, `./`, config dir) | **secrets only**, referenced by name (`secret_env`) |
|
|
275
|
+
|
|
276
|
+
`uv run acpgw paths` shows the config, data and log directories on the
|
|
277
|
+
current platform.
|
|
278
|
+
|
|
279
|
+
## Development
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
uv sync # environment and dependencies
|
|
283
|
+
git config core.hooksPath .githooks # pre-commit: secret scan + ruff
|
|
284
|
+
uv run pytest # unit + integration tests
|
|
285
|
+
uv run ruff check && uv run ruff format --check
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Integration tests start a mock goose (`tests/fakes/fake_goose.py`) whose
|
|
289
|
+
messages come from real goose 1.53.0 recordings in `tests/fixtures/acp/`; it
|
|
290
|
+
covers TLS, the secret, session modes, approvals, cancellation, session
|
|
291
|
+
loading and restarts. New recordings are cleaned with
|
|
292
|
+
`scripts/sanitize_fixture.py` before committing.
|
|
293
|
+
|
|
294
|
+
The development plan lives in [`road-map.md`](road-map.md) (statuses and
|
|
295
|
+
queue) and [`road-notes.md`](road-notes.md) (decisions and findings); both are
|
|
296
|
+
kept in Russian.
|
|
297
|
+
|
|
298
|
+
## Layout
|
|
299
|
+
|
|
300
|
+
```text
|
|
301
|
+
src/acp_gateway/ ACP client, core, SQLite storage, daemon, HTTP/SSE API, CLI, channels
|
|
302
|
+
scripts/ spike_acp.py (agent check), check_secrets.py (hook), sanitize_fixture.py
|
|
303
|
+
tests/ unit, integration, fakes, fixtures/acp
|
|
304
|
+
docs/ architecture.md, setup/, archive/
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## License
|
|
308
|
+
|
|
309
|
+
[MIT](LICENSE)
|