feat(boltbot): add dev server and seed script for standalone testing
This commit is contained in:
parent
be30d2f088
commit
abbe5e9880
@ -5,4 +5,11 @@ import tailwindcss from "@tailwindcss/vite";
|
||||
export default defineConfig({
|
||||
base: "/boltbot/dashboard/",
|
||||
plugins: [react(), tailwindcss()],
|
||||
server: {
|
||||
proxy: {
|
||||
"/boltbot/receipts": "http://localhost:18789",
|
||||
"/boltbot/receipt": "http://localhost:18789",
|
||||
"/boltbot/stats": "http://localhost:18789",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
65
extensions/boltbot/scripts/dev-server.ts
Normal file
65
extensions/boltbot/scripts/dev-server.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
|
||||
import { LocalReceiptStore } from "../src/stores/local.js";
|
||||
|
||||
const PORT = 18789;
|
||||
const store = new LocalReceiptStore();
|
||||
|
||||
const CORS_HEADERS = {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "GET, OPTIONS",
|
||||
"Access-Control-Allow-Headers": "Content-Type",
|
||||
};
|
||||
|
||||
function json(res: ServerResponse, status: number, data: unknown) {
|
||||
res.writeHead(status, { "Content-Type": "application/json", ...CORS_HEADERS });
|
||||
res.end(JSON.stringify(data));
|
||||
}
|
||||
|
||||
function parseQuery(url: string): Record<string, string> {
|
||||
const idx = url.indexOf("?");
|
||||
if (idx === -1) return {};
|
||||
return Object.fromEntries(new URLSearchParams(url.slice(idx + 1)).entries());
|
||||
}
|
||||
|
||||
function pathname(url: string): string {
|
||||
const idx = url.indexOf("?");
|
||||
return idx === -1 ? url : url.slice(0, idx);
|
||||
}
|
||||
|
||||
const server = createServer(async (req: IncomingMessage, res: ServerResponse) => {
|
||||
if (req.method === "OPTIONS") {
|
||||
res.writeHead(204, CORS_HEADERS);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
const path = pathname(req.url ?? "");
|
||||
const query = parseQuery(req.url ?? "");
|
||||
|
||||
try {
|
||||
if (path === "/boltbot/receipts") {
|
||||
const limit = Math.min(Math.max(parseInt(query.limit ?? "50", 10) || 50, 1), 500);
|
||||
const offset = Math.max(parseInt(query.offset ?? "0", 10) || 0, 0);
|
||||
const receipts = await store.list({ limit, offset });
|
||||
json(res, 200, { receipts });
|
||||
} else if (path === "/boltbot/receipt") {
|
||||
const id = query.id;
|
||||
if (!id) return json(res, 400, { error: "missing_id" });
|
||||
const receipt = await store.get(id);
|
||||
if (!receipt) return json(res, 404, { error: "not_found" });
|
||||
json(res, 200, { receipt });
|
||||
} else if (path === "/boltbot/stats") {
|
||||
const stats = await store.stats();
|
||||
json(res, 200, stats);
|
||||
} else {
|
||||
json(res, 404, { error: "not_found" });
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Request error:", err);
|
||||
json(res, 500, { error: "internal_error" });
|
||||
}
|
||||
});
|
||||
|
||||
server.listen(PORT, () => {
|
||||
console.log(`boltbot dev server: http://localhost:${PORT}`);
|
||||
});
|
||||
62
extensions/boltbot/scripts/seed.ts
Normal file
62
extensions/boltbot/scripts/seed.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { LocalReceiptStore } from "../src/stores/local.js";
|
||||
import type { ActionReceipt } from "../src/receipt-store.js";
|
||||
|
||||
const MEDIUM_TOOLS = ["message", "write", "edit", "cron", "sessions_send", "browser", "canvas", "nodes"];
|
||||
const HIGH_TOOLS = ["exec", "apply_patch", "gateway", "sessions_spawn", "process"];
|
||||
|
||||
const ANOMALY_LABELS = ["shell_injection", "data_exfiltration", "unauthorized_gateway"];
|
||||
|
||||
function pick<T>(arr: T[]): T {
|
||||
return arr[Math.floor(Math.random() * arr.length)];
|
||||
}
|
||||
|
||||
function randInt(min: number, max: number): number {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
function randomHex64(): string {
|
||||
const buf = new Uint8Array(32);
|
||||
crypto.getRandomValues(buf);
|
||||
return Array.from(buf, (b) => b.toString(16).padStart(2, "0")).join("");
|
||||
}
|
||||
|
||||
function pickTool(): { name: string; tier: "medium" | "high" } {
|
||||
const isHigh = Math.random() < 0.4;
|
||||
return isHigh
|
||||
? { name: pick(HIGH_TOOLS), tier: "high" }
|
||||
: { name: pick(MEDIUM_TOOLS), tier: "medium" };
|
||||
}
|
||||
|
||||
const sessionCount = randInt(3, 5);
|
||||
const sessionKeys = Array.from({ length: sessionCount }, () => randomUUID());
|
||||
|
||||
const now = Date.now();
|
||||
const DAY_MS = 86_400_000;
|
||||
const receiptCount = randInt(50, 100);
|
||||
|
||||
const receipts: ActionReceipt[] = Array.from({ length: receiptCount }, () => {
|
||||
const { name, tier } = pickTool();
|
||||
const hasAnomaly = Math.random() < 0.125;
|
||||
|
||||
return {
|
||||
id: randomUUID(),
|
||||
timestamp: new Date(now - Math.random() * DAY_MS).toISOString(),
|
||||
sessionKey: pick(sessionKeys),
|
||||
tier,
|
||||
toolName: name,
|
||||
argumentsHash: randomHex64(),
|
||||
resultHash: randomHex64(),
|
||||
success: Math.random() < 0.9,
|
||||
durationMs: randInt(50, 5000),
|
||||
anomalies: hasAnomaly ? [pick(ANOMALY_LABELS)] : [],
|
||||
daCommitment: Math.random() < 0.2 ? randomHex64() : undefined,
|
||||
};
|
||||
});
|
||||
|
||||
receipts.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
||||
|
||||
const store = new LocalReceiptStore();
|
||||
for (const r of receipts) await store.put(r);
|
||||
|
||||
console.log(`Seeded ${receipts.length} receipts across ${sessionCount} sessions`);
|
||||
Loading…
Reference in New Issue
Block a user