From 4289e319ccae9141d4641771240782d04579588e Mon Sep 17 00:00:00 2001 From: Michael Gardiner <34581105+gardinermichael@users.noreply.github.com> Date: Mon, 10 Aug 2026 20:32:49 -0700 Subject: [PATCH 01/14] Fix localhost DevTools probe handling --- server/server.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/server/server.js b/server/server.js index 3da19a7..4e1cf97 100644 --- a/server/server.js +++ b/server/server.js @@ -78,6 +78,17 @@ app.set("trust proxy", true); app.use(cors({ exposedHeaders: ["x-continue-it-quota"] })); app.use(express.json({ limit: "2mb" })); +app.get("/", (req, res) => { + res.type("text/plain").send("Continue It backend is running. Use GET /health or POST /api/summarize.\n"); +}); + +// Chrome DevTools may probe this localhost path for automatic workspace setup. +// This backend does not expose a browser workspace, so answer quietly instead of +// letting Express generate a CSP-protected 404 page in local development. +app.get("/.well-known/appspecific/com.chrome.devtools.json", (req, res) => { + res.status(204).end(); +}); + function clientKeys(req) { const clientId = String(req.header("x-continue-it-client") || "").slice(0, 128); const ip = req.ip || req.socket?.remoteAddress || "unknown"; @@ -147,8 +158,8 @@ app.post("/api/summarize", async (req, res) => { return; } - const { source, mode, heuristicSummary, compactConversation } = req.body || {}; - if (!source || !mode || !heuristicSummary || !compactConversation) { + const { source, mode, compactConversation } = req.body || {}; + if (!source || !mode || !compactConversation) { res.status(400).json({ ok: false, error: "Missing required summarize fields." }); return; } @@ -179,7 +190,7 @@ app.post("/api/summarize", async (req, res) => { model: provider.model, temperature: 0.2, max_tokens: maxTokens, - messages: buildMessages({ source, mode, heuristicSummary, compactConversation }) + messages: buildMessages({ source, mode, compactConversation }) }) }); From ea4212b97897062de2e203113f624354f75907c5 Mon Sep 17 00:00:00 2001 From: Michael Gardiner <34581105+gardinermichael@users.noreply.github.com> Date: Mon, 10 Aug 2026 20:51:45 -0700 Subject: [PATCH 02/14] Clarify Server AI backend configuration --- README.md | 12 ++++++++---- background.js | 3 ++- popup.html | 6 ++++-- popup.js | 11 +++++++---- shared-ai.js | 5 ++++- 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 2bd2cb5..cf7386e 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ Pick a mode in the popup under **AI summary mode**. All three produce the same p | Mode | Quality | Cost | Privacy | Setup | |---|---|---|---|---| | **No AI** (default) | Good (local heuristic) | Free | Fully local, offline | None | -| **Server AI** | Better (real LLM) | Free, **5 exports / 24h** | Summary sent to the shared backend | None for the user | +| **Server AI** | Better (real LLM) | Depends on configured backend | Summary sent to the configured backend | A hosted backend, or your own local backend with a `.env` key | | **Your own API key** | Best (any model you like) | Free on the providers below | Summary sent to your chosen provider | Paste a key | If an AI request ever fails (rate limit, bad key, network), the extension automatically falls back to the local summary and adds a warning — an export never breaks. @@ -118,9 +118,13 @@ Any OpenAI-compatible endpoint works. Create a key (most need no credit card), p Free-tier limits change often — verify the current numbers on each provider's site. Keys are stored only in `chrome.storage.local` on your machine and are sent only to the provider you configured. -### Server AI — running the shared backend (for maintainers) +### Server AI — running or using a backend -The Server AI mode lets you offer smart summaries to users without them needing a key, capped at **5 exports per 24h per user** (by anonymous client id + IP) so it stays cheap. +Server AI is not bundled into the Chrome extension. The extension sends summary requests to a separate backend URL, and that backend uses a provider key from its own environment. + +Users do not need to enter an API key only when they are pointed at a backend already operated by someone else. If you run the backend locally, you must provide your own key in `.env`. Do not put a shared provider key in the extension files; extension source is inspectable by users. + +The included backend caps requests at **5 exports per 24h per user** (by anonymous client id + IP) so a maintainer-operated deployment can control cost. ```bash cd server # from the repo root @@ -139,7 +143,7 @@ DAILY_LIMIT=5 # exports per window RATE_WINDOW_HOURS=24 ``` -Then set the **Server URL** in the popup (default `http://localhost:8787`) and choose **Server AI**. Deploy the server anywhere (Render, Railway, Fly, a VPS) and point the popup at that URL. +Then set the **Server URL** in the popup (default `http://localhost:8787`) and choose **Server AI**. To offer Server AI to other users without asking them for provider keys, deploy the server anywhere (Render, Railway, Fly, a VPS), configure provider secrets in that hosting environment, and tell users to point the popup at that URL. > The in-memory rate limiter is per-instance and resets on restart. For a hardened multi-instance deployment, back it with Redis or a database. diff --git a/background.js b/background.js index 03830e7..fac4cc6 100644 --- a/background.js +++ b/background.js @@ -74,7 +74,8 @@ function buildSummarizeMessages({ source, mode, compactConversation }) { // --- Shared backend (sarvam AI) async function summarizeViaServer(payload) { - const serverUrl = DEFAULT_SERVER_URL.replace(/\/$/, ""); + const stored = await getStorage([AI_STORAGE_KEYS.serverUrl]); + const serverUrl = (stored[AI_STORAGE_KEYS.serverUrl] || DEFAULT_SERVER_URL).replace(/\/$/, ""); const url = `${serverUrl}/api/summarize`; if (!(await hasOriginPermission(serverUrl))) { diff --git a/popup.html b/popup.html index ac2a366..a655887 100644 --- a/popup.html +++ b/popup.html @@ -61,11 +61,13 @@
Choose how the export summary is generated.