diff options
| author | Adam Malczewski <[email protected]> | 2026-05-21 19:52:35 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-05-21 19:52:35 +0900 |
| commit | 4ba2673557c7cfd0bc31b03e2c35ab5e1efe60e7 (patch) | |
| tree | dddae8fc37a12157f4c8e840475e9e5d6203464a /packages/api/src/app.ts | |
| parent | 55633c90c0d96e62153a4b6655f3f833a3b46ad4 (diff) | |
| download | dispatch-4ba2673557c7cfd0bc31b03e2c35ab5e1efe60e7.tar.gz dispatch-4ba2673557c7cfd0bc31b03e2c35ab5e1efe60e7.zip | |
feat: tab system with per-tab agents, DB persistence, and DaisyUI tabs-lift UI
- Add tabs, messages, and settings tables to SQLite database
- Backend: refactor AgentManager to manage per-tab Agent instances via Map<tabId, TabAgent>
- Backend: WebSocket events tagged with tabId for multiplexing
- Backend: tab CRUD routes (create, list, update, archive, messages)
- Backend: persist user and assistant messages to DB during chat
- Frontend: new tabStore replaces single chatStore with multi-tab reactive state
- Frontend: TabBar component using DaisyUI tabs-lift style with status dots
- Frontend: Settings sidebar panel for title generation model selection
- Frontend: wire ChatPanel, ChatInput, Header to use tabStore
- Fix HMR listener accumulation via wsClient.clearCallbacks()
- Delete old single-chat chatStore (chat.svelte.ts)
Diffstat (limited to 'packages/api/src/app.ts')
| -rw-r--r-- | packages/api/src/app.ts | 19 |
1 files changed, 13 insertions, 6 deletions
diff --git a/packages/api/src/app.ts b/packages/api/src/app.ts index ce26aaa..f5588a6 100644 --- a/packages/api/src/app.ts +++ b/packages/api/src/app.ts @@ -5,6 +5,7 @@ import { PermissionManager } from "./permission-manager.js"; import { configRoutes } from "./routes/config.js"; import { skillsRoutes } from "./routes/skills.js"; import { modelsRoutes, startWakeScheduler } from "./routes/models.js"; +import { tabsRoutes } from "./routes/tabs.js"; export const permissionManager = new PermissionManager(); export const agentManager = new AgentManager(permissionManager); @@ -17,7 +18,7 @@ app.use( origin: "http://localhost:5173", credentials: true, allowHeaders: ["Content-Type", "Authorization"], - allowMethods: ["GET", "POST", "OPTIONS"], + allowMethods: ["GET", "POST", "PATCH", "PUT", "DELETE", "OPTIONS"], }), ); @@ -29,19 +30,24 @@ app.get("/status", (c) => { return c.json({ status: agentManager.getStatus(), messageCount: agentManager.getMessageCount(), + statuses: agentManager.getAllStatuses(), }); }); app.post("/chat", async (c) => { - const body = await c.req.json<{ message?: unknown; keyId?: unknown; modelId?: unknown; reasoningEffort?: unknown }>(); - const message = body.message; + const body = await c.req.json<{ tabId?: unknown; message?: unknown; keyId?: unknown; modelId?: unknown; reasoningEffort?: unknown }>(); + const { tabId, message } = body; + + if (typeof tabId !== "string" || tabId.trim() === "") { + return c.json({ error: "tabId must be a non-empty string" }, 400); + } if (typeof message !== "string" || message.trim() === "") { return c.json({ error: "message must be a non-empty string" }, 400); } - if (agentManager.getStatus() === "running") { - return c.json({ error: "agent is already running" }, 409); + if (agentManager.getTabStatus(tabId) === "running") { + return c.json({ error: "agent is already running for this tab" }, 409); } const keyId = typeof body.keyId === "string" ? body.keyId : undefined; @@ -52,7 +58,7 @@ app.post("/chat", async (c) => { : undefined; // Non-blocking — let the agent run in the background - agentManager.processMessage(message, keyId, modelId, reasoningEffort).catch(console.error); + agentManager.processMessage(tabId, message, keyId, modelId, reasoningEffort).catch(console.error); return c.json({ status: "ok" }); }); @@ -60,6 +66,7 @@ app.post("/chat", async (c) => { app.route("/config", configRoutes); app.route("/skills", skillsRoutes); app.route("/models", modelsRoutes); +app.route("/tabs", tabsRoutes); // Start the wake scheduler on boot (restores persisted schedule) startWakeScheduler(); |
