Metadata-Version: 2.5
Name: awp-runner
Version: 0.1.0
Summary: AWP runner: install/run a remote execution host for the Agent Workflow Platform
Requires-Python: >=3.11
Requires-Dist: agent-client-protocol<1,>=0.11
Requires-Dist: httpx<1,>=0.27
Requires-Dist: jsonschema<5,>=4.23
Requires-Dist: pydantic<3,>=2.10
Requires-Dist: pyyaml<7,>=6
Provides-Extra: dev
Requires-Dist: build<2,>=1.2; extra == 'dev'
Requires-Dist: fastapi<1,>=0.115; extra == 'dev'
Requires-Dist: langgraph-checkpoint-sqlite<4,>=2; extra == 'dev'
Requires-Dist: langgraph<2,>=1; extra == 'dev'
Requires-Dist: pytest<9,>=8; extra == 'dev'
Requires-Dist: ruff<1,>=0.9; extra == 'dev'
Requires-Dist: twine<6,>=5; extra == 'dev'
Requires-Dist: uvicorn[standard]<1,>=0.30; extra == 'dev'
Provides-Extra: server
Requires-Dist: fastapi<1,>=0.115; extra == 'server'
Requires-Dist: langgraph-checkpoint-sqlite<4,>=2; extra == 'server'
Requires-Dist: langgraph<2,>=1; extra == 'server'
Requires-Dist: uvicorn[standard]<1,>=0.30; extra == 'server'
Description-Content-Type: text/markdown

# Agent Workflow Platform

AWP is a local-first control plane for cataloging ACP agents and declarative
LangGraph workflows, launching jobs, and monitoring every node in real time.

It also manages an agent cluster: register ACP agents on hosts (local or remote
`awp runner` processes), create workspace-backed 工作节点 with persistent chat
sessions, dispatch swarm-team tasks in chat, and generate/import workflow manifests
through chat with an awp-helper node.

## 安装（PyPI / wheel）

单个 wheel，两种角色（控制面默认只装基础包会缺服务端依赖，所以用 extras 区分）：

```bash
# 远端执行主机（Host）：只装基础包，得到 `awp_runner`
pip install awp-runner           # 依赖仅 httpx/pydantic/pyyaml/jsonschema/acp

# 控制面（Server）：装服务端依赖，得到 `awp serve`
pip install "awp-runner[server]" # 追加 fastapi/uvicorn/langgraph
```

远端主机：装包后一条命令完成配置 + 服务托管（systemd --user 优先，无 systemd 退回 nohup）：

```bash
awp_runner -h
awp_runner install --control http://<控制面>:8000 --token <runner.token> --name host-b
awp_runner status          # backend + 运行状态 + 已装版本 + control/name
awp_runner start|stop|restart
awp_runner logs -f
awp_runner upgrade         # 从 PyPI 升级并重启；离线用 upgrade --wheel ./awp_runner-*.whl
awp_runner doctor          # 环境自检（opencode / 连通性 / 服务）
awp_runner uninstall [--purge]        # --purge 删配置；--purge-all 连 workspaces 一起删
```

`install` 把 control/token/name 写入 `~/.awp/runner.yaml`（权限 600），systemd 场景生成
`~/.config/systemd/user/awp-runner.service`（并开启 linger 开机自启）；无 systemd 时用
`~/.awp/runner.pid` + `~/.awp/runner.log` 托管。前台调试：`awp_runner run --config ~/.awp/runner.yaml`。
仅装基础包时 `awp serve` 会提示缺少 `[server]` 依赖。

### 发布到 PyPI

```bash
export PYPI_TOKEN='pypi-...'
scripts/build.sh     # 构建 sdist+wheel 到 dist/（uv build，回退 python -m build）
scripts/publish.sh   # twine upload -u __token__ -p "$PYPI_TOKEN" --skip-existing dist/*
```

`--skip-existing` 不会覆盖已存在版本，发布前先 bump `pyproject.toml` 的 `version`。

## Development

Requirements: Python 3.11+, Node.js 20+, `uv`, and npm.

```bash
uv sync --extra dev
uv run awp serve --reload
```

In another terminal:

```bash
cd frontend
npm install
npm run dev
```

Open `http://127.0.0.1:5173`. The Vite server proxies `/api` to the backend at
`http://127.0.0.1:8000`.

Remote execution host (outbound-only connection to the control plane):

```bash
uv run awp_runner run --control http://<control-host>:8000 --token <runner.token> --name host-b
# 或等价的老入口：uv run awp runner --control ... --token ... --name ...
```

The shared token comes from `runner.token` in the optional `awp.yaml`; without
it the runner channel stays disabled.

For a production-style local run, build the frontend once and let FastAPI serve it:

```bash
cd frontend && npm install && npm run build
cd .. && uv run awp serve
```

Run checks with:

```bash
PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 uv run pytest
uv run ruff check src tests
cd frontend && npm run build
```

## Docker

基础镜像 `somersault.harbor:20010/mellon/hermes:latest`（自带 opencode、node、uv；
镜像内用 uv 安装独立 Python 3.11 venv，不改动基础镜像）。构建：

```bash
docker build -t somersault.harbor:20010/mellon/awp:latest .
```

AWP 是标准 Python 包（`pyproject.toml` + `awp` 入口），容器内通过
`uv pip install .` 安装，也可以在任何 3.11+ 环境用 pip/uv 单独安装。

### 场景一：部署服务端（控制面板）

```bash
docker run -d --name awp -p 8000:8000 \
  -e AWP_RUNNER_TOKEN=change-me \
  -v awp-catalog:/catalog \
  -v awp-data:/data \
  -v awp-workspaces:/workspaces \
  somersault.harbor:20010/mellon/awp:latest
```

- 控制台：`http://<主机>:8000`
- **数据库/检查点**：`/data` 卷（`platform.db`、`checkpoints.sqlite`）——`AWP_DATA_DIR`
- **工作路径**：`/workspaces` 卷（工作节点的 workspace）——`AWP_WORKSPACE_ROOT`
- **配置文件**：`/catalog` 卷（Agent/Workflow 定义）——`AWP_CATALOG_DIR`；
  首次启动自动铺入默认定义（Server 模板 `opencode-local`）
- **配置 awp-helper**：编排助手的人设/平台快照/团队属于**工作节点**，在控制台
  Nodes 页编辑（或 `PATCH /api/nodes/awp-helper`），不写进 catalog 文件
- `AWP_RUNNER_TOKEN` 必须设置，否则 runner 通道返回 403

### 场景二：部署客户端 Host（只暴露本机 ACP）

```bash
docker run -d --name awp-host \
  -v awp-host-workspaces:/workspaces \
  somersault.harbor:20010/mellon/awp:latest \
  awp runner --control http://<服务端>:8000 --token <AWP_RUNNER_TOKEN> --name my-host
```

- 不需要 `/catalog`、`/data` 卷（runner 无数据库、无面板），只需 `/workspaces`
- 只出站连接服务端（注册/心跳/任务长轮询/事件回传），**客户端不开任何入站端口**
- 镜像内的 opencode 即可直接在服务端注册为 Agent：
  `POST /api/hosts/my-host/agents {"name":"...","agentRef":"opencode-local@1.0.3"}`
- token 也可用 `-e AWP_RUNNER_TOKEN=...` 传入（省略 `--token`）

### 环境变量（优先级：环境 > 挂载的 awp.yaml > 默认值）

| 变量 | 默认 | 说明 |
|---|---|---|
| `AWP_HOST` / `AWP_PORT` | `127.0.0.1` / `8000`（容器内置 `0.0.0.0:8000`） | 监听地址 |
| `AWP_RUNNER_TOKEN` | 无 | runner 通道共享密钥，未设置则通道关闭 |
| `AWP_CATALOG_DIR` | `$AWP_ROOT/catalog`（容器 `/catalog`） | 定义目录（卷） |
| `AWP_DATA_DIR` | `$AWP_ROOT/data`（容器 `/data`） | SQLite/检查点目录（卷） |
| `AWP_WORKSPACE_ROOT` | `$AWP_ROOT/workspaces`（容器 `/workspaces`） | 工作节点 workspace 根（卷） |
| `AWP_FRONTEND_DIR` | `$AWP_ROOT/frontend/dist` | 前端静态资源 |
| `AWP_CATALOG_SCAN_SECONDS` | `2` | catalog 热加载间隔 |
| `AWP_HEARTBEAT_SECONDS` / `AWP_OFFLINE_AFTER_SECONDS` | `10` / `45` | runner 心跳 / 离线判定 |

同一镜像两种角色：无参数 = 服务端（`awp serve`）；带命令 = 客户端 Host
（`docker run <image> awp runner ...`，入口脚本原样透传，见 `docker/entrypoint.sh`）。

## 定时任务

在 Workflow manifest 的 `spec` 里声明 `schedule`，平台会按 cron 周期自动创建
Job（名字带 `[定时]` 前缀）并在控制台铃铛通知中心推送触发/完成/失败：

```yaml
spec:
  schedule:
    cron: "0 9 * * *"   # 标准 5 字段 cron，本地时间
    input: {}           # 可选；缺省取 inputSchema 的 default/example
    enabled: true       # 改 manifest 文件即可（catalog 热加载）
    cwd: null           # 可选
```

错过的点位（服务端宕机、上一个 Job 未跑完）会自动补跑最近一次；`enabled: false`
停用。触发/完成/失败会自动弹到顶栏铃铛（页面隐藏时走浏览器系统通知，需在
铃铛面板授权一次）。

## Catalog

Definitions are immutable YAML manifests under `catalog/agents` and
`catalog/workflows`. Validate or import definitions with:

```bash
uv run awp validate path/to/resource.yaml
uv run awp import path/to/resource.yaml
uv run awp schema
```

See `CURRENT-DESIGN.md` for architecture and runtime semantics.

See [`docs/README.md`](docs/README.md) for the documentation system, including
Manifest authoring, public API v1, architecture, and operations.

The public integration API is available under `/api/v1`; FastAPI publishes
interactive documentation at `/docs` and the OpenAPI document at `/openapi.json`.

AWP is a framework rather than a single workflow application. Agents and
Workflows are declarative YAML resources; the runtime launches local ACP
processes on demand, persists Job/Attempt/Event history in SQLite, and uses
LangGraph checkpoints for recovery.

The repository intentionally ships without business-specific Agent or Workflow
templates. Add project-specific manifests under `catalog/` or import them at
runtime when a concrete use case is ready.
