From d4745591a9831172a7b6828e2150d06946aa7d21 Mon Sep 17 00:00:00 2001 From: -Puter <22245429+puterhimself@users.noreply.github.com> Date: Sat, 25 Jul 2026 16:12:04 +0530 Subject: [PATCH] feat(zopu): chat server, standalone chat route, and CORS --- .env.example | 3 + .../standalone-chat/standalone-chat-page.tsx | 186 +++ apps/web/src/routes.ts | 4 +- apps/web/src/routes/standalone/chat/page.tsx | 16 + bun.lock | 33 +- docs/dogfood-plan.md | 1281 +++++++++++++++++ package.json | 4 +- packages/backend/convex/_generated/api.d.ts | 2 + packages/env/src/web.ts | 1 + packages/primitives/package.json.new | 13 + packages/server/data/zopu.db | Bin 0 -> 450560 bytes packages/server/flue.config.ts | 11 + packages/server/package.json | 29 + packages/server/src/agents/zopu.ts | 42 + packages/server/src/app.ts | 31 + packages/server/src/db.ts | 4 + packages/server/src/env.ts | 46 + packages/server/src/git/gitea-service.ts | 360 +++++ packages/server/src/sandboxes/agentos.ts | 114 ++ packages/server/src/tools/issues.ts | 68 + packages/server/src/tools/pull-requests.ts | 68 + packages/server/src/tools/runtime.ts | 22 + packages/server/src/tools/workflows.ts | 22 + .../server/src/workflows/resolve-issue.ts | 159 ++ packages/server/tsconfig.json | 6 + 25 files changed, 2520 insertions(+), 5 deletions(-) create mode 100644 apps/web/src/components/standalone-chat/standalone-chat-page.tsx create mode 100644 apps/web/src/routes/standalone/chat/page.tsx create mode 100644 docs/dogfood-plan.md create mode 100644 packages/primitives/package.json.new create mode 100644 packages/server/data/zopu.db create mode 100644 packages/server/flue.config.ts create mode 100644 packages/server/package.json create mode 100644 packages/server/src/agents/zopu.ts create mode 100644 packages/server/src/app.ts create mode 100644 packages/server/src/db.ts create mode 100644 packages/server/src/env.ts create mode 100644 packages/server/src/git/gitea-service.ts create mode 100644 packages/server/src/sandboxes/agentos.ts create mode 100644 packages/server/src/tools/issues.ts create mode 100644 packages/server/src/tools/pull-requests.ts create mode 100644 packages/server/src/tools/runtime.ts create mode 100644 packages/server/src/tools/workflows.ts create mode 100644 packages/server/src/workflows/resolve-issue.ts create mode 100644 packages/server/tsconfig.json diff --git a/.env.example b/.env.example index 76d0139..c08b730 100644 --- a/.env.example +++ b/.env.example @@ -32,3 +32,6 @@ AGENT_MODEL_BASE_URL=https://ai.example.com/v1 AGENT_MODEL_API_KEY=replace-with-provider-api-key AGENT_MODEL_CONTEXT_WINDOW=262000 AGENT_MODEL_MAX_TOKENS=131072 + +# Zopu lean server (standalone chat + work actor) +VITE_ZOPU_SERVER_URL=http://localhost:3590 diff --git a/apps/web/src/components/standalone-chat/standalone-chat-page.tsx b/apps/web/src/components/standalone-chat/standalone-chat-page.tsx new file mode 100644 index 0000000..9c21fc2 --- /dev/null +++ b/apps/web/src/components/standalone-chat/standalone-chat-page.tsx @@ -0,0 +1,186 @@ +import { env } from "@code/env/web"; +import { FlueProvider, useFlueAgent } from "@flue/react"; +import { createFlueClient } from "@flue/sdk"; +import { ArrowUp, Loader2, MessageSquare } from "lucide-react"; +import { useEffect, useMemo, useRef, useState } from "react"; + +const STANDALONE_CHAT_ID = "zopu-standalone"; + +const StandaloneFlueProvider = ({ + children, +}: { + children: React.ReactNode; +}) => { + const client = useMemo( + () => + createFlueClient({ + baseUrl: env.VITE_ZOPU_SERVER_URL, + }), + [] + ); + return {children}; +}; + +const extractText = (parts: { type: string; text?: string }[]): string => + parts + .filter((p) => p.type === "text" && p.text) + .map((p) => p.text ?? "") + .join(""); + +const ChatMessage = ({ + parts, + role, +}: { + parts: { type: string; text?: string }[]; + role: string; +}) => { + const isUser = role === "user"; + return ( +
+ {!isUser && ( +
+ +
+ )} +
+ {extractText(parts) || ...} +
+
+ ); +}; + +const ChatContent = () => { + const agent = useFlueAgent({ + id: STANDALONE_CHAT_ID, + name: "zopu", + }); + const [input, setInput] = useState(""); + const scrollRef = useRef(null); + + const hasMessages = agent.messages.length > 0; + const isBusy = agent.status === "submitted" || agent.status === "streaming"; + + useEffect(() => { + const el = scrollRef.current; + if (el) { + el.scrollTop = el.scrollHeight; + } + }, [agent.messages, isBusy]); + + const submit = async () => { + const message = input.trim(); + if (!message || isBusy) { + return; + } + setInput(""); + await agent.sendMessage(message); + }; + + return ( +
+
+
+ Z +
+
+

Zopu

+

+ {isBusy ? "working..." : "ready"} +

+
+
+ +
+ {!hasMessages && !agent.historyReady && ( +
+ +
+ )} + {!hasMessages && agent.historyReady && ( +
+
+ +
+
+

+ Chat with Zopu +

+

+ Ask about issues, trigger work, or check PR status. +

+
+
+ {[ + "List recent pull requests", + "Create an issue for adding a settings page", + ].map((suggestion) => ( + + ))} +
+
+ )} + {agent.messages.map((message) => ( + + ))} +
+ +
+
+