- Switch typecheck from 'pnpm -r exec tsgo' to 'pnpm -r run typecheck'
to skip ui package (electrobun ships .ts source with type errors)
- Add typecheck script to cli/package.json
- Fix formatting in cli/src/cli.ts
- commits: use RPC for branches, commits, working tree status
- runs: use RPC for run detail, logs, timeline, errors, Finder
- workflows: pass RPC to getAppStateAsync
- state: remove server-side ui-state, use RPC for cross-view state
- runs.html: add 'Open in Finder' button
- commits.html: minor layout tweaks
Add typed RPC methods for cross-view state management and direct
filesystem access from the bun process:
- getInitialState, setActiveRunId, getActiveRunId
- getBranches, getCommits, getWorkingTreeDirty
- getRunDetail, getRunLogs, getRunTimeline, getRunErrors
- openRunInFinder
This replaces HTTP API calls with Electrobun RPC, solving the
views:// protocol limitation where query params and hash fragments
are treated as part of the file path.
- Rename runner label from 'opposite-actions' to 'machinen' everywhere
- Fix git shim: create seed commit when HEAD is invalid so branches work
- Move warm-modules under cache/ subdirectory
- Add /runs/path endpoint for Finder integration
- Remove /ui-state endpoints (replaced by Electrobun RPC)
- Log runner CLI args to stdout and log file for debugging
README.md:
- Recommend OrbStack as the preferred Docker provider on macOS
- Rename dtu/github-actions → dtu-github-actions throughout
- Add 'Headless Mode (This Repository Only)' section with oa run examples
- Add 'Full Services Stack' heading before existing pnpm dev content
.env.example: fix GITHUB_REPO example value to use machinen repo name
.gitignore: add .machinen so repo-scoped run state is never accidentally
committed to version control.
ui/src/mainview/commits.ts: fix run group header — change cursor from
'default' to 'pointer' and wire up a click handler that navigates to
the runs view for the selected run.
Each repository now maintains its own isolated run history under
<repo>/.machinen/runs/ instead of the shared global working directory.
run-store.ts:
- getRunsForCommit(): scans <repo>/.machinen/runs/ for machinen-* dirs;
reads metadata from runs/<name>/logs/metadata.json
- getRecentRuns(): iterates each registered repo's own .machinen/runs/
instead of the shared logsDir
- getRunDetail() / getRunStats() / getStatsHistory(): resolve runDir via
resolveRunDir() which checks each repo's .machinen/runs/
- resolveRunDir(): new helper to locate a runId across all known repos
- getRunsDirForRepo(): returns <repo>/.machinen/runs/
runner.ts:
- Uses runDir (co-located runs/<name>/) for work/, shims/, diag/
- Docker network renamed from oa-net-* to machinen-net-*
- Runner container named machinen-<n> instead of oa-runner-<n>
local-job.ts:
- Cleanup now removes the entire runDir on success (single rm)
- On failure, retains runDir so work/, shims/, diag/, logs/ are all
available for inspection
service-containers.ts / .test.ts: minor reference updates
startEphemeralDtu() spins up an independent DTU server bound to a random
OS-assigned port (port 0). Each call creates isolated state, making it
suitable for per-repo headless runs.
- ephemeral.ts: new module exporting startEphemeralDtu(cacheDir)
- Binds to 127.0.0.1:0 and returns the assigned port + close()
- Injects repo-scoped cacheDir before starting
- store.ts: adds setCacheDir() so the cache path can be overridden at
runtime for ephemeral instances (default still reads from env)
Update all Docker container/network filters to use the 'machinen-' prefix.
Also updates pruneStaleWorkspaces to scan runs/ for 'machinen-' entries
instead of work/ for 'oa-runner-' entries, matching the new directory layout.
- killAllRunnerContainers: filter 'machinen-' containers, 'machinen-net-' networks
- killRunnerContainers: remove 'machinen-net-<name>' network
- pruneOrphanedDockerResources: filter 'machinen-net-' networks
- pruneStaleWorkspaces: scan runs/ for 'machinen-*' run directories
- Tests updated to reflect new naming and directory structure
Each run now gets its own top-level run directory under <workDir>/runs/:
runs/<name>/ — runDir (work, shims, diag all live here)
runs/<name>/logs/ — logDir (output.log, debug.log, metadata.json)
This co-locates all per-run artefacts so cleanup and debugging are easier.
- getRunsDir(): new export returning <workDir>/runs/
- createLogContext(): adds runDir field; logDir is now a sub-dir of runDir
- getNextLogNum(): updated to parse new machinen-<n>-j<n>-m<n> suffix convention
- ensureLogDirs(): creates runs/ instead of logs/
- Tests rewritten to use real temp dirs (no fs mocks) for reliability
- vitest.config.ts: bumps testTimeout to 30s for fs integration tests
The global ~/.config/oa/config.jsonc is no longer read at startup.
All components now source the working directory from the OA_WORKING_DIR
environment variable, which is already injected by the supervisor when
spawning child runner processes.
- supervisor/config.ts: removes loadOaConfig, parseJsonc, DEFAULT_CONFIG_PATH
- supervisor/cli.ts: removes --config flag and loadOaConfig calls; reads
OA_WORKING_DIR directly; drops config-path forwarding to server
- supervisor/index.ts: removes loadOaConfig (legacy entry-point)
- supervisor/server/index.ts: removes JSONC persistence from PUT /concurrency
- dtu/config.ts: removes loadOaConfig, parseJsonc, DEFAULT_CONFIG_PATH
- dtu/server/index.ts: extracts process start-up into server/start.ts
- dtu/server/start.ts: new dedicated entry-point that reads OA_WORKING_DIR
- ui/src/bun/config.ts: removes JSONC loading; reads OA_WORKING_DIR directly
- ui/src/bun/index.ts: removes uiConfigPath/parsedConfig references
- Show warm 🔥 / cold ❄️ cache badge per workflow run in commits view
- Live-ticking elapsed time for running jobs (updates every second)
- Remove CPU/memory/network stats graphs from runs page
- Add GET /workflows/warm-status to expose whether node_modules cache
is warm for a given repo (used by UI warm/cold badge)
- Persist maxConcurrentJobs to config file on PUT /concurrency so the
setting survives supervisor restarts
- Forward OA_WORKING_DIR env var in CLI so spawned runners inherit the
server's working directory
deriveRunStatus no longer calls docker inspect to check exit codes.
activeRuns (populated by setupJob, cleared by proc.on('close')) is the
authoritative source for live job state. metadata.status covers
everything after the process exits.
Show elapsed/timeout progress every second while waiting for a service
container (e.g. MySQL) to become healthy. Also log total time taken
once healthy so users can see how long it actually took.
No longer collecting CPU / memory / network metrics per user request.
Removes the background `docker stats` loop that polled every 5s and
wrote peakCpu, peakMemMB, peakNetRxMB, peakNetTxMB, statsHistory
into metadata.json. Also cleans up the now-unused execFile/promisify/execAsync.
Root cause: all concurrent custom-container jobs shared a single
`_/work/runner/` bind-mount at /home/runner. Concurrent config.sh calls
raced on .runner/.credentials, causing run.sh to fail with 'Not configured'.
Fix:
- Extract runner binary once into shared seed dir (`_/work/runner/`)
- Give each container its own writable copy (`_/work/runner-<name>/`)
- Strip stale .runner/.credentials from seed + per-container copy on startup
- Clean per-container copy after job completes
- Replace fixed `sleep 0.3` with nc poll loop so config.sh only runs
once the Node.js DTU proxy has actually bound port 80
- Log proxy startup timing ([OA] DTU proxy ready in Xms)
- cleanup.ts: rewrite copyWorkspace() to avoid shell injection
- Get file list from git, then copy each file via execFileSync/spawnSync
with argument arrays so dest and file paths are never shell-interpolated
- macOS: per-file cp -c via spawnSync (no xargs/sh -c)
- Linux: rsync via spawnSync with file list piped to stdin (--files-from=-)
- Fallback: Node.js fs.cpSync (extracted to copyViaNodeFs helper)
- local-job.ts: fix signal handler listener leak in executeLocalJob()
- Use process.once instead of process.on to prevent listener accumulation
across multiple job invocations in the same process
- Wrap entire job body in try/finally so SIGINT/SIGTERM handlers are always
removed even when the function throws before normal completion
The warm-pool concept (pre-warming runner containers) is no longer used.
All job execution goes through the wave-based orchestrator in server/.
- Delete warm-pool.ts (WarmPool class, startWarmPool, stopWarmPool)
- Remove startWarmPool/stopWarmPool calls from src/index.ts
The PATCH jobrequests handler was always injecting lockedUntil, even on
finish requests (which carry result + finishTime). This caused the runner
to see conflicting state on job completion.
- Only set lockedUntil on renewal requests (no result/finishTime present)
- Register both bare path and parameterised /:requestId for safety
- Add tests covering renewal, parameterised renewal, and finish