# gitea Consolidated Gitea API client and pi extension for git.dominat.us. Provides a pure TypeScript Gitea API client (zero external dependencies), a pi extension with 22 tools for LLM use, and a standalone CLI. Works as a pi extension in both **pi-bot** (persistent session, webhook server + notification polling) and **openclaw** (ephemeral sessions, tools-only). ## Structure ``` src/ Core API client (pure fetch, token auth, zero deps) client.ts GiteaClient class + request helpers repos.ts Repo operations issues.ts Issues + comments pulls.ts Pull requests actions.ts CI/Actions workflow runs files.ts File read/write (POST for create, PUT for update) webhooks.ts Webhook management index.ts Re-exports pi-extension/ Pi extension adapter index.ts Registers tools, lifecycle hooks, runtime detection tools/ read-tools.ts 8 read tools write-tools.ts 12 write + management tools webhook/ server.ts Inbound webhook server, notification poller, @mention routing cli.ts Standalone CLI (replaces gitea-scripts/gitea.js) SKILL.md Agent skill definition (auto-discovered by pi) TOOL.md Full tool + environment variable reference ``` ## Installation ### As a pi extension Symlink or copy `pi-extension/` into your `/extensions/` directory: ```bash ln -sf /path/to/gitea/pi-extension ~/.pi/agent/extensions/pi-gitea ``` Or add to `settings.json`: ```json { "packages": ["/path/to/gitea/pi-extension"] } ``` ### In pi-bot (Docker) The `pi-bot` Dockerfile mounts the whole package and symlinks it: ``` -v /srv/pods/pi-bot/gitea:/app/gitea:Z ``` `entrypoint.sh` creates `~/.pi/agent/extensions/pi-gitea → /app/gitea/pi-extension` on startup. ### In openclaw (Docker) The `openclaw-custom` Dockerfile copies `gitea/` into `/app/gitea/`. An entrypoint wrapper creates the symlink into `agentDir/extensions/pi-gitea` at container start. Webhook server + polling are disabled automatically (tools only); openclaw's hooks system handles inbound events. ### As a library ```typescript import { GiteaClient, listRepos, getIssue } from "./src/index.js"; const client = new GiteaClient({ url: "https://git.dominat.us", token: process.env.GITEA_TOKEN!, }); const repos = await listRepos(client); const issue = await getIssue(client, "owner", "repo", 1); ``` ### As a CLI ```bash export GITEA_TOKEN="your-token" npx tsx cli.ts whoami npx tsx cli.ts repos npx tsx cli.ts issues owner/repo npx tsx cli.ts issue owner/repo 42 npx tsx cli.ts create-issue owner/repo "Bug title" --body "Description" npx tsx cli.ts comment owner/repo 42 "Fixed in #7" npx tsx cli.ts close owner/repo 42 npx tsx cli.ts prs owner/repo npx tsx cli.ts runs owner/repo npx tsx cli.ts logs owner/repo 1234 ``` ## Runtime Detection On `session_start` the extension auto-detects which runtime it's in: | Condition | Mode | Behaviour | |-----------|------|-----------| | `ctx.sendUserMessage` exists | **pi-bot** | Events injected directly into session; webhook server + notification poller start | | `GITEA_HOOKS_URL` set | **openclaw** | Events POSTed to `GITEA_HOOKS_URL/hooks/agent`; no server/poller | | `GITEA_ENABLE_POLLING=1` | forced polling | Webhook server + poller start regardless of other settings | ## @mention Routing In collab repos (where the bot isn't the owner), the default response mode is `"mention"` — the bot only responds when its username appears as `@botname` in the text. Own repos default to `"all"`. Override per repo with the `gitea_repo_config` tool: ``` gitea_repo_config repo="oc/myrepo" respondTo="all" ``` ## Authentication All API calls use `Authorization: token ` — no HMAC secrets. The inbound webhook endpoint optionally validates `Authorization: Bearer `. ## Configuration ### Required | Variable | Purpose | |----------|---------| | `GITEA_TOKEN` | Gitea API token | ### Core | Variable | Purpose | Default | |----------|---------|---------| | `GITEA_URL` | Gitea instance URL | `https://git.dominat.us` | | `GITEA_USER` | Bot's own username (for @mention detection) | — | | `GITEA_OWNER` | Default repo owner for tools | — | | `GITEA_REPO` | Default repo name for tools | — | ### pi-bot (webhook server + polling) | Variable | Purpose | Default | |----------|---------|---------| | `GITEA_WEBHOOK_HOST` | Bind address | `0.0.0.0` | | `GITEA_WEBHOOK_PORT` | Inbound webhook port | `3000` | | `GITEA_WEBHOOK_TOKEN` | Bearer token for webhook validation | — (open) | | `GITEA_WEBHOOK_URL` | Public URL registered with Gitea | — | | `GITEA_POLL_INTERVAL` | Repo discovery interval (seconds) | `300` | | `GITEA_NOTIF_INTERVAL` | Notification poll interval (seconds) | `30` | ### openclaw (hooks delivery) | Variable | Purpose | Default | |----------|---------|---------| | `GITEA_HOOKS_URL` | openclaw gateway base URL | — | | `GITEA_HOOKS_TOKEN` | Bearer token for the hooks endpoint | — | | `GITEA_HOOKS_PATH` | Hooks endpoint path | `/hooks/agent` | | `GITEA_ENABLE_POLLING` | Set `"1"` to force webhook server + polling | — | See [TOOL.md](TOOL.md) for the full tool reference and [SKILL.md](SKILL.md) for the agent skill definition.