Add "gitea" to local RepoResult provider type (was missing from UI
interface despite being returned by repo-search). Copy opencode binary
instead of symlinking — symlink through /root/ is inaccessible to the
nextjs user due to directory permissions.
- GITEA_URL was pointing to gitea.platform.svc but the Helm chart
names the HTTP service gitea-helm-http.platform.svc
- Add Gitea badge (GT, green) to repo search results UI
- Update placeholder and credential hint to mention Gitea
- Rewrite internal service URLs to external gitea.coreworlds.io in
search results so agents can clone from outside the cluster
- Add error logging to diagnose search failures
The opencode curl installer puts the binary in /root/.local/bin which
isn't on PATH for the nextjs user. Add a symlink to /usr/local/bin
after install. Also ensure /usr/local/bin is always in the PATH
passed to spawned agent processes.
The localhost check using host header and x-forwarded-for was unreliable
in the standalone Next.js server which may inject forwarded headers
internally. Replace with a per-process random token shared between the
PTY server and the API route via env var.
In K8s, HOSTNAME is set to the pod name, so the server only listened
on that interface. The PTY server's loopback fetch to 127.0.0.1 was
connection-refused. Always bind to 0.0.0.0 so loopback works.
The standalone next package is trimmed and doesn't include webpack.
The custom server.js was using next() which triggers config loading
that requires webpack. Fix by extracting the standalone config at
build time and setting __NEXT_PRIVATE_STANDALONE_CONFIG before
requiring next, matching what the generated standalone server does.
server.js requires 'next', which the standalone output places at
apps/harness/node_modules/next. Running server.js from the repo root
meant Node couldn't resolve it. Move server.js and pty-server.js into
apps/harness/ so module resolution finds the standalone node_modules.
The Dockerfile check in the while-read loop used `[ -f ... ] && echo`,
which exits non-zero for packages without Dockerfiles. With bash's
pipefail, this killed the entire step. Also remove unused GitHub
workflow copies since CI runs on Gitea only.
Turbo's change detection includes shared packages like @homelab/db,
which don't have Dockerfiles. Filter to only apps with a Dockerfile
to prevent 'path not found' errors during docker build.
Webhook endpoint at /api/webhooks/gitea receives Gitea status events,
matches them against configurable event triggers with conditions
(event type, repo glob, state, context), renders task templates with
{{variable}} substitution, and creates harness tasks automatically.
Includes circuit breaker: after N consecutive task failures from the
same trigger (default 3), the trigger auto-disables. Re-enable
manually via PATCH /api/event-triggers/:id.
New tables: harness_event_triggers (rules + circuit breaker state),
harness_event_log (audit trail + dedup via X-Gitea-Delivery).
@modelcontextprotocol/server-git is not published to npm (it's a
Python package). Agents already have git installed and can use it
directly, so the MCP wrapper is unnecessary.
golang:1.26-alpine uses GOPATH=/go (not /root/go), so the binary was
installed to /go/bin/gitea-mcp but the COPY looked at /root/go/bin/.
Set GOBIN=/usr/local/bin for a deterministic install location.
Also adds harness MCP server bundle to the image.
Wire 5 MCP servers into Claude Code agents spawned by the harness:
- Gitea MCP for repo/issue/PR management on self-hosted Gitea
- Kubernetes MCP with read-only RBAC for cluster inspection
- Postgres MCP with read-only user for database queries
- Filesystem and Git MCP scoped to task worktrees
Generates .claude/settings.json in each worktree before agent spawn.
Gracefully skips for Codex/OpenCode runtimes (no MCP support).
Also fixes node-pty build failure by using local Node.js headers
instead of downloading from unofficial-builds.nodejs.org (ECONNRESET).
Move Field component out of NewTaskTab to prevent React from
remounting input wrappers on every keystroke. Same root cause as
the ProjectsTab DetailView fix.
DetailView was defined as a component inside ProjectsTab's render,
causing React to unmount/remount it on every keystroke. Replace with
inline JSX so the input element identity stays stable across renders.
Browser-based interactive terminal sessions with agent CLIs via
WebSocket + node-pty. Supports full TUI rendering (colors, cursor,
ctrl-c) through xterm.js in the browser.
Architecture: xterm.js ←WebSocket→ pty-server.js ←PTY→ agent CLI
- Extract shared buildAgentEnv() from executor into agent-env.ts
- Add internal /api/agents/[id]/env endpoint for PTY server
- Add pty-server.js (WebSocket + node-pty, max 3 sessions, 2hr cleanup)
- Add custom server.js wrapping Next.js with WebSocket upgrade
- Add ChatTab component with agent selector and terminal
- Wire CHAT tab into dashboard nav and render
- Configure serverExternalPackages for node-pty
- Update Dockerfile with build tools and custom server
- Bump k8s memory limit 1Gi → 2Gi for PTY sessions
Next.js standalone output nests server.js under apps/harness/ when
built from a pnpm workspace. Preserve the directory structure and
update CMD to point to the correct server.js path.
Support Gitea alongside GitHub/GitLab for repo search, authenticated
cloning, and pull request creation via Gitea API. Tasks can specify
gitProvider and gitBaseUrl in their spec (defaults to github for
backwards compat). Auto-discovers GITEA_TOKEN from env on boot.
Add model fetchers for OpenCode Zen (https://opencode.ai/zen/v1/models) and
Go (https://opencode.ai/zen/go/v1/models) APIs. Register opencode-go as a new
provider, load shared credentials from auth.json, add known models with pricing,
and create default agents for both tiers on first boot.
Replace the manual "Add Model" form with a search bar that filters by model
name/ID and paginate the catalog at 25 models per page.
Switch harness Dockerfile to pnpm with repo root build context so
workspace:^ dependency on @homelab/db resolves. Use .dockercontext
marker file to opt individual apps into root context builds while
keeping web/api on their local app context.
Replace all in-memory Map-backed stores (credentials, models, agents,
tasks, iterations, usage) with Drizzle ORM queries against the
homelab-pg PostgreSQL cluster. All store functions are now async.
- Add 6 harness_* tables to @homelab/db schema
- Generate and apply initial Drizzle migration
- Add lazy DB connection proxy to avoid build-time errors
- Wire DATABASE_URL from sealed secret into harness deployment
- Update all API routes, orchestrator, executor, and boot to await
async store operations
Use globalThis for all in-memory stores (credentials, models, agents,
tasks) so the instrumentation hook and API route handlers share the
same data. Next.js bundles these as separate chunks with independent
module instances, causing boot-populated state to be invisible to
API routes.
Read mounted secret files (Claude OAuth, OpenCode auth.json) and env
vars on boot, register them as credentials, fetch available models
from provider APIs, and create default agent configs for each viable
runtime+provider+model combination.
Strip all seed/mock data (fake tasks, models, usage entries, agent configs)
so the dashboard starts clean and populates from real API state. Add
/api/agents/health endpoint that validates each agent's provider credentials
and CLI availability.