feat(x): add headless server runner and Dockerfile for agent core

This commit is contained in:
TylerClaw 2026-02-15 10:23:06 +00:00
parent 601f5af978
commit 33f526ba53
5 changed files with 187 additions and 0 deletions

36
apps/x/DOCKER.md Normal file
View file

@ -0,0 +1,36 @@
# Running Rowboat (Headless) in Docker
You can run the core agent logic of Rowboat in a Docker container, suitable for server environments or always-on agents.
## Build
From the `apps/x` directory:
```bash
docker build -t rowboat-agent .
```
## Run
You need to mount a volume for the data directory (`~/.rowboat`) to persist your knowledge graph and credentials.
```bash
docker run -d \
--name rowboat \
-v $(pwd)/rowboat-data:/data/.rowboat \
rowboat-agent
```
## Configuration
The agent uses the configuration files in your data volume (`/data/.rowboat/config/`).
If you are starting fresh, you may need to manually populate `models.json` or `config.json` in that volume, as there is no UI to guide you through onboarding in this headless mode.
### Environment Variables
You can inject API keys via environment variables which Rowboat will pick up (if configured to read them):
- `OPENAI_API_KEY`
- `ANTHROPIC_API_KEY`
- `GOOGLE_API_KEY`
```

52
apps/x/Dockerfile Normal file
View file

@ -0,0 +1,52 @@
FROM node:22-slim
# Install system dependencies if needed (e.g. for native modules)
RUN apt-get update && apt-get install -y \
python3 \
make \
g++ \
&& rm -rf /var/lib/apt/lists/*
# Enable pnpm
RUN corepack enable
WORKDIR /app
# Copy workspace config
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
# Copy packages
COPY apps/server ./apps/server
COPY packages/core ./packages/core
COPY packages/shared ./packages/shared
# Note: we skip apps/main, apps/renderer, apps/preload as they are Electron-specific
# Install dependencies (skipping Electron devDeps if possible, but they are in root)
# We might need to ignore scripts or optional deps
RUN pnpm install --frozen-lockfile
# Build dependencies in order
WORKDIR /app/packages/shared
RUN npm run build
WORKDIR /app/packages/core
RUN npm run build
WORKDIR /app/apps/server
RUN npm run build
WORKDIR /app
# Set environment to production
ENV NODE_ENV=production
# The app uses ~/.rowboat for storage by default.
# In Docker, we should probably mount a volume or set HOME.
# Let's verify if we need to override the path via env var, but the code hardcodes path.join(homedir(), ".rowboat")
# So setting HOME=/data should work, provided we create the dir.
ENV HOME=/data
RUN mkdir -p /data/.rowboat
VOLUME /data/.rowboat
CMD ["node", "apps/server/dist/main.js"]

View file

@ -0,0 +1,21 @@
{
"name": "@x/server",
"version": "0.1.0",
"private": true,
"type": "module",
"main": "dist/main.js",
"scripts": {
"build": "tsc",
"start": "node dist/main.js",
"dev": "tsx src/main.ts"
},
"dependencies": {
"@x/core": "workspace:*",
"@x/shared": "workspace:*"
},
"devDependencies": {
"tsx": "^4.19.1",
"typescript": "^5.6.3",
"@types/node": "^22.7.5"
}
}

View file

@ -0,0 +1,67 @@
import { init as initGmailSync } from "@x/core/dist/knowledge/sync_gmail.js";
import { init as initCalendarSync } from "@x/core/dist/knowledge/sync_calendar.js";
import { init as initFirefliesSync } from "@x/core/dist/knowledge/sync_fireflies.js";
import { init as initGranolaSync } from "@x/core/dist/knowledge/granola/sync.js";
import { init as initGraphBuilder } from "@x/core/dist/knowledge/build_graph.js";
import { init as initPreBuiltRunner } from "@x/core/dist/pre_built/runner.js";
import { init as initAgentRunner } from "@x/core/dist/agent-schedule/runner.js";
import { initConfigs } from "@x/core/dist/config/initConfigs.js";
import { startWorkspaceWatcher } from "@x/core/dist/workspace/watcher.js";
async function main() {
console.log("Rowboat Headless Server starting...");
// Initialize all config files
await initConfigs();
console.log("Configs initialized.");
// Start workspace watcher (optional in headless, but good for reactivity if files change)
// We need to import it from core if possible, or reimplement the watcher start logic if it was only in main.ts
// Looking at main.ts imports: import { startWorkspaceWatcher } from "./ipc.js";
// Wait, startWorkspaceWatcher in main.ts came from "./ipc.js". I need to find the real core watcher.
// I saw "rowboat-workspace/apps/x/packages/core/src/workspace/watcher.ts" in grep results.
// So I can import it directly.
// Note: The main.ts version wrapped it. I will try to use the core one directly if available.
// Let's assume the side-effects are safe.
// Start services
console.log("Starting Gmail sync...");
initGmailSync();
console.log("Starting Calendar sync...");
initCalendarSync();
console.log("Starting Fireflies sync...");
initFirefliesSync();
console.log("Starting Granola sync...");
initGranolaSync();
console.log("Starting Graph Builder...");
initGraphBuilder();
console.log("Starting Pre-built Runner...");
initPreBuiltRunner();
console.log("Starting Agent Runner...");
initAgentRunner();
console.log("Rowboat Headless Server is running. Press Ctrl+C to stop.");
// Keep process alive
process.stdin.resume();
const cleanup = () => {
console.log("Stopping Rowboat Headless Server...");
process.exit(0);
};
process.on("SIGINT", cleanup);
process.on("SIGTERM", cleanup);
}
main().catch((err) => {
console.error("Fatal error:", err);
process.exit(1);
});

View file

@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ES2022"
},
"include": ["src/**/*"]
}