mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-04-25 00:16:29 +02:00
feat(x): add headless server runner and Dockerfile for agent core
This commit is contained in:
parent
601f5af978
commit
33f526ba53
5 changed files with 187 additions and 0 deletions
36
apps/x/DOCKER.md
Normal file
36
apps/x/DOCKER.md
Normal 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
52
apps/x/Dockerfile
Normal 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"]
|
||||||
21
apps/x/apps/server/package.json
Normal file
21
apps/x/apps/server/package.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
67
apps/x/apps/server/src/main.ts
Normal file
67
apps/x/apps/server/src/main.ts
Normal 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);
|
||||||
|
});
|
||||||
11
apps/x/apps/server/tsconfig.json
Normal file
11
apps/x/apps/server/tsconfig.json
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "src",
|
||||||
|
"outDir": "dist",
|
||||||
|
"module": "NodeNext",
|
||||||
|
"moduleResolution": "NodeNext",
|
||||||
|
"target": "ES2022"
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"]
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue