Skip to content

Commit 496d5ed

Browse files
committed
Add BullBoard
1 parent a898dd0 commit 496d5ed

File tree

9 files changed

+403
-2
lines changed

9 files changed

+403
-2
lines changed

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ src/node_modules
7777
!pnpm-workspace.yaml
7878
!scripts/bootstrap.mjs
7979
!apps/web-evals/
80+
!apps/cloud-agents/
8081
!src/
8182
!webview-ui/
8283
!packages/evals/.docker/entrypoints/runner.sh

apps/cloud-agents/docker-compose.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,22 @@ services:
3737
profiles:
3838
- server
3939

40+
bullmq-dashboard:
41+
container_name: cloud-agents-bullmq-dashboard
42+
build:
43+
context: ../../
44+
dockerfile: apps/cloud-agents/docker/Dockerfile.dashboard
45+
ports:
46+
- "${BULL_BOARD_PORT:-3002}:3002"
47+
environment:
48+
- REDIS_URL=redis://redis:6379
49+
- BULL_BOARD_PORT=3002
50+
- NODE_ENV=production
51+
depends_on:
52+
- redis
53+
profiles:
54+
- server
55+
4056
db:
4157
container_name: cloud-agents-db
4258
image: postgres:17.5
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
FROM node:20-slim AS base
2+
3+
ENV PNPM_HOME="/pnpm"
4+
ENV PATH="$PNPM_HOME:$PATH"
5+
RUN corepack enable
6+
7+
WORKDIR /app
8+
9+
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
10+
COPY scripts/bootstrap.mjs ./scripts/
11+
COPY packages/types/package.json ./packages/types/
12+
COPY packages/config-eslint/package.json ./packages/config-eslint/
13+
COPY packages/config-typescript/package.json ./packages/config-typescript/
14+
COPY apps/cloud-agents/package.json ./apps/cloud-agents/
15+
16+
RUN pnpm install --filter @roo-code/cloud-agents
17+
18+
COPY packages/types ./packages/types/
19+
COPY packages/config-eslint ./packages/config-eslint/
20+
COPY packages/config-typescript ./packages/config-typescript/
21+
COPY apps/cloud-agents ./apps/cloud-agents/
22+
23+
WORKDIR /app/apps/cloud-agents
24+
25+
EXPOSE 3002
26+
27+
CMD ["pnpm", "dashboard"]
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
CREATE TABLE "cloud_jobs" (
2+
"id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "cloud_jobs_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
3+
"type" text NOT NULL,
4+
"status" text DEFAULT 'pending' NOT NULL,
5+
"payload" jsonb NOT NULL,
6+
"result" jsonb,
7+
"error" text,
8+
"created_at" timestamp DEFAULT now() NOT NULL,
9+
"started_at" timestamp,
10+
"completed_at" timestamp
11+
);
12+
--> statement-breakpoint
13+
CREATE TABLE "cloud_tasks" (
14+
"id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "cloud_tasks_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
15+
"job_id" integer NOT NULL,
16+
"task_id" integer,
17+
"container_id" text,
18+
"created_at" timestamp DEFAULT now() NOT NULL
19+
);
20+
--> statement-breakpoint
21+
ALTER TABLE "cloud_tasks" ADD CONSTRAINT "cloud_tasks_job_id_cloud_jobs_id_fk" FOREIGN KEY ("job_id") REFERENCES "public"."cloud_jobs"("id") ON DELETE no action ON UPDATE no action;
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
{
2+
"id": "8f65bfed-78de-4e22-a15f-36de8afe5f2e",
3+
"prevId": "00000000-0000-0000-0000-000000000000",
4+
"version": "7",
5+
"dialect": "postgresql",
6+
"tables": {
7+
"public.cloud_jobs": {
8+
"name": "cloud_jobs",
9+
"schema": "",
10+
"columns": {
11+
"id": {
12+
"name": "id",
13+
"type": "integer",
14+
"primaryKey": true,
15+
"notNull": true,
16+
"identity": {
17+
"type": "always",
18+
"name": "cloud_jobs_id_seq",
19+
"schema": "public",
20+
"increment": "1",
21+
"startWith": "1",
22+
"minValue": "1",
23+
"maxValue": "2147483647",
24+
"cache": "1",
25+
"cycle": false
26+
}
27+
},
28+
"type": {
29+
"name": "type",
30+
"type": "text",
31+
"primaryKey": false,
32+
"notNull": true
33+
},
34+
"status": {
35+
"name": "status",
36+
"type": "text",
37+
"primaryKey": false,
38+
"notNull": true,
39+
"default": "'pending'"
40+
},
41+
"payload": {
42+
"name": "payload",
43+
"type": "jsonb",
44+
"primaryKey": false,
45+
"notNull": true
46+
},
47+
"result": {
48+
"name": "result",
49+
"type": "jsonb",
50+
"primaryKey": false,
51+
"notNull": false
52+
},
53+
"error": {
54+
"name": "error",
55+
"type": "text",
56+
"primaryKey": false,
57+
"notNull": false
58+
},
59+
"created_at": {
60+
"name": "created_at",
61+
"type": "timestamp",
62+
"primaryKey": false,
63+
"notNull": true,
64+
"default": "now()"
65+
},
66+
"started_at": {
67+
"name": "started_at",
68+
"type": "timestamp",
69+
"primaryKey": false,
70+
"notNull": false
71+
},
72+
"completed_at": {
73+
"name": "completed_at",
74+
"type": "timestamp",
75+
"primaryKey": false,
76+
"notNull": false
77+
}
78+
},
79+
"indexes": {},
80+
"foreignKeys": {},
81+
"compositePrimaryKeys": {},
82+
"uniqueConstraints": {},
83+
"policies": {},
84+
"checkConstraints": {},
85+
"isRLSEnabled": false
86+
},
87+
"public.cloud_tasks": {
88+
"name": "cloud_tasks",
89+
"schema": "",
90+
"columns": {
91+
"id": {
92+
"name": "id",
93+
"type": "integer",
94+
"primaryKey": true,
95+
"notNull": true,
96+
"identity": {
97+
"type": "always",
98+
"name": "cloud_tasks_id_seq",
99+
"schema": "public",
100+
"increment": "1",
101+
"startWith": "1",
102+
"minValue": "1",
103+
"maxValue": "2147483647",
104+
"cache": "1",
105+
"cycle": false
106+
}
107+
},
108+
"job_id": {
109+
"name": "job_id",
110+
"type": "integer",
111+
"primaryKey": false,
112+
"notNull": true
113+
},
114+
"task_id": {
115+
"name": "task_id",
116+
"type": "integer",
117+
"primaryKey": false,
118+
"notNull": false
119+
},
120+
"container_id": {
121+
"name": "container_id",
122+
"type": "text",
123+
"primaryKey": false,
124+
"notNull": false
125+
},
126+
"created_at": {
127+
"name": "created_at",
128+
"type": "timestamp",
129+
"primaryKey": false,
130+
"notNull": true,
131+
"default": "now()"
132+
}
133+
},
134+
"indexes": {},
135+
"foreignKeys": {
136+
"cloud_tasks_job_id_cloud_jobs_id_fk": {
137+
"name": "cloud_tasks_job_id_cloud_jobs_id_fk",
138+
"tableFrom": "cloud_tasks",
139+
"tableTo": "cloud_jobs",
140+
"columnsFrom": ["job_id"],
141+
"columnsTo": ["id"],
142+
"onDelete": "no action",
143+
"onUpdate": "no action"
144+
}
145+
},
146+
"compositePrimaryKeys": {},
147+
"uniqueConstraints": {},
148+
"policies": {},
149+
"checkConstraints": {},
150+
"isRLSEnabled": false
151+
}
152+
},
153+
"enums": {},
154+
"schemas": {},
155+
"sequences": {},
156+
"roles": {},
157+
"policies": {},
158+
"views": {},
159+
"_meta": {
160+
"columns": {},
161+
"schemas": {},
162+
"tables": {}
163+
}
164+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"version": "7",
3+
"dialect": "postgresql",
4+
"entries": [
5+
{
6+
"idx": 0,
7+
"version": "7",
8+
"when": 1749740498648,
9+
"tag": "0000_cuddly_luke_cage",
10+
"breakpoints": true
11+
}
12+
]
13+
}

apps/cloud-agents/package.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,20 @@
1515
"db:push": "pnpm drizzle-kit push",
1616
"db:check": "pnpm drizzle-kit check",
1717
"db:studio": "pnpm drizzle-kit studio",
18-
"services:start": "docker compose up -d db redis",
18+
"services:start": "docker compose up -d db redis bullmq-dashboard",
1919
"services:stop": "docker compose down",
20-
"worker:dev": "tsx src/lib/queue/worker.ts"
20+
"worker:dev": "tsx src/lib/queue/worker.ts",
21+
"dashboard": "tsx scripts/dashboard.ts"
2122
},
2223
"dependencies": {
24+
"@bull-board/api": "^6.10.1",
25+
"@bull-board/express": "^6.10.1",
26+
"@bull-board/ui": "^6.10.1",
2327
"@roo-code/types": "workspace:^",
2428
"bullmq": "^5.37.0",
2529
"drizzle-orm": "^0.44.1",
2630
"execa": "^9.6.0",
31+
"express": "^5.1.0",
2732
"ioredis": "^5.4.3",
2833
"next": "^15.2.5",
2934
"postgres": "^3.4.7",
@@ -34,6 +39,7 @@
3439
"devDependencies": {
3540
"@roo-code/config-eslint": "workspace:^",
3641
"@roo-code/config-typescript": "workspace:^",
42+
"@types/express": "^5.0.3",
3743
"@types/node": "^22.15.20",
3844
"@types/react": "^18.3.23",
3945
"@types/react-dom": "^18.3.7",
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { createBullBoard } from "@bull-board/api"
2+
import { BullMQAdapter } from "@bull-board/api/bullMQAdapter"
3+
import { ExpressAdapter } from "@bull-board/express"
4+
import { Queue } from "bullmq"
5+
import IORedis from "ioredis"
6+
import express, { type Express, type Request, type Response } from "express"
7+
8+
const redis = new IORedis(process.env.REDIS_URL || "redis://localhost:6380", {
9+
maxRetriesPerRequest: null,
10+
})
11+
12+
const cloudQueue = new Queue("cloud-agents", { connection: redis })
13+
14+
const serverAdapter = new ExpressAdapter()
15+
serverAdapter.setBasePath("/admin/queues")
16+
17+
createBullBoard({ queues: [new BullMQAdapter(cloudQueue)], serverAdapter: serverAdapter })
18+
19+
const app: Express = express()
20+
21+
app.use("/admin/queues", serverAdapter.getRouter())
22+
23+
app.use("/", (req: Request, res: Response) => {
24+
res.redirect("/admin/queues")
25+
})
26+
27+
const port = process.env.BULL_BOARD_PORT || 3002
28+
29+
app.listen(port, () => {
30+
console.log(`Bull Board running on: http://localhost:${port}/admin/queues`)
31+
})

0 commit comments

Comments
 (0)