chore: added robomp workspace, ignored fallow deps, cleaned up exports
- Added `python/robomp` as a workspace package alongside its nested `web` sub-package. - Added `ignoreDependencies` in `.fallowrc.jsonc` for deps used via bin or in nested workspaces. - Removed unused `clearTriggerStatus` export and unexported `tick` and `ByModelToggle`.
This commit is contained in:
@@ -165,7 +165,7 @@ ROBOMP_BIND_PORT=8080
|
||||
# --- oh-my-pi source location (host side) ---
|
||||
# =============================================================================
|
||||
# robomp lives inside the oh-my-pi monorepo at `python/robomp/`. The default
|
||||
# `bun run pi-artifacts` builds the parent monorepo (`../..`) as its docker
|
||||
# `bun run robomp:pi-artifacts` builds the parent monorepo (`../..`) as its docker
|
||||
# build context, and `docker-compose.yml` mounts that same path read-only at
|
||||
# `/work/pi` inside the container. Override `PI_ROOT` only if you want to point
|
||||
# the build/mount at a different oh-my-pi checkout.
|
||||
|
||||
+23
-23
@@ -23,48 +23,48 @@ Webhook → durable queue → async dispatcher → per-issue git worktree → om
|
||||
- `src/robomp/prompts/` — Mustache-style `{{var}}` templates loaded by `persona.py` via `@cache` and `importlib.resources`. Shipped as package data (`pyproject.toml` `package-data`).
|
||||
- `tests/` — pytest suite. `test_worker_smoke.py` is gated on `ROBOMP_INTEGRATION=1`.
|
||||
- `data/` — runtime state (sqlite + WAL, `workspaces/`, `logs/`). Never committed.
|
||||
- `/work/pi/Dockerfile` — produces `oh-my-pi/artifacts:dev` (pi-natives `.node` + omp-rpc wheel). Built once per pi-source change via `bun run pi-artifacts`; roboomp's runtime image consumes it via `COPY --from=`.
|
||||
- `/work/pi/Dockerfile` — produces `oh-my-pi/artifacts:dev` (pi-natives `.node` + omp-rpc wheel). Built once per pi-source change via `bun run robomp:pi-artifacts`; roboomp's runtime image consumes it via `COPY --from=`.
|
||||
|
||||
## Development Commands
|
||||
|
||||
Task runner is now `bun` against the root `package.json` (workspaces = `["web"]`). `just` is gone; every recipe lives in the root `scripts` block. Local venv (no docker): `bun run install:py` runs `pip install -e '.[dev]'`. From there:
|
||||
Task runner is `bun` against the **monorepo root** `package.json`. roboomp itself no longer ships a `package.json`; every recipe lives at the root under the `robomp:*` namespace. Local venv (no docker): `bun run robomp:install` runs `pip install -e 'python/robomp[dev]'`. From there:
|
||||
|
||||
```
|
||||
bun run test # pytest -x tests/
|
||||
bun run test:file <PATH> # single file
|
||||
bun run test:integration # ROBOMP_INTEGRATION=1, requires omp on PATH
|
||||
bun run serve # python -m robomp serve on the host
|
||||
bun run test:py # pytest -x python/omp-rpc/tests python/robomp/tests
|
||||
bun run robomp:test:integration # ROBOMP_INTEGRATION=1, requires omp on PATH
|
||||
bun run robomp:serve # python -m robomp serve on the host
|
||||
```
|
||||
|
||||
Docker inner loop:
|
||||
|
||||
```
|
||||
bun run build # pi-artifacts (if pi changed) + docker compose build
|
||||
bun run dev # build + up -d + follow logs
|
||||
bun run up / down / restart / logs / sh
|
||||
bun run rebuild # docker compose build --no-cache
|
||||
bun run robomp:build # pi-artifacts (if pi changed) + docker compose build
|
||||
bun run robomp:dev # build + up -d + follow logs
|
||||
bun run robomp:up / robomp:down / robomp:restart / robomp:logs
|
||||
bun run robomp:rebuild # docker compose build --no-cache
|
||||
bun run robomp:reset # `down -v` + drop the pi-artifacts image
|
||||
```
|
||||
|
||||
Frontend (Vite + SolidJS, in `web/`):
|
||||
Frontend (Vite + SolidJS, in `web/` — still a bun workspace):
|
||||
|
||||
```
|
||||
bun run web:dev # vite dev server with proxy to :8080
|
||||
bun run web:build # produce src/robomp/static/ bundle
|
||||
bun run web:typecheck # tsc --noEmit
|
||||
bun run robomp:web:dev # vite dev server with proxy to :8080
|
||||
bun run robomp:web:build # produce src/robomp/static/ bundle
|
||||
bun --cwd=python/robomp/web run typecheck # tsc --noEmit
|
||||
```
|
||||
|
||||
In-container CLI (`robomp` console script → `robomp.cli:main`):
|
||||
In-container CLI (`robomp` console script → `robomp.cli:main`): no root aliases — invoke directly:
|
||||
|
||||
```
|
||||
bun run triage owner/repo#N # full pipeline against a live issue
|
||||
bun run replay <delivery_id> # re-enqueue a stored webhook
|
||||
bun run issue-status # tabular dump of issues table
|
||||
bun run cleanup owner/repo#N # force workspace removal + state=abandoned
|
||||
docker compose --project-directory python/robomp exec robomp robomp triage owner/repo#N
|
||||
docker compose --project-directory python/robomp exec robomp robomp replay <delivery_id>
|
||||
docker compose --project-directory python/robomp exec robomp robomp status
|
||||
docker compose --project-directory python/robomp exec robomp robomp cleanup owner/repo#N
|
||||
```
|
||||
|
||||
HTTP/sqlite inspection: `bun run healthz`, `bun run readyz`, `bun run events [N]`, `bun run issues [N]`, `bun run sqlite`, `bun run sql "<SQL>"`, `bun run tool-calls owner/repo#N`, `bun run stuck`. Webhook smoke: `bun run ping`. Danger: `bun run wipe-workspaces`, `bun run nuke-data`, `bun run reset`.
|
||||
HTTP / sqlite / webhook inspection is unaliased — use `curl http://localhost:${ROBOMP_BIND_PORT:-8080}/{healthz,readyz,events,issues}` and `docker compose --project-directory python/robomp exec robomp sqlite3 /data/robomp.sqlite` directly.
|
||||
|
||||
Lint + format: TypeScript via Biome (config in `biome.json`), Python via Ruff (config in `pyproject.toml`). `bun run lint` checks both; `bun run fix` rewrites both. `bun run lint:ts` / `bun run lint:py` / `bun run fix:ts` / `bun run fix:py` scope to one language. `bun run typecheck` runs `tsc --noEmit` against `web/`. Run before committing non-trivial changes; CI is not yet wired up.
|
||||
Lint + format: TypeScript via Biome (config in `biome.json`), Python via Ruff (config in `pyproject.toml`). Root recipes cover both languages — `bun run lint` / `bun run fix` apply to the whole monorepo including roboomp. `bun run lint:py` / `bun run fix:py` scope to Python only.
|
||||
|
||||
## Code Conventions & Common Patterns
|
||||
|
||||
@@ -108,7 +108,7 @@ Lint + format: TypeScript via Biome (config in `biome.json`), Python via Ruff (c
|
||||
- **Task runner**: `bun` (root `package.json` `scripts`). Always reach for an existing `bun run` recipe before invoking `docker compose` or `pytest` directly.
|
||||
- **Container runtime**: Docker Compose v2. The image embeds Bun 1.3.14 + a rustup launcher and exposes `omp` via a `/usr/local/bin/omp` shim; `ROBOMP_OMP_COMMAND=omp` should not need changing.
|
||||
- **Required env** (set in `.env`, see `.env.example`): `GITHUB_WEBHOOK_SECRET`, `ROBOMP_BOT_LOGIN`, `ROBOMP_GIT_AUTHOR_NAME`, `ROBOMP_GIT_AUTHOR_EMAIL`, `ROBOMP_REPO_ALLOWLIST`, plus model knobs (`ROBOMP_MODEL`, `ROBOMP_THINKING`, optional `ROBOMP_PROVIDER`) and rate-limit / concurrency / timeout overrides. **GitHub auth is mode-exclusive**: either set `ROBOMP_GH_PROXY_URL` + `ROBOMP_GH_PROXY_HMAC_KEY` (gh-proxy mode; PAT lives only in the sidecar container — the bundled compose default), or set `GITHUB_TOKEN` directly (single-process PAT mode). `Settings._validate_proxy_or_pat` rejects a `.env` that sets both.
|
||||
- **PI_ROOT resolution**: roboomp lives inside the oh-my-pi monorepo at `python/robomp/`. `bun run pi-artifacts` builds the parent monorepo (`../..`) as its docker build context, and `docker-compose.yml` mounts that same path read-only at `/work/pi`. Override `PI_ROOT` only when pointing the build/mount at a different oh-my-pi checkout. Inside the container the path is always `/work/pi`. Build invalidation stays bounded: Python-only edits in roboomp never trigger a natives recompile.
|
||||
- **PI_ROOT resolution**: roboomp lives inside the oh-my-pi monorepo at `python/robomp/`. `bun run robomp:pi-artifacts` builds the parent monorepo (`../..`) as its docker build context, and `docker-compose.yml` mounts that same path read-only at `/work/pi`. Override `PI_ROOT` only when pointing the build/mount at a different oh-my-pi checkout. Inside the container the path is always `/work/pi`. Build invalidation stays bounded: Python-only edits in roboomp never trigger a natives recompile.
|
||||
- **Forbidden**: no docker-in-docker, no extra service containers, no new background workers outside `WorkerPool`. The container itself is the isolation boundary; per-issue isolation is the git worktree.
|
||||
|
||||
## Testing & QA
|
||||
@@ -121,5 +121,5 @@ Lint + format: TypeScript via Biome (config in `biome.json`), Python via Ruff (c
|
||||
- **Isolation rules**: any test mutating env via `monkeypatch.setenv` MUST also call `reset_settings_cache()` to invalidate the `@cache`d `get_settings()`.
|
||||
- **Async tests**: `test_github_client.py` and `test_host_tools.py` spin custom event loops in background threads to bridge sync-style tests with async client code. Prefer `pytest-asyncio` `auto` mode (`async def test_*`) for new tests; only fall back to the loop helpers if matching the surrounding file's style.
|
||||
- **Mocking**: never patch internals; inject test doubles via `httpx.MockTransport` for HTTP and via the `db` / `tmp_path` fixtures for storage. Sandbox tests use a real local bare repo as the upstream.
|
||||
- **Integration**: `tests/test_worker_smoke.py` is gated by `ROBOMP_INTEGRATION=1` (uses `pytestmark.skipif`) and needs `omp` on `PATH`. Don't enable it in default `bun run test`.
|
||||
- **Integration**: `tests/test_worker_smoke.py` is gated by `ROBOMP_INTEGRATION=1` (uses `pytestmark.skipif`) and needs `omp` on `PATH`. Don't enable it in default `bun run test:py`.
|
||||
- **Coverage expectation**: ~80 unit tests currently. New code with a control-flow branch needs a test covering it; new host tools need at minimum a happy path + one validation-failure path mirroring `test_host_tools.py`. Test logical behavior (assertions on observable effects in DB / HTTP requests), not literal strings or default config values.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# Build is split across three stages:
|
||||
#
|
||||
# 1) pi-artifacts — pull a pre-built `oh-my-pi/artifacts:dev` image (built
|
||||
# separately from /work/pi/Dockerfile, see `bun run pi-artifacts`):
|
||||
# separately from /work/pi/Dockerfile, see `bun run robomp:pi-artifacts`):
|
||||
# - pi_natives.linux-<arch>.node → /opt/bun/bin/ (the pi loader probes here)
|
||||
# - omp_rpc-*.whl → pip install
|
||||
# 2) web-builder — Bun + Vite compile the SolidJS dashboard bundle from
|
||||
|
||||
@@ -63,8 +63,8 @@ $EDITOR .env
|
||||
openssl rand -hex 32 # ROBOMP_GH_PROXY_HMAC_KEY
|
||||
openssl rand -hex 32 # GITHUB_WEBHOOK_SECRET
|
||||
|
||||
bun run pi-artifacts # build oh-my-pi/artifacts:dev (one-time / on pi change)
|
||||
bun run build && bun run up
|
||||
bun run robomp:pi-artifacts # build oh-my-pi/artifacts:dev (one-time / on pi change)
|
||||
bun run robomp:build && bun run robomp:up
|
||||
curl -fsS http://localhost:8080/healthz
|
||||
```
|
||||
|
||||
@@ -111,9 +111,9 @@ docker compose exec robomp robomp status # dump issues table
|
||||
docker compose exec robomp robomp cleanup owner/repo#123 # force workspace removal, state=abandoned
|
||||
```
|
||||
|
||||
`bun run …` shortcuts in `package.json` cover the common ones
|
||||
(`bun run triage`, `bun run replay`, `bun run sql`, `bun run events`,
|
||||
`bun run logs`, `bun run sh`, etc.).
|
||||
`bun run robomp:…` shortcuts in the root `package.json` cover the common
|
||||
lifecycle commands (`robomp:dev`, `robomp:build`, `robomp:up`, `robomp:down`,
|
||||
`robomp:logs`, `robomp:restart`, `robomp:reset`).
|
||||
|
||||
## Tests
|
||||
|
||||
@@ -124,7 +124,7 @@ ROBOMP_INTEGRATION=1 pytest -x tests/test_worker_smoke.py
|
||||
|
||||
The integration test spawns a real `omp --mode rpc` against an
|
||||
`httpx.MockTransport` GitHub and a local bare repo, so it needs `omp` on
|
||||
`PATH`. `bun run test` runs the unit suite.
|
||||
`PATH`. `bun run test:py` runs the unit suite.
|
||||
|
||||
## Security posture
|
||||
|
||||
@@ -186,7 +186,7 @@ The integration test spawns a real `omp --mode rpc` against an
|
||||
| `refusing to push: commit author identity mismatch` | Some commit not authored as `ROBOMP_GIT_AUTHOR_*`. The error lists the offending shas; `git commit --amend --reset-author --no-edit`. |
|
||||
| `refusing to push: working tree is dirty` | Uncommitted agent edits. Or just call `gh_open_pr`, which auto-commits `bun run fix` output. |
|
||||
| `bun check failed before PR creation` | Fix the reported failure and retry `gh_open_pr`. |
|
||||
| `Failed to load pi_natives` | Wrong arch / missing native. `bun run pi-artifacts` then `bun run build`. |
|
||||
| `Failed to load pi_natives` | Wrong arch / missing native. `bun run robomp:pi-artifacts` then `bun run robomp:build`. |
|
||||
| `No API key found for <provider>` | `~/.omp/agent/models.container.yml` mount missing or provider id mismatch with `ROBOMP_MODEL`. |
|
||||
|
||||
## Layout
|
||||
|
||||
@@ -17,7 +17,7 @@ services:
|
||||
context: ../..
|
||||
dockerfile: python/robomp/Dockerfile
|
||||
args:
|
||||
# Tag of the pre-built artifacts image produced by `bun run pi-artifacts`
|
||||
# Tag of the pre-built artifacts image produced by `bun run robomp:pi-artifacts`
|
||||
# (sources: pi root /Dockerfile). Override per-environment as needed.
|
||||
PI_ARTIFACTS_IMAGE: oh-my-pi/artifacts:dev
|
||||
image: robomp:dev
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
{
|
||||
"name": "robomp",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"packageManager": "bun@1.3.14",
|
||||
"description": "Self-hosted GitHub triage-and-fix bot driving oh-my-pi.",
|
||||
"scripts": {
|
||||
"dev": "bun run build && bun run up && bun run logs",
|
||||
"build": "bun run pi-artifacts && docker compose build",
|
||||
"rebuild": "docker build --no-cache -t \"${PI_ARTIFACTS_IMAGE:-oh-my-pi/artifacts:dev}\" ../.. && docker compose build --no-cache",
|
||||
"pi-artifacts": "docker build -t \"${PI_ARTIFACTS_IMAGE:-oh-my-pi/artifacts:dev}\" ../..",
|
||||
"clean-pi-artifacts": "docker image rm ${PI_ARTIFACTS_IMAGE:-oh-my-pi/artifacts:dev} || true",
|
||||
"image-info": "docker image inspect robomp:dev --format 'size: {{.Size}} bytes layers: {{len .RootFS.Layers}} created: {{.Created}}'",
|
||||
"up": "docker compose up -d",
|
||||
"down": "docker compose down",
|
||||
"restart": "docker compose restart robomp",
|
||||
"ps": "docker compose ps",
|
||||
"logs": "docker compose logs -f robomp",
|
||||
"proxy-logs": "docker compose logs -f gh-proxy",
|
||||
"tail": "docker compose logs --no-color --tail \"${1:-200}\" robomp",
|
||||
"log-grep": "docker compose logs --no-color robomp | grep -i -- \"$1\" || true",
|
||||
"sh": "docker compose exec robomp bash",
|
||||
"exec": "docker compose exec robomp \"$@\"",
|
||||
"triage": "docker compose exec robomp robomp triage \"$1\"",
|
||||
"replay": "docker compose exec robomp robomp replay \"$1\"",
|
||||
"issue-status": "docker compose exec robomp robomp status",
|
||||
"cleanup": "docker compose exec robomp robomp cleanup \"$1\"",
|
||||
"install:py": "pip install -e '.[dev]'",
|
||||
"test": "pytest -x tests/ \"$@\"",
|
||||
"test:integration": "ROBOMP_INTEGRATION=1 pytest -x tests/test_worker_smoke.py \"$@\"",
|
||||
"test:file": "pytest -x \"$@\"",
|
||||
"serve": "python3 -m robomp serve",
|
||||
"lint": "bun run lint:ts && bun run lint:py",
|
||||
"lint:ts": "biome check . --no-errors-on-unmatched",
|
||||
"lint:py": "ruff check src tests && ruff format --check src tests",
|
||||
"fix": "bun run fix:ts && bun run fix:py",
|
||||
"fix:ts": "biome check --write --unsafe --no-errors-on-unmatched .",
|
||||
"fix:py": "ruff check --fix src tests && ruff format src tests",
|
||||
"fmt": "biome format --write --no-errors-on-unmatched .",
|
||||
"check": "biome check . --no-errors-on-unmatched",
|
||||
"typecheck": "bun --cwd=web run typecheck",
|
||||
"web:dev": "bun --cwd=web run dev",
|
||||
"web:build": "bun --cwd=web run build",
|
||||
"web:preview": "bun --cwd=web run preview",
|
||||
"web:typecheck": "bun --cwd=web run typecheck",
|
||||
"healthz": "curl -fsS \"http://localhost:${ROBOMP_BIND_PORT:-8080}/healthz\" && echo",
|
||||
"readyz": "curl -fsS \"http://localhost:${ROBOMP_BIND_PORT:-8080}/readyz\" && echo",
|
||||
"events": "curl -fsS \"http://localhost:${ROBOMP_BIND_PORT:-8080}/events?limit=${1:-50}\" | python3 -m json.tool",
|
||||
"issues": "curl -fsS \"http://localhost:${ROBOMP_BIND_PORT:-8080}/issues?limit=${1:-100}\" | python3 -m json.tool",
|
||||
"sqlite": "docker compose exec robomp sqlite3 /data/robomp.sqlite",
|
||||
"sql": "docker compose exec robomp sqlite3 -header -column /data/robomp.sqlite \"$1\"",
|
||||
"tool-calls": "docker compose exec robomp sqlite3 -header -column /data/robomp.sqlite \"SELECT id, ts, tool, COALESCE(error,'ok') AS err FROM tool_calls WHERE issue_key='$1' ORDER BY id;\"",
|
||||
"recent-events": "docker compose exec robomp sqlite3 -header -column /data/robomp.sqlite \"SELECT received_at, event_type, issue_key, state, attempts FROM events ORDER BY received_at DESC LIMIT ${1:-20};\"",
|
||||
"stuck": "docker compose exec robomp sqlite3 -header -column /data/robomp.sqlite \"SELECT delivery_id, event_type, issue_key, state, attempts, started_at FROM events WHERE state IN ('queued','running') ORDER BY received_at;\"",
|
||||
"ping": "bash scripts/ping.sh",
|
||||
"wipe-workspaces": "rm -rf ./data/workspaces && mkdir -p ./data/workspaces",
|
||||
"nuke-data": "rm -rf ./data && mkdir -p ./data",
|
||||
"reset": "docker compose down -v && (docker image rm ${PI_ARTIFACTS_IMAGE:-oh-my-pi/artifacts:dev} || true)"
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ export { isFetching, lastTickAt, lastTickError };
|
||||
|
||||
let pollHandle: number | null = null;
|
||||
|
||||
export async function tick(): Promise<void> {
|
||||
async function tick(): Promise<void> {
|
||||
setIsFetching(true);
|
||||
try {
|
||||
await Promise.all([refetchStatus(), refetchLogs()]);
|
||||
@@ -113,7 +113,3 @@ export async function runCancel(deliveryId: string): Promise<void> {
|
||||
}
|
||||
void tick();
|
||||
}
|
||||
|
||||
export function clearTriggerStatus(): void {
|
||||
setTriggerStatus({ kind: "idle", text: "" });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user