@tstax/coding-tab 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.js +50 -42
- package/dist/browser.js.map +1 -1
- package/dist/server.cjs +26 -5
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +18 -0
- package/dist/server.d.ts +18 -0
- package/dist/server.js +26 -5
- package/dist/server.js.map +1 -1
- package/dist/style.css +150 -8
- package/package.json +1 -1
package/dist/server.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/index.ts","../node_modules/tsup/assets/cjs_shims.js","../src/server/authRoutes.ts","../src/server/authMiddleware.ts","../src/server/session.ts","../src/server/agentRoutes.ts","../src/server/models.ts","../src/server/sessions.ts","../src/server/storage.ts","../src/server/chatRoutes.ts","../src/server/githubRoutes.ts","../src/server/staticAssets.ts"],"sourcesContent":["import express, { type Express, type Router } from \"express\";\nimport { makeAuthRouter, type GitHubOAuthOptions } from \"./authRoutes.js\";\nimport { makeAgentRouter } from \"./agentRoutes.js\";\nimport { makeChatRouter } from \"./chatRoutes.js\";\nimport { makeGitHubRouter } from \"./githubRoutes.js\";\nimport { makeRequireAuth } from \"./authMiddleware.js\";\nimport { makeAssetRouter } from \"./staticAssets.js\";\nimport { createDefaultStorage, type ChatStorage } from \"./storage.js\";\n\nexport interface MountCodingTabOptions {\n cursorApiKey: string;\n githubOAuth: GitHubOAuthOptions;\n sessionPassword: string;\n defaultRepo: { url: string; ref?: string };\n basePath?: string;\n secure?: boolean;\n envName?: string;\n skipReviewerRequest?: boolean;\n /**\n * Optional pluggable persistence layer. Defaults to a JSON file store\n * rooted at `dataDir` (or `<cwd>/.coding-tab-data`). Override to plug in\n * Postgres, Supabase, Redis, etc. on hosts with ephemeral filesystems.\n */\n storage?: ChatStorage;\n /** Directory used by the default file storage. Ignored when `storage` is set. */\n dataDir?: string;\n}\n\nexport function mountCodingTab(app: Express, options: MountCodingTabOptions): Router {\n const basePath = (options.basePath ?? \"/coding-tab\").replace(/\\/$/, \"\");\n const secure = options.secure ?? process.env.NODE_ENV === \"production\";\n\n if (!options.cursorApiKey) throw new Error(\"[coding-tab] cursorApiKey is required\");\n if (!options.sessionPassword || options.sessionPassword.length < 32) {\n throw new Error(\"[coding-tab] sessionPassword must be at least 32 characters\");\n }\n if (!options.githubOAuth?.clientId || !options.githubOAuth?.clientSecret) {\n throw new Error(\"[coding-tab] githubOAuth.clientId and clientSecret are required\");\n }\n if (!options.githubOAuth.callbackUrl) {\n throw new Error(\"[coding-tab] githubOAuth.callbackUrl is required\");\n }\n if (!options.githubOAuth.allowedLogins || options.githubOAuth.allowedLogins.length === 0) {\n console.warn(\"[coding-tab] WARNING: allowedLogins is empty — anyone with a GitHub account can sign in.\");\n }\n\n const storage = options.storage ?? createDefaultStorage(options.dataDir);\n\n const router = express.Router();\n router.use(express.json({ limit: \"1mb\" }));\n\n const assetRouter = makeAssetRouter(basePath);\n router.use(assetRouter);\n\n const authRouter = makeAuthRouter({\n oauth: options.githubOAuth,\n sessionPassword: options.sessionPassword,\n secure,\n basePath,\n defaultRepoUrl: options.defaultRepo.url,\n defaultRepoRef: options.defaultRepo.ref,\n });\n router.use(authRouter);\n\n const requireAuth = makeRequireAuth({\n sessionPassword: options.sessionPassword,\n secure,\n allowedLogins: options.githubOAuth.allowedLogins,\n });\n\n const chatRouter = makeChatRouter({ storage, defaultRepo: options.defaultRepo });\n router.use(requireAuth, chatRouter);\n\n const agentRouter = makeAgentRouter({\n cursorApiKey: options.cursorApiKey,\n defaultRepo: options.defaultRepo,\n envName: options.envName,\n skipReviewerRequest: options.skipReviewerRequest,\n storage,\n });\n router.use(requireAuth, agentRouter);\n\n const githubRouter = makeGitHubRouter();\n router.use(requireAuth, githubRouter);\n\n app.use(basePath, router);\n console.log(`[coding-tab] mounted at ${basePath}`);\n return router;\n}\n\nexport type { GitHubOAuthOptions } from \"./authRoutes.js\";\nexport type { ChatStorage } from \"./storage.js\";\nexport { FileChatStorage, createDefaultStorage } from \"./storage.js\";\nexport type {\n ChatMode,\n ChatListItem,\n CreateChatRequest,\n CreateChatResponse,\n ExecuteRequest,\n FullChat,\n MeResponse,\n MergeRequest,\n MergeResponse,\n ModelChoice,\n ModelOption,\n PatchChatRequest,\n PrInfo,\n SendMessageRequest,\n StartAgentRequest,\n StoredChat,\n StoredTurn,\n StreamEvent,\n TimelineEvent,\n TurnRole,\n TurnStatus,\n} from \"../shared/types.js\";\n","// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","import { Router, type Request, type Response } from \"express\";\nimport { randomBytes } from \"node:crypto\";\nimport { getSession } from \"./authMiddleware.js\";\n\nexport interface GitHubOAuthOptions {\n clientId: string;\n clientSecret: string;\n callbackUrl: string;\n allowedLogins: string[];\n scopes?: string[];\n}\n\nexport interface AuthRoutesOptions {\n oauth: GitHubOAuthOptions;\n sessionPassword: string;\n secure: boolean;\n basePath: string;\n defaultRepoUrl?: string;\n defaultRepoRef?: string;\n}\n\nconst OAUTH_STATE_TTL_MS = 10 * 60 * 1000;\n\nexport function makeAuthRouter(opts: AuthRoutesOptions): Router {\n const router = Router();\n const allowed = new Set(opts.oauth.allowedLogins.map((l) => l.toLowerCase()));\n const scopes = opts.oauth.scopes ?? [\"repo\", \"read:user\"];\n\n router.get(\"/auth/login\", async (req: Request, res: Response) => {\n const session = await getSession(req, res, opts);\n const state = randomBytes(24).toString(\"hex\");\n session.oauthState = state;\n session.oauthStateCreatedAt = Date.now();\n await session.save();\n\n const url = new URL(\"https://github.com/login/oauth/authorize\");\n url.searchParams.set(\"client_id\", opts.oauth.clientId);\n url.searchParams.set(\"redirect_uri\", opts.oauth.callbackUrl);\n url.searchParams.set(\"scope\", scopes.join(\" \"));\n url.searchParams.set(\"state\", state);\n url.searchParams.set(\"allow_signup\", \"false\");\n res.redirect(302, url.toString());\n });\n\n router.get(\"/auth/callback\", async (req: Request, res: Response) => {\n const session = await getSession(req, res, opts);\n const code = typeof req.query.code === \"string\" ? req.query.code : null;\n const state = typeof req.query.state === \"string\" ? req.query.state : null;\n\n const expectedState = session.oauthState;\n const stateAge = session.oauthStateCreatedAt ? Date.now() - session.oauthStateCreatedAt : Number.POSITIVE_INFINITY;\n session.oauthState = undefined;\n session.oauthStateCreatedAt = undefined;\n\n if (!code || !state || !expectedState || state !== expectedState || stateAge > OAUTH_STATE_TTL_MS) {\n await session.save();\n res.status(400).send(\"OAuth state mismatch or expired. Try signing in again.\");\n return;\n }\n\n try {\n const tokenResp = await fetch(\"https://github.com/login/oauth/access_token\", {\n method: \"POST\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n client_id: opts.oauth.clientId,\n client_secret: opts.oauth.clientSecret,\n code,\n redirect_uri: opts.oauth.callbackUrl,\n }),\n });\n const tokenJson = (await tokenResp.json()) as {\n access_token?: string;\n error?: string;\n error_description?: string;\n scope?: string;\n token_type?: string;\n };\n if (!tokenJson.access_token) {\n await session.save();\n res.status(401).send(`GitHub token exchange failed: ${tokenJson.error_description ?? tokenJson.error ?? \"unknown\"}`);\n return;\n }\n\n const userResp = await fetch(\"https://api.github.com/user\", {\n headers: {\n Authorization: `Bearer ${tokenJson.access_token}`,\n Accept: \"application/vnd.github+json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n },\n });\n if (!userResp.ok) {\n await session.save();\n res.status(401).send(`GitHub user lookup failed: ${userResp.status}`);\n return;\n }\n const user = (await userResp.json()) as { login: string; avatar_url?: string };\n\n if (allowed.size > 0 && !allowed.has(user.login.toLowerCase())) {\n await session.save();\n res.status(403).send(`@${user.login} is not on the allowlist for this app.`);\n return;\n }\n\n session.githubLogin = user.login;\n session.avatarUrl = user.avatar_url;\n session.accessToken = tokenJson.access_token;\n await session.save();\n\n res.redirect(302, opts.basePath + \"/\");\n } catch (err) {\n console.error(\"[coding-tab] OAuth callback failed\", err);\n res.status(500).send(\"OAuth callback failed. Check server logs.\");\n }\n });\n\n router.post(\"/auth/logout\", async (req: Request, res: Response) => {\n const session = await getSession(req, res, opts);\n session.destroy();\n res.json({ ok: true });\n });\n\n router.get(\"/auth/me\", async (req: Request, res: Response) => {\n const session = await getSession(req, res, opts);\n if (!session.githubLogin || !session.accessToken) {\n res.status(401).json({ error: \"unauthenticated\" });\n return;\n }\n if (allowed.size > 0 && !allowed.has(session.githubLogin.toLowerCase())) {\n res.status(403).json({ error: \"not_authorized\" });\n return;\n }\n res.json({\n githubLogin: session.githubLogin,\n avatarUrl: session.avatarUrl,\n defaultRepoUrl: opts.defaultRepoUrl,\n defaultRepoRef: opts.defaultRepoRef,\n });\n });\n\n return router;\n}\n","import type { NextFunction, Request, Response } from \"express\";\nimport { getIronSession } from \"iron-session\";\nimport { buildSessionOptions, SESSION_COOKIE_NAME, type CodingTabSession } from \"./session.js\";\n\nexport interface AuthenticatedRequest extends Request {\n user: {\n githubLogin: string;\n accessToken: string;\n avatarUrl?: string;\n };\n}\n\nexport interface AuthMiddlewareOptions {\n sessionPassword: string;\n secure: boolean;\n allowedLogins: string[];\n}\n\nexport function getSession(\n req: Request,\n res: Response,\n opts: { sessionPassword: string; secure: boolean },\n) {\n return getIronSession<CodingTabSession>(req, res, buildSessionOptions(opts.sessionPassword, opts.secure));\n}\n\nexport function makeRequireAuth(opts: AuthMiddlewareOptions) {\n const allowed = new Set(opts.allowedLogins.map((l) => l.toLowerCase()));\n return async function requireAuth(req: Request, res: Response, next: NextFunction) {\n try {\n const session = await getSession(req, res, opts);\n if (!session.githubLogin || !session.accessToken) {\n res.status(401).json({ error: \"unauthenticated\" });\n return;\n }\n if (allowed.size > 0 && !allowed.has(session.githubLogin.toLowerCase())) {\n res.status(403).json({ error: \"not_authorized\" });\n return;\n }\n (req as AuthenticatedRequest).user = {\n githubLogin: session.githubLogin,\n accessToken: session.accessToken,\n avatarUrl: session.avatarUrl,\n };\n next();\n } catch (err) {\n console.error(`[${SESSION_COOKIE_NAME}] requireAuth failed`, err);\n res.status(500).json({ error: \"session_error\" });\n }\n };\n}\n","import type { SessionOptions } from \"iron-session\";\n\nexport interface CodingTabSession {\n githubLogin?: string;\n avatarUrl?: string;\n accessToken?: string;\n oauthState?: string;\n oauthStateCreatedAt?: number;\n}\n\nexport const SESSION_COOKIE_NAME = \"coding_tab_session\";\n\nexport function buildSessionOptions(password: string, secure: boolean): SessionOptions {\n return {\n password,\n cookieName: SESSION_COOKIE_NAME,\n cookieOptions: {\n httpOnly: true,\n secure,\n sameSite: \"lax\",\n path: \"/\",\n maxAge: 60 * 60 * 24 * 30,\n },\n };\n}\n","import { Router, type Request, type Response } from \"express\";\nimport {\n Agent,\n CursorAgentError,\n type Run,\n type SDKAgent,\n type SDKMessage,\n} from \"@cursor/sdk\";\nimport { randomUUID } from \"node:crypto\";\nimport type { AuthenticatedRequest } from \"./authMiddleware.js\";\nimport { listAvailableModels, resolveModel } from \"./models.js\";\nimport {\n disposeSessionsForChat,\n getLiveSession,\n registerLiveSession,\n} from \"./sessions.js\";\nimport { sanitizeEvent, type ChatStorage } from \"./storage.js\";\nimport type {\n ChatMode,\n ModelChoice,\n PrInfo,\n StoredChat,\n StoredTurn,\n StreamEvent,\n TimelineEvent,\n TurnStatus,\n} from \"../shared/types.js\";\n\nconst PLAN_INSTRUCTION = `You are operating in PLAN MODE.\n\nProduce a clear, concise markdown plan for the user's request. Do NOT modify any files. Do NOT call any file-editing or shell tools that mutate state. Read-only exploration tools (Read, Grep, Glob, semantic search) are encouraged.\n\nStructure the plan with:\n- Goal (1 line)\n- Approach (3-7 bullet points)\n- Files that will change (bulleted, with absolute or repo-relative paths)\n- Risks / things to verify after implementation\n\nWhen you are done, end your message with a single line: PLAN READY`;\n\nconst EXECUTE_INSTRUCTION = `You are now in AGENT MODE. Implement the plan you produced above. Make all required file changes, run any necessary commands, and prepare the changes for a pull request. When complete, summarize what changed in 3-5 bullets.`;\n\nexport interface AgentRoutesOptions {\n cursorApiKey: string;\n defaultRepo: { url: string; ref?: string };\n envName?: string;\n skipReviewerRequest?: boolean;\n storage: ChatStorage;\n}\n\nfunction sse(res: Response, event: StreamEvent) {\n res.write(`data: ${JSON.stringify(event)}\\n\\n`);\n}\n\nfunction parsePrUrl(url: string | undefined): PrInfo | undefined {\n if (!url) return undefined;\n const match = url.match(/github\\.com\\/([^/]+)\\/([^/]+)\\/pull\\/(\\d+)/);\n if (!match) return undefined;\n return { url, owner: match[1]!, repo: match[2]!, number: Number(match[3]!) };\n}\n\nfunction modeInstructionPrefix(mode: ChatMode, prompt: string): string {\n return mode === \"plan\" ? `${PLAN_INSTRUCTION}\\n\\n---\\n\\nUser request:\\n${prompt}` : prompt;\n}\n\nfunction setupSseHeaders(res: Response) {\n res.set({\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache, no-transform\",\n Connection: \"keep-alive\",\n \"X-Accel-Buffering\": \"no\",\n });\n res.flushHeaders?.();\n}\n\nfunction deriveTitle(prompt: string): string {\n const trimmed = prompt.trim().replace(/\\s+/g, \" \");\n if (trimmed.length <= 60) return trimmed || \"New chat\";\n return `${trimmed.slice(0, 57)}…`;\n}\n\n/**\n * Builds an in-memory mirror of the assistant turn's `events[]` while the\n * SSE stream runs, so we can persist the full timeline at the end while also\n * folding deltas into the same `TimelineEvent` (so `text` chunks within one\n * model \"block\" stay together but a new block starts a new paragraph).\n */\nclass TurnBuffer {\n events: TimelineEvent[] = [];\n private lastWasText = false;\n\n pushText(text: string): TimelineEvent {\n if (this.lastWasText) {\n const last = this.events[this.events.length - 1]!;\n if (last.kind === \"text\") {\n last.text += text;\n return last;\n }\n }\n const evt: TimelineEvent = { kind: \"text\", id: randomUUID(), text };\n this.events.push(evt);\n this.lastWasText = true;\n return evt;\n }\n\n /**\n * Mark the next text event as starting a new block (called when the SDK\n * emits a non-streaming-delta boundary, e.g. a tool call or a new assistant\n * message). The next `pushText` will start a fresh paragraph.\n */\n endTextBlock(): void {\n this.lastWasText = false;\n }\n\n upsertTool(args: {\n callId: string;\n name: string;\n status: \"running\" | \"completed\" | \"error\";\n args?: unknown;\n result?: unknown;\n }): TimelineEvent {\n this.endTextBlock();\n const existing = this.events.find(\n (e): e is Extract<TimelineEvent, { kind: \"tool\" }> =>\n e.kind === \"tool\" && e.callId === args.callId,\n );\n if (existing) {\n existing.status = args.status;\n if (args.args !== undefined) existing.args = args.args;\n if (args.result !== undefined) existing.result = args.result;\n return existing;\n }\n const evt: TimelineEvent = {\n kind: \"tool\",\n id: randomUUID(),\n callId: args.callId,\n name: args.name,\n status: args.status,\n args: args.args,\n result: args.result,\n };\n this.events.push(evt);\n return evt;\n }\n\n snapshot(): TimelineEvent[] {\n return this.events.map(sanitizeEvent);\n }\n}\n\ninterface RunStreamContext {\n storage: ChatStorage;\n chat: StoredChat;\n turn: StoredTurn;\n buffer: TurnBuffer;\n}\n\nasync function streamRun(res: Response, run: Run, ctx: RunStreamContext): Promise<void> {\n const flushDebounceMs = 750;\n let flushPending = false;\n let lastFlush = 0;\n\n const persist = async (status?: TurnStatus, pr?: PrInfo) => {\n try {\n await ctx.storage.patchTurn(ctx.chat.id, ctx.turn.id, {\n events: ctx.buffer.snapshot(),\n ...(status ? { status } : {}),\n ...(pr ? { pr } : {}),\n });\n lastFlush = Date.now();\n } catch (err) {\n console.error(\"[coding-tab] patchTurn failed\", err);\n }\n };\n\n const scheduleFlush = () => {\n if (flushPending) return;\n flushPending = true;\n setTimeout(async () => {\n flushPending = false;\n const elapsed = Date.now() - lastFlush;\n if (elapsed < flushDebounceMs) return;\n await persist();\n }, flushDebounceMs).unref?.();\n };\n\n let finalStatus: TurnStatus = \"finished\";\n let finalPr: PrInfo | undefined;\n\n try {\n for await (const message of run.stream() as AsyncGenerator<SDKMessage, void>) {\n if (res.writableEnded) break;\n if (message.type === \"assistant\") {\n ctx.buffer.endTextBlock();\n for (const block of message.message.content) {\n if (block.type === \"text\" && block.text) {\n ctx.buffer.pushText(block.text);\n ctx.buffer.endTextBlock();\n sse(res, { kind: \"text\", text: block.text });\n } else if (block.type === \"tool_use\") {\n ctx.buffer.upsertTool({\n callId: block.id,\n name: block.name,\n status: \"running\",\n args: block.input,\n });\n sse(res, {\n kind: \"tool\",\n name: block.name,\n status: \"running\",\n callId: block.id,\n args: block.input,\n });\n }\n }\n } else if (message.type === \"thinking\") {\n sse(res, { kind: \"thinking\", text: message.text });\n } else if (message.type === \"tool_call\") {\n ctx.buffer.upsertTool({\n callId: message.call_id,\n name: message.name,\n status: message.status,\n args: message.args,\n result: message.result,\n });\n sse(res, {\n kind: \"tool\",\n name: message.name,\n status: message.status,\n callId: message.call_id,\n args: message.args,\n result: message.result,\n });\n } else if (message.type === \"status\") {\n sse(res, { kind: \"status\", status: message.status, message: message.message });\n }\n scheduleFlush();\n }\n const result = await run.wait();\n const prUrl = result.git?.branches?.find((b) => b.prUrl)?.prUrl;\n finalPr = parsePrUrl(prUrl);\n finalStatus = (result.status as TurnStatus) ?? \"finished\";\n sse(res, {\n kind: \"result\",\n status: result.status,\n pr: finalPr,\n durationMs: result.durationMs,\n });\n } catch (err) {\n finalStatus = \"error\";\n const message = err instanceof Error ? err.message : String(err);\n const retryable = err instanceof CursorAgentError ? Boolean((err as { isRetryable?: boolean }).isRetryable) : false;\n sse(res, { kind: \"error\", message, retryable });\n } finally {\n await persist(finalStatus, finalPr);\n if (!res.writableEnded) res.end();\n }\n}\n\nasync function ensureAgentForChat(\n storage: ChatStorage,\n chat: StoredChat,\n user: { githubLogin: string; accessToken: string },\n opts: AgentRoutesOptions,\n modelChoice: ModelChoice,\n): Promise<{ agent: SDKAgent; agentId: string; resumed: boolean }> {\n const existing = getLiveSession(chat.id);\n if (existing && existing.githubLogin.toLowerCase() === user.githubLogin.toLowerCase()) {\n return { agent: existing.agent, agentId: existing.agentId, resumed: true };\n }\n\n const resolved = await resolveModel(opts.cursorApiKey, modelChoice);\n const cloud = {\n repos: [\n {\n url: chat.repoUrl,\n startingRef: chat.startingRef,\n },\n ],\n autoCreatePR: chat.mode === \"agent\",\n skipReviewerRequest: opts.skipReviewerRequest ?? true,\n envVars: { GITHUB_TOKEN: user.accessToken },\n ...(opts.envName ? { env: { type: \"cloud\" as const, name: opts.envName } } : {}),\n };\n\n if (chat.agentId) {\n try {\n const resumed = await Agent.resume(chat.agentId, {\n apiKey: opts.cursorApiKey,\n model: { id: resolved.cursorModelId },\n cloud,\n });\n registerLiveSession({\n chatId: chat.id,\n githubLogin: user.githubLogin,\n agent: resumed,\n agentId: resumed.agentId,\n });\n if (resumed.agentId !== chat.agentId) {\n await storage.patchChat(chat.id, user.githubLogin, { agentId: resumed.agentId });\n }\n return { agent: resumed, agentId: resumed.agentId, resumed: true };\n } catch (err) {\n console.warn(\n `[coding-tab] resume failed for chat=${chat.id} agentId=${chat.agentId}; creating fresh agent`,\n err instanceof Error ? err.message : err,\n );\n }\n }\n\n const fresh = await Agent.create({\n apiKey: opts.cursorApiKey,\n model: { id: resolved.cursorModelId },\n cloud,\n });\n registerLiveSession({\n chatId: chat.id,\n githubLogin: user.githubLogin,\n agent: fresh,\n agentId: fresh.agentId,\n });\n await storage.patchChat(chat.id, user.githubLogin, { agentId: fresh.agentId });\n return { agent: fresh, agentId: fresh.agentId, resumed: false };\n}\n\nasync function nextSeq(storage: ChatStorage, chatId: string, login: string): Promise<number> {\n const full = await storage.loadChat(chatId, login);\n if (!full) throw Object.assign(new Error(\"chat_not_found\"), { status: 404 });\n const max = full.turns.reduce((acc, t) => Math.max(acc, t.seq), -1);\n return max + 1;\n}\n\nexport function makeAgentRouter(opts: AgentRoutesOptions): Router {\n const router = Router();\n\n router.get(\"/models\", async (_req, res) => {\n try {\n const models = await listAvailableModels(opts.cursorApiKey);\n res.json({ models });\n } catch (err) {\n console.error(\"[coding-tab] /models failed\", err);\n res.status(500).json({ error: err instanceof Error ? err.message : \"models_failed\" });\n }\n });\n\n // Both /agent/start and /agent/send do the same thing now (require chatId,\n // resume or create the agent). /start is kept as an alias for compatibility.\n const handleSend = async (req: Request, res: Response, isExecute: boolean) => {\n const user = (req as AuthenticatedRequest).user;\n const body = (req.body ?? {}) as {\n chatId?: string;\n prompt?: string;\n mode?: ChatMode;\n };\n if (!body.chatId) {\n res.status(400).json({ error: \"missing_chatId\" });\n return;\n }\n if (!isExecute && (!body.prompt || (body.mode !== \"plan\" && body.mode !== \"agent\"))) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n\n const chat = await opts.storage.loadChat(body.chatId, user.githubLogin);\n if (!chat) {\n res.status(404).json({ error: \"chat_not_found\" });\n return;\n }\n\n setupSseHeaders(res);\n\n let assistantTurn: StoredTurn | null = null;\n try {\n // If the user changed mode in the composer, persist it on the chat row.\n if (!isExecute && body.mode && body.mode !== chat.mode) {\n chat.mode = body.mode;\n await opts.storage.patchChat(chat.id, user.githubLogin, { mode: body.mode });\n }\n\n // Persist the user turn (skip for execute since the user didn't type).\n let seq = await nextSeq(opts.storage, chat.id, user.githubLogin);\n if (!isExecute) {\n const userTurn: StoredTurn = {\n id: randomUUID(),\n chatId: chat.id,\n seq: seq++,\n role: \"user\",\n isPlan: chat.mode === \"plan\",\n status: \"finished\",\n events: [{ kind: \"text\", id: randomUUID(), text: body.prompt! }],\n prompt: body.prompt!,\n createdAt: Date.now(),\n };\n await opts.storage.appendTurn(userTurn);\n // First user turn becomes the chat title if still default.\n if (chat.title === \"New chat\") {\n const title = deriveTitle(body.prompt!);\n chat.title = title;\n await opts.storage.patchChat(chat.id, user.githubLogin, { title });\n }\n }\n\n assistantTurn = {\n id: randomUUID(),\n chatId: chat.id,\n seq,\n role: \"assistant\",\n isPlan: !isExecute && chat.mode === \"plan\",\n status: \"running\",\n events: [],\n createdAt: Date.now(),\n };\n await opts.storage.appendTurn(assistantTurn);\n\n const { agent } = await ensureAgentForChat(opts.storage, chat, user, opts, chat.model);\n const sendText = isExecute\n ? EXECUTE_INSTRUCTION\n : modeInstructionPrefix(body.mode!, body.prompt!);\n const run = await agent.send(sendText);\n sse(res, {\n kind: \"ready\",\n chatId: chat.id,\n turnId: assistantTurn.id,\n agentId: agent.agentId,\n runId: run.id,\n });\n console.log(\n `[coding-tab] ${isExecute ? \"execute\" : \"send\"} agent=${agent.agentId} run=${run.id} chat=${chat.id} login=${user.githubLogin}`,\n );\n\n const buffer = new TurnBuffer();\n await streamRun(res, run, {\n storage: opts.storage,\n chat,\n turn: assistantTurn,\n buffer,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const retryable = err instanceof CursorAgentError ? Boolean((err as { isRetryable?: boolean }).isRetryable) : false;\n console.error(`[coding-tab] /agent/${isExecute ? \"execute\" : \"send\"} failed`, err);\n sse(res, { kind: \"error\", message, retryable });\n if (assistantTurn) {\n await opts.storage\n .patchTurn(assistantTurn.chatId, assistantTurn.id, {\n status: \"error\",\n events: [\n ...assistantTurn.events,\n { kind: \"text\", id: randomUUID(), text: `[error] ${message}` },\n ],\n })\n .catch(() => {});\n }\n if (!res.writableEnded) res.end();\n }\n\n req.on(\"close\", () => {\n if (!res.writableEnded) res.end();\n });\n };\n\n router.post(\"/agent/start\", (req, res) => handleSend(req, res, false));\n router.post(\"/agent/send\", (req, res) => handleSend(req, res, false));\n router.post(\"/agent/execute\", (req, res) => handleSend(req, res, true));\n\n router.post(\"/agent/cancel\", async (req: Request, res: Response) => {\n const { chatId, runId } = (req.body ?? {}) as { chatId?: string; runId?: string };\n if (!chatId || !runId) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n const live = getLiveSession(chatId);\n if (!live) {\n res.status(404).json({ error: \"session_not_found\" });\n return;\n }\n try {\n await Agent.cancelRun(runId, {\n runtime: \"cloud\",\n agentId: live.agentId,\n apiKey: opts.cursorApiKey,\n });\n res.json({ ok: true });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(\"[coding-tab] /agent/cancel failed\", err);\n res.status(500).json({ error: message });\n }\n });\n\n router.post(\"/agent/dispose\", async (req: Request, res: Response) => {\n const { chatId } = (req.body ?? {}) as { chatId?: string };\n if (!chatId) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n await disposeSessionsForChat(chatId);\n res.json({ ok: true });\n });\n\n return router;\n}\n","import { Cursor, type SDKModel } from \"@cursor/sdk\";\nimport type { ModelChoice, ModelOption } from \"../shared/types.js\";\n\nconst SONNET_PATTERN = /sonnet/i;\nconst OPUS_PATTERN = /opus/i;\n\ninterface ModelMapEntry {\n cursorModelId: string;\n displayName: string;\n}\n\nlet cache: { fetchedAt: number; models: SDKModel[] } | null = null;\nconst CACHE_TTL_MS = 5 * 60 * 1000;\n\nasync function getModelList(apiKey: string): Promise<SDKModel[]> {\n if (cache && Date.now() - cache.fetchedAt < CACHE_TTL_MS) return cache.models;\n const models = await Cursor.models.list({ apiKey });\n cache = { fetchedAt: Date.now(), models };\n return models;\n}\n\nfunction pickBest(models: SDKModel[], pattern: RegExp): SDKModel | undefined {\n const matches = models.filter((m) => pattern.test(m.id) || pattern.test(m.displayName) || (m.aliases ?? []).some((a) => pattern.test(a)));\n if (matches.length === 0) return undefined;\n matches.sort((a, b) => b.id.localeCompare(a.id));\n return matches[0];\n}\n\nexport async function listAvailableModels(apiKey: string): Promise<ModelOption[]> {\n const models = await getModelList(apiKey);\n const out: ModelOption[] = [];\n const sonnet = pickBest(models, SONNET_PATTERN);\n if (sonnet) out.push({ choice: \"sonnet\", cursorModelId: sonnet.id, displayName: sonnet.displayName ?? sonnet.id });\n const opus = pickBest(models, OPUS_PATTERN);\n if (opus) out.push({ choice: \"opus\", cursorModelId: opus.id, displayName: opus.displayName ?? opus.id });\n return out;\n}\n\nexport async function resolveModel(apiKey: string, choice: ModelChoice): Promise<ModelMapEntry> {\n const models = await getModelList(apiKey);\n const pattern = choice === \"sonnet\" ? SONNET_PATTERN : OPUS_PATTERN;\n const picked = pickBest(models, pattern);\n if (!picked) {\n const available = models.map((m) => `${m.displayName} (${m.id})`).join(\", \");\n throw new Error(`No Cursor-routed model matches \"${choice}\". Available: ${available}`);\n }\n return { cursorModelId: picked.id, displayName: picked.displayName ?? picked.id };\n}\n","import type { SDKAgent } from \"@cursor/sdk\";\n\n/**\n * In-memory cache of live `Agent` handles, keyed by chatId. Persistent\n * chat metadata (including the cloud `agentId`) lives in `ChatStorage`; this\n * map exists only so the same `Agent.create()` instance can be reused across\n * multiple turns within a single Node process to avoid the cold-start\n * resume cost on every send.\n */\nexport interface LiveSession {\n chatId: string;\n githubLogin: string;\n agent: SDKAgent;\n agentId: string;\n createdAt: number;\n lastUsedAt: number;\n}\n\nconst SESSION_IDLE_TTL_MS = 30 * 60 * 1000;\nconst sessions = new Map<string, LiveSession>();\n\nlet sweeperStarted = false;\nfunction startSweeper() {\n if (sweeperStarted) return;\n sweeperStarted = true;\n setInterval(() => {\n const now = Date.now();\n for (const [id, s] of sessions) {\n if (now - s.lastUsedAt > SESSION_IDLE_TTL_MS) {\n disposeSessionsForChat(id).catch((e) =>\n console.error(\"[coding-tab] session sweep dispose failed\", e),\n );\n }\n }\n }, 5 * 60 * 1000).unref?.();\n}\n\nexport function registerLiveSession(opts: {\n chatId: string;\n githubLogin: string;\n agent: SDKAgent;\n agentId: string;\n}): LiveSession {\n startSweeper();\n // If a session already exists for this chat, dispose it first.\n const prev = sessions.get(opts.chatId);\n if (prev) {\n sessions.delete(opts.chatId);\n prev.agent[Symbol.asyncDispose]().catch(() => {});\n }\n const session: LiveSession = {\n chatId: opts.chatId,\n githubLogin: opts.githubLogin,\n agent: opts.agent,\n agentId: opts.agentId,\n createdAt: Date.now(),\n lastUsedAt: Date.now(),\n };\n sessions.set(opts.chatId, session);\n return session;\n}\n\nexport function getLiveSession(chatId: string): LiveSession | undefined {\n const s = sessions.get(chatId);\n if (s) s.lastUsedAt = Date.now();\n return s;\n}\n\nexport async function disposeSessionsForChat(chatId: string): Promise<void> {\n const s = sessions.get(chatId);\n if (!s) return;\n sessions.delete(chatId);\n try {\n await s.agent[Symbol.asyncDispose]();\n } catch (err) {\n console.error(\"[coding-tab] dispose failed\", err);\n }\n}\n","import { mkdir, readFile, readdir, rename, rm, writeFile } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport type {\n ChatListItem,\n FullChat,\n StoredChat,\n StoredTurn,\n TimelineEvent,\n} from \"../shared/types.js\";\n\n/**\n * Pluggable persistence layer for chats + turns. The default\n * `FileChatStorage` writes JSON files under a data directory; host apps can\n * swap in their own implementation (e.g. Postgres / Supabase) by passing\n * `storage` to `mountCodingTab`.\n */\nexport interface ChatStorage {\n listChats(login: string): Promise<ChatListItem[]>;\n loadChat(id: string, login: string): Promise<FullChat | null>;\n createChat(chat: StoredChat): Promise<void>;\n patchChat(\n id: string,\n login: string,\n patch: Partial<Omit<StoredChat, \"id\" | \"githubLogin\" | \"createdAt\">>,\n ): Promise<StoredChat | null>;\n appendTurn(turn: StoredTurn): Promise<void>;\n patchTurn(\n chatId: string,\n turnId: string,\n patch: Partial<Omit<StoredTurn, \"id\" | \"chatId\" | \"createdAt\">>,\n ): Promise<void>;\n deleteChat(id: string, login: string): Promise<boolean>;\n}\n\n// ───────── helpers ─────────\n\nfunction safeId(s: string): string {\n return s.replace(/[^a-zA-Z0-9_.-]/g, \"_\");\n}\n\nasync function ensureDir(p: string): Promise<void> {\n await mkdir(p, { recursive: true });\n}\n\nasync function readJson<T>(path: string): Promise<T | null> {\n try {\n const raw = await readFile(path, \"utf8\");\n return JSON.parse(raw) as T;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n}\n\nasync function writeJsonAtomic(path: string, data: unknown): Promise<void> {\n await ensureDir(dirname(path));\n const tmp = `${path}.${randomUUID()}.tmp`;\n await writeFile(tmp, JSON.stringify(data, null, 2), \"utf8\");\n await rename(tmp, path);\n}\n\n/** Truncate args/result blobs that are too large to comfortably persist. */\nconst MAX_BLOB_BYTES = 32 * 1024;\nfunction truncateBlob(value: unknown): unknown {\n if (value === undefined || value === null) return value;\n try {\n const str = typeof value === \"string\" ? value : JSON.stringify(value);\n if (str.length <= MAX_BLOB_BYTES) return value;\n const head = str.slice(0, MAX_BLOB_BYTES);\n return `${head}\\n\\n…[truncated ${str.length - MAX_BLOB_BYTES} chars]`;\n } catch {\n return \"[unserializable]\";\n }\n}\n\nexport function sanitizeEvent(evt: TimelineEvent): TimelineEvent {\n if (evt.kind === \"tool\") {\n return {\n ...evt,\n args: truncateBlob(evt.args),\n result: truncateBlob(evt.result),\n };\n }\n return evt;\n}\n\n// ───────── default file-backed storage ─────────\n\nexport interface FileChatStorageOptions {\n dataDir: string;\n}\n\ninterface ChatFile {\n chat: StoredChat;\n turns: StoredTurn[];\n}\n\nexport class FileChatStorage implements ChatStorage {\n private readonly dataDir: string;\n /** Per-chat write mutex chain to keep concurrent appends consistent. */\n private readonly chains = new Map<string, Promise<unknown>>();\n\n constructor(opts: FileChatStorageOptions) {\n this.dataDir = resolve(opts.dataDir);\n }\n\n private chatPath(id: string): string {\n return join(this.dataDir, \"chats\", `${safeId(id)}.json`);\n }\n\n private indexPath(login: string): string {\n return join(this.dataDir, \"index\", `${safeId(login.toLowerCase())}.json`);\n }\n\n /** Serialize all reads/writes for a single chat through a chained promise. */\n private withChat<T>(id: string, fn: () => Promise<T>): Promise<T> {\n const prev = this.chains.get(id) ?? Promise.resolve();\n const next = prev.then(fn, fn);\n this.chains.set(\n id,\n next.finally(() => {\n if (this.chains.get(id) === next) this.chains.delete(id);\n }),\n );\n return next;\n }\n\n private async readChat(id: string): Promise<ChatFile | null> {\n return readJson<ChatFile>(this.chatPath(id));\n }\n\n private async writeChat(file: ChatFile): Promise<void> {\n await writeJsonAtomic(this.chatPath(file.chat.id), file);\n }\n\n private async readIndex(login: string): Promise<ChatListItem[]> {\n const list = await readJson<ChatListItem[]>(this.indexPath(login));\n return list ?? [];\n }\n\n private async writeIndex(login: string, items: ChatListItem[]): Promise<void> {\n items.sort((a, b) => b.updatedAt - a.updatedAt);\n await writeJsonAtomic(this.indexPath(login), items);\n }\n\n /**\n * Rebuild the index for a user by scanning every chat file. Used as a\n * self-heal path when the index gets out of sync (e.g. crash mid-write).\n */\n private async rebuildIndex(login: string): Promise<ChatListItem[]> {\n const dir = join(this.dataDir, \"chats\");\n let entries: string[] = [];\n try {\n entries = await readdir(dir);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n const items: ChatListItem[] = [];\n const target = login.toLowerCase();\n for (const name of entries) {\n if (!name.endsWith(\".json\")) continue;\n const file = await readJson<ChatFile>(join(dir, name));\n if (!file?.chat) continue;\n if (file.chat.githubLogin.toLowerCase() !== target) continue;\n items.push(this.toListItem(file.chat));\n }\n await this.writeIndex(login, items);\n return items;\n }\n\n private toListItem(c: StoredChat): ChatListItem {\n return {\n id: c.id,\n title: c.title,\n mode: c.mode,\n model: c.model,\n createdAt: c.createdAt,\n updatedAt: c.updatedAt,\n };\n }\n\n private async upsertIndex(chat: StoredChat): Promise<void> {\n const items = await this.readIndex(chat.githubLogin);\n const idx = items.findIndex((c) => c.id === chat.id);\n const item = this.toListItem(chat);\n if (idx >= 0) items[idx] = item;\n else items.push(item);\n await this.writeIndex(chat.githubLogin, items);\n }\n\n private async removeFromIndex(login: string, chatId: string): Promise<void> {\n const items = await this.readIndex(login);\n const filtered = items.filter((c) => c.id !== chatId);\n if (filtered.length !== items.length) {\n await this.writeIndex(login, filtered);\n }\n }\n\n async listChats(login: string): Promise<ChatListItem[]> {\n const items = await this.readIndex(login);\n if (items.length > 0) return items;\n // Self-heal: index missing or empty but chat files might exist.\n return this.rebuildIndex(login);\n }\n\n async loadChat(id: string, login: string): Promise<FullChat | null> {\n return this.withChat(id, async () => {\n const file = await this.readChat(id);\n if (!file) return null;\n if (file.chat.githubLogin.toLowerCase() !== login.toLowerCase()) {\n return null;\n }\n const turns = [...file.turns].sort((a, b) => a.seq - b.seq);\n return { ...file.chat, turns };\n });\n }\n\n async createChat(chat: StoredChat): Promise<void> {\n await this.withChat(chat.id, async () => {\n await this.writeChat({ chat, turns: [] });\n });\n await this.upsertIndex(chat);\n }\n\n async patchChat(\n id: string,\n login: string,\n patch: Partial<Omit<StoredChat, \"id\" | \"githubLogin\" | \"createdAt\">>,\n ): Promise<StoredChat | null> {\n const updated = await this.withChat(id, async () => {\n const file = await this.readChat(id);\n if (!file) return null;\n if (file.chat.githubLogin.toLowerCase() !== login.toLowerCase()) {\n return null;\n }\n const next: StoredChat = {\n ...file.chat,\n ...patch,\n updatedAt: Date.now(),\n };\n await this.writeChat({ chat: next, turns: file.turns });\n return next;\n });\n if (updated) await this.upsertIndex(updated);\n return updated;\n }\n\n async appendTurn(turn: StoredTurn): Promise<void> {\n const sanitized: StoredTurn = {\n ...turn,\n events: turn.events.map(sanitizeEvent),\n };\n let chat: StoredChat | null = null;\n await this.withChat(turn.chatId, async () => {\n const file = await this.readChat(turn.chatId);\n if (!file) throw new Error(`chat_not_found:${turn.chatId}`);\n file.turns = file.turns.filter((t) => t.id !== sanitized.id);\n file.turns.push(sanitized);\n file.chat = { ...file.chat, updatedAt: Date.now() };\n chat = file.chat;\n await this.writeChat(file);\n });\n if (chat) await this.upsertIndex(chat);\n }\n\n async patchTurn(\n chatId: string,\n turnId: string,\n patch: Partial<Omit<StoredTurn, \"id\" | \"chatId\" | \"createdAt\">>,\n ): Promise<void> {\n let chat: StoredChat | null = null;\n await this.withChat(chatId, async () => {\n const file = await this.readChat(chatId);\n if (!file) return;\n const idx = file.turns.findIndex((t) => t.id === turnId);\n if (idx < 0) return;\n const current = file.turns[idx]!;\n const events = patch.events ? patch.events.map(sanitizeEvent) : current.events;\n file.turns[idx] = { ...current, ...patch, events };\n file.chat = { ...file.chat, updatedAt: Date.now() };\n chat = file.chat;\n await this.writeChat(file);\n });\n if (chat) await this.upsertIndex(chat);\n }\n\n async deleteChat(id: string, login: string): Promise<boolean> {\n const removed = await this.withChat(id, async () => {\n const file = await this.readChat(id);\n if (!file) return false;\n if (file.chat.githubLogin.toLowerCase() !== login.toLowerCase()) {\n return false;\n }\n await rm(this.chatPath(id), { force: true });\n return true;\n });\n if (removed) await this.removeFromIndex(login, id);\n return removed;\n }\n}\n\nexport function createDefaultStorage(dataDir?: string): ChatStorage {\n const dir = dataDir ?? join(process.cwd(), \".coding-tab-data\");\n return new FileChatStorage({ dataDir: dir });\n}\n","import { Router, type Request, type Response } from \"express\";\nimport { randomUUID } from \"node:crypto\";\nimport type { AuthenticatedRequest } from \"./authMiddleware.js\";\nimport { disposeSessionsForChat } from \"./sessions.js\";\nimport type { ChatStorage } from \"./storage.js\";\nimport type {\n ChatMode,\n CreateChatRequest,\n ModelChoice,\n PatchChatRequest,\n StoredChat,\n} from \"../shared/types.js\";\n\nexport interface ChatRoutesOptions {\n storage: ChatStorage;\n defaultRepo: { url: string; ref?: string };\n}\n\nconst VALID_MODES: ChatMode[] = [\"plan\", \"agent\"];\nconst VALID_MODELS: ModelChoice[] = [\"sonnet\", \"opus\"];\n\nexport function makeChatRouter(opts: ChatRoutesOptions): Router {\n const router = Router();\n\n router.get(\"/chats\", async (req: Request, res: Response) => {\n const user = (req as AuthenticatedRequest).user;\n try {\n const items = await opts.storage.listChats(user.githubLogin);\n res.json({ chats: items });\n } catch (err) {\n console.error(\"[coding-tab] /chats list failed\", err);\n res.status(500).json({ error: err instanceof Error ? err.message : \"list_failed\" });\n }\n });\n\n router.post(\"/chats\", async (req: Request, res: Response) => {\n const user = (req as AuthenticatedRequest).user;\n const body = (req.body ?? {}) as CreateChatRequest;\n const mode: ChatMode = VALID_MODES.includes(body.mode as ChatMode) ? (body.mode as ChatMode) : \"plan\";\n const model: ModelChoice = VALID_MODELS.includes(body.model as ModelChoice) ? (body.model as ModelChoice) : \"sonnet\";\n const now = Date.now();\n const chat: StoredChat = {\n id: randomUUID(),\n githubLogin: user.githubLogin,\n title: (body.title ?? \"\").trim() || \"New chat\",\n mode,\n model,\n repoUrl: body.repoUrl?.trim() || opts.defaultRepo.url,\n startingRef: body.startingRef ?? opts.defaultRepo.ref,\n createdAt: now,\n updatedAt: now,\n };\n try {\n await opts.storage.createChat(chat);\n res.status(201).json({ chat });\n } catch (err) {\n console.error(\"[coding-tab] /chats create failed\", err);\n res.status(500).json({ error: err instanceof Error ? err.message : \"create_failed\" });\n }\n });\n\n router.get(\"/chats/:id\", async (req: Request, res: Response) => {\n const user = (req as AuthenticatedRequest).user;\n try {\n const full = await opts.storage.loadChat(req.params.id!, user.githubLogin);\n if (!full) {\n res.status(404).json({ error: \"chat_not_found\" });\n return;\n }\n res.json({ chat: full });\n } catch (err) {\n console.error(\"[coding-tab] /chats/:id load failed\", err);\n res.status(500).json({ error: err instanceof Error ? err.message : \"load_failed\" });\n }\n });\n\n router.patch(\"/chats/:id\", async (req: Request, res: Response) => {\n const user = (req as AuthenticatedRequest).user;\n const body = (req.body ?? {}) as PatchChatRequest;\n const patch: PatchChatRequest = {};\n if (typeof body.title === \"string\") patch.title = body.title.trim() || \"Untitled\";\n if (body.mode && VALID_MODES.includes(body.mode)) patch.mode = body.mode;\n if (body.model && VALID_MODELS.includes(body.model)) patch.model = body.model;\n try {\n const updated = await opts.storage.patchChat(req.params.id!, user.githubLogin, patch);\n if (!updated) {\n res.status(404).json({ error: \"chat_not_found\" });\n return;\n }\n res.json({ chat: updated });\n } catch (err) {\n console.error(\"[coding-tab] /chats/:id patch failed\", err);\n res.status(500).json({ error: err instanceof Error ? err.message : \"patch_failed\" });\n }\n });\n\n router.delete(\"/chats/:id\", async (req: Request, res: Response) => {\n const user = (req as AuthenticatedRequest).user;\n try {\n const removed = await opts.storage.deleteChat(req.params.id!, user.githubLogin);\n if (!removed) {\n res.status(404).json({ error: \"chat_not_found\" });\n return;\n }\n // Best-effort: tear down any in-memory live session for this chat.\n await disposeSessionsForChat(req.params.id!).catch(() => {});\n res.json({ ok: true });\n } catch (err) {\n console.error(\"[coding-tab] /chats/:id delete failed\", err);\n res.status(500).json({ error: err instanceof Error ? err.message : \"delete_failed\" });\n }\n });\n\n return router;\n}\n","import { Router, type Request, type Response } from \"express\";\nimport { Octokit } from \"@octokit/rest\";\nimport type { AuthenticatedRequest } from \"./authMiddleware.js\";\nimport type { MergeRequest, PrInfo } from \"../shared/types.js\";\n\nfunction parsePrUrl(prUrl: string): { owner: string; repo: string; number: number } | null {\n const match = prUrl.match(/github\\.com\\/([^/]+)\\/([^/]+)\\/pull\\/(\\d+)/);\n if (!match) return null;\n return { owner: match[1]!, repo: match[2]!, number: Number(match[3]!) };\n}\n\nexport function makeGitHubRouter(): Router {\n const router = Router();\n\n router.get(\"/pr/status\", async (req: Request, res: Response) => {\n const prUrl = typeof req.query.prUrl === \"string\" ? req.query.prUrl : null;\n if (!prUrl) {\n res.status(400).json({ error: \"missing_prUrl\" });\n return;\n }\n const parsed = parsePrUrl(prUrl);\n if (!parsed) {\n res.status(400).json({ error: \"invalid_prUrl\" });\n return;\n }\n const user = (req as AuthenticatedRequest).user;\n const octokit = new Octokit({ auth: user.accessToken });\n try {\n const pr = await octokit.pulls.get({ owner: parsed.owner, repo: parsed.repo, pull_number: parsed.number });\n const info: PrInfo = {\n url: pr.data.html_url,\n owner: parsed.owner,\n repo: parsed.repo,\n number: parsed.number,\n branch: pr.data.head?.ref,\n state: pr.data.merged ? \"merged\" : (pr.data.state as \"open\" | \"closed\"),\n title: pr.data.title,\n body: pr.data.body ?? undefined,\n };\n res.json({ pr: info, mergeable: pr.data.mergeable, mergeable_state: pr.data.mergeable_state });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n res.status(500).json({ error: message });\n }\n });\n\n router.post(\"/pr/merge\", async (req: Request, res: Response) => {\n const body = (req.body ?? {}) as MergeRequest;\n if (!body.prUrl) {\n res.status(400).json({ error: \"missing_prUrl\" });\n return;\n }\n const parsed = parsePrUrl(body.prUrl);\n if (!parsed) {\n res.status(400).json({ error: \"invalid_prUrl\" });\n return;\n }\n const user = (req as AuthenticatedRequest).user;\n const octokit = new Octokit({ auth: user.accessToken });\n try {\n const pr = await octokit.pulls.get({\n owner: parsed.owner,\n repo: parsed.repo,\n pull_number: parsed.number,\n });\n if (pr.data.draft) {\n await octokit.graphql<{ markPullRequestReadyForReview: { pullRequest: { id: string } } }>(\n `mutation($id: ID!) {\n markPullRequestReadyForReview(input: { pullRequestId: $id }) {\n pullRequest { id }\n }\n }`,\n { id: pr.data.node_id },\n );\n }\n const merge = await octokit.pulls.merge({\n owner: parsed.owner,\n repo: parsed.repo,\n pull_number: parsed.number,\n merge_method: body.mergeMethod ?? \"squash\",\n });\n res.json({ sha: merge.data.sha, merged: merge.data.merged });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const status = (err as { status?: number }).status ?? 500;\n console.error(\"[coding-tab] /pr/merge failed\", err);\n res.status(status >= 400 && status < 600 ? status : 500).json({ error: message });\n }\n });\n\n return router;\n}\n","import { Router, type Request, type Response } from \"express\";\nimport { readFile } from \"node:fs/promises\";\nimport { resolve, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst here = dirname(fileURLToPath(import.meta.url));\n\nconst ASSET_CANDIDATES = [\n resolve(here, \"browser.js\"),\n resolve(here, \"..\", \"dist\", \"browser.js\"),\n resolve(here, \"..\", \"browser.js\"),\n];\nconst STYLE_CANDIDATES = [\n resolve(here, \"style.css\"),\n resolve(here, \"..\", \"dist\", \"style.css\"),\n resolve(here, \"..\", \"style.css\"),\n];\n\nasync function readFirst(paths: string[]): Promise<{ path: string; data: Buffer }> {\n let lastErr: unknown;\n for (const p of paths) {\n try {\n const data = await readFile(p);\n return { path: p, data };\n } catch (err) {\n lastErr = err;\n }\n }\n throw lastErr instanceof Error ? lastErr : new Error(\"asset not found\");\n}\n\nexport function makeAssetRouter(basePath: string): Router {\n const router = Router();\n\n router.get(\"/browser.js\", async (_req, res) => {\n try {\n const { data } = await readFirst(ASSET_CANDIDATES);\n res.set(\"Content-Type\", \"application/javascript; charset=utf-8\");\n res.set(\"Cache-Control\", \"public, max-age=0, must-revalidate\");\n res.send(data);\n } catch (err) {\n res.status(500).send(\"// coding-tab browser bundle missing — did you run `npm run build`?\");\n }\n });\n\n router.get(\"/style.css\", async (_req, res) => {\n try {\n const { data } = await readFirst(STYLE_CANDIDATES);\n res.set(\"Content-Type\", \"text/css; charset=utf-8\");\n res.set(\"Cache-Control\", \"public, max-age=0, must-revalidate\");\n res.send(data);\n } catch {\n res.status(404).send(\"/* style.css missing */\");\n }\n });\n\n router.get(\"/\", async (_req: Request, res: Response) => {\n const html = renderHostHtml(basePath);\n res.set(\"Content-Type\", \"text/html; charset=utf-8\");\n res.send(html);\n });\n\n return router;\n}\n\nfunction renderHostHtml(basePath: string): string {\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, viewport-fit=cover\" />\n <title>Coding Tab</title>\n <link rel=\"stylesheet\" href=\"${basePath}/style.css\" />\n </head>\n <body>\n <div id=\"coding-tab-root\"></div>\n <script src=\"${basePath}/browser.js\"></script>\n <script>\n window.CodingTab.mountCodingTab(\n document.getElementById(\"coding-tab-root\"),\n { apiBase: ${JSON.stringify(basePath)} },\n );\n </script>\n </body>\n</html>`;\n}\n"],"mappings":"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","names":["import_express","import_express","import_sdk","import_node_crypto","import_node_crypto","import_express","import_node_crypto","import_express","parsePrUrl","import_express","import_promises","import_node_path","express"]}
|
|
1
|
+
{"version":3,"sources":["../src/server/index.ts","../node_modules/tsup/assets/cjs_shims.js","../src/server/authRoutes.ts","../src/server/authMiddleware.ts","../src/server/session.ts","../src/server/agentRoutes.ts","../src/server/models.ts","../src/server/sessions.ts","../src/server/storage.ts","../src/server/chatRoutes.ts","../src/server/githubRoutes.ts","../src/server/staticAssets.ts"],"sourcesContent":["import express, { type Express, type Router } from \"express\";\nimport { makeAuthRouter, type GitHubOAuthOptions } from \"./authRoutes.js\";\nimport { makeAgentRouter } from \"./agentRoutes.js\";\nimport { makeChatRouter } from \"./chatRoutes.js\";\nimport { makeGitHubRouter } from \"./githubRoutes.js\";\nimport { makeRequireAuth } from \"./authMiddleware.js\";\nimport { makeAssetRouter } from \"./staticAssets.js\";\nimport { createDefaultStorage, type ChatStorage } from \"./storage.js\";\n\nexport interface MountCodingTabOptions {\n cursorApiKey: string;\n githubOAuth: GitHubOAuthOptions;\n sessionPassword: string;\n defaultRepo: { url: string; ref?: string };\n basePath?: string;\n secure?: boolean;\n envName?: string;\n skipReviewerRequest?: boolean;\n /**\n * Optional pluggable persistence layer. Defaults to a JSON file store\n * rooted at `dataDir` (or `<cwd>/.coding-tab-data`). Override to plug in\n * Postgres, Supabase, Redis, etc. on hosts with ephemeral filesystems.\n */\n storage?: ChatStorage;\n /** Directory used by the default file storage. Ignored when `storage` is set. */\n dataDir?: string;\n}\n\nexport function mountCodingTab(app: Express, options: MountCodingTabOptions): Router {\n const basePath = (options.basePath ?? \"/coding-tab\").replace(/\\/$/, \"\");\n const secure = options.secure ?? process.env.NODE_ENV === \"production\";\n\n if (!options.cursorApiKey) throw new Error(\"[coding-tab] cursorApiKey is required\");\n if (!options.sessionPassword || options.sessionPassword.length < 32) {\n throw new Error(\"[coding-tab] sessionPassword must be at least 32 characters\");\n }\n if (!options.githubOAuth?.clientId || !options.githubOAuth?.clientSecret) {\n throw new Error(\"[coding-tab] githubOAuth.clientId and clientSecret are required\");\n }\n if (!options.githubOAuth.callbackUrl) {\n throw new Error(\"[coding-tab] githubOAuth.callbackUrl is required\");\n }\n if (!options.githubOAuth.allowedLogins || options.githubOAuth.allowedLogins.length === 0) {\n console.warn(\"[coding-tab] WARNING: allowedLogins is empty — anyone with a GitHub account can sign in.\");\n }\n\n const storage = options.storage ?? createDefaultStorage(options.dataDir);\n\n const router = express.Router();\n router.use(express.json({ limit: \"1mb\" }));\n\n const assetRouter = makeAssetRouter(basePath);\n router.use(assetRouter);\n\n const authRouter = makeAuthRouter({\n oauth: options.githubOAuth,\n sessionPassword: options.sessionPassword,\n secure,\n basePath,\n defaultRepoUrl: options.defaultRepo.url,\n defaultRepoRef: options.defaultRepo.ref,\n });\n router.use(authRouter);\n\n const requireAuth = makeRequireAuth({\n sessionPassword: options.sessionPassword,\n secure,\n allowedLogins: options.githubOAuth.allowedLogins,\n });\n\n const chatRouter = makeChatRouter({ storage, defaultRepo: options.defaultRepo });\n router.use(requireAuth, chatRouter);\n\n const agentRouter = makeAgentRouter({\n cursorApiKey: options.cursorApiKey,\n defaultRepo: options.defaultRepo,\n envName: options.envName,\n skipReviewerRequest: options.skipReviewerRequest,\n storage,\n });\n router.use(requireAuth, agentRouter);\n\n const githubRouter = makeGitHubRouter();\n router.use(requireAuth, githubRouter);\n\n app.use(basePath, router);\n console.log(`[coding-tab] mounted at ${basePath}`);\n return router;\n}\n\nexport type { GitHubOAuthOptions } from \"./authRoutes.js\";\nexport type { ChatStorage } from \"./storage.js\";\nexport { FileChatStorage, createDefaultStorage } from \"./storage.js\";\nexport type {\n ChatMode,\n ChatListItem,\n CreateChatRequest,\n CreateChatResponse,\n ExecuteRequest,\n FullChat,\n MeResponse,\n MergeRequest,\n MergeResponse,\n ModelChoice,\n ModelOption,\n PatchChatRequest,\n PrInfo,\n SendMessageRequest,\n StartAgentRequest,\n StoredChat,\n StoredTurn,\n StreamEvent,\n TimelineEvent,\n TurnRole,\n TurnStatus,\n} from \"../shared/types.js\";\n","// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","import { Router, type Request, type Response } from \"express\";\nimport { randomBytes } from \"node:crypto\";\nimport { getSession } from \"./authMiddleware.js\";\n\nexport interface GitHubOAuthOptions {\n clientId: string;\n clientSecret: string;\n callbackUrl: string;\n allowedLogins: string[];\n scopes?: string[];\n}\n\nexport interface AuthRoutesOptions {\n oauth: GitHubOAuthOptions;\n sessionPassword: string;\n secure: boolean;\n basePath: string;\n defaultRepoUrl?: string;\n defaultRepoRef?: string;\n}\n\nconst OAUTH_STATE_TTL_MS = 10 * 60 * 1000;\n\nexport function makeAuthRouter(opts: AuthRoutesOptions): Router {\n const router = Router();\n const allowed = new Set(opts.oauth.allowedLogins.map((l) => l.toLowerCase()));\n const scopes = opts.oauth.scopes ?? [\"repo\", \"read:user\"];\n\n router.get(\"/auth/login\", async (req: Request, res: Response) => {\n const session = await getSession(req, res, opts);\n const state = randomBytes(24).toString(\"hex\");\n session.oauthState = state;\n session.oauthStateCreatedAt = Date.now();\n await session.save();\n\n const url = new URL(\"https://github.com/login/oauth/authorize\");\n url.searchParams.set(\"client_id\", opts.oauth.clientId);\n url.searchParams.set(\"redirect_uri\", opts.oauth.callbackUrl);\n url.searchParams.set(\"scope\", scopes.join(\" \"));\n url.searchParams.set(\"state\", state);\n url.searchParams.set(\"allow_signup\", \"false\");\n res.redirect(302, url.toString());\n });\n\n router.get(\"/auth/callback\", async (req: Request, res: Response) => {\n const session = await getSession(req, res, opts);\n const code = typeof req.query.code === \"string\" ? req.query.code : null;\n const state = typeof req.query.state === \"string\" ? req.query.state : null;\n\n const expectedState = session.oauthState;\n const stateAge = session.oauthStateCreatedAt ? Date.now() - session.oauthStateCreatedAt : Number.POSITIVE_INFINITY;\n session.oauthState = undefined;\n session.oauthStateCreatedAt = undefined;\n\n if (!code || !state || !expectedState || state !== expectedState || stateAge > OAUTH_STATE_TTL_MS) {\n await session.save();\n res.status(400).send(\"OAuth state mismatch or expired. Try signing in again.\");\n return;\n }\n\n try {\n const tokenResp = await fetch(\"https://github.com/login/oauth/access_token\", {\n method: \"POST\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n client_id: opts.oauth.clientId,\n client_secret: opts.oauth.clientSecret,\n code,\n redirect_uri: opts.oauth.callbackUrl,\n }),\n });\n const tokenJson = (await tokenResp.json()) as {\n access_token?: string;\n error?: string;\n error_description?: string;\n scope?: string;\n token_type?: string;\n };\n if (!tokenJson.access_token) {\n await session.save();\n res.status(401).send(`GitHub token exchange failed: ${tokenJson.error_description ?? tokenJson.error ?? \"unknown\"}`);\n return;\n }\n\n const userResp = await fetch(\"https://api.github.com/user\", {\n headers: {\n Authorization: `Bearer ${tokenJson.access_token}`,\n Accept: \"application/vnd.github+json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n },\n });\n if (!userResp.ok) {\n await session.save();\n res.status(401).send(`GitHub user lookup failed: ${userResp.status}`);\n return;\n }\n const user = (await userResp.json()) as { login: string; avatar_url?: string };\n\n if (allowed.size > 0 && !allowed.has(user.login.toLowerCase())) {\n await session.save();\n res.status(403).send(`@${user.login} is not on the allowlist for this app.`);\n return;\n }\n\n session.githubLogin = user.login;\n session.avatarUrl = user.avatar_url;\n session.accessToken = tokenJson.access_token;\n await session.save();\n\n res.redirect(302, opts.basePath + \"/\");\n } catch (err) {\n console.error(\"[coding-tab] OAuth callback failed\", err);\n res.status(500).send(\"OAuth callback failed. Check server logs.\");\n }\n });\n\n router.post(\"/auth/logout\", async (req: Request, res: Response) => {\n const session = await getSession(req, res, opts);\n session.destroy();\n res.json({ ok: true });\n });\n\n router.get(\"/auth/me\", async (req: Request, res: Response) => {\n const session = await getSession(req, res, opts);\n if (!session.githubLogin || !session.accessToken) {\n res.status(401).json({ error: \"unauthenticated\" });\n return;\n }\n if (allowed.size > 0 && !allowed.has(session.githubLogin.toLowerCase())) {\n res.status(403).json({ error: \"not_authorized\" });\n return;\n }\n res.json({\n githubLogin: session.githubLogin,\n avatarUrl: session.avatarUrl,\n defaultRepoUrl: opts.defaultRepoUrl,\n defaultRepoRef: opts.defaultRepoRef,\n });\n });\n\n return router;\n}\n","import type { NextFunction, Request, Response } from \"express\";\nimport { getIronSession } from \"iron-session\";\nimport { buildSessionOptions, SESSION_COOKIE_NAME, type CodingTabSession } from \"./session.js\";\n\nexport interface AuthenticatedRequest extends Request {\n user: {\n githubLogin: string;\n accessToken: string;\n avatarUrl?: string;\n };\n}\n\nexport interface AuthMiddlewareOptions {\n sessionPassword: string;\n secure: boolean;\n allowedLogins: string[];\n}\n\nexport function getSession(\n req: Request,\n res: Response,\n opts: { sessionPassword: string; secure: boolean },\n) {\n return getIronSession<CodingTabSession>(req, res, buildSessionOptions(opts.sessionPassword, opts.secure));\n}\n\nexport function makeRequireAuth(opts: AuthMiddlewareOptions) {\n const allowed = new Set(opts.allowedLogins.map((l) => l.toLowerCase()));\n return async function requireAuth(req: Request, res: Response, next: NextFunction) {\n try {\n const session = await getSession(req, res, opts);\n if (!session.githubLogin || !session.accessToken) {\n res.status(401).json({ error: \"unauthenticated\" });\n return;\n }\n if (allowed.size > 0 && !allowed.has(session.githubLogin.toLowerCase())) {\n res.status(403).json({ error: \"not_authorized\" });\n return;\n }\n (req as AuthenticatedRequest).user = {\n githubLogin: session.githubLogin,\n accessToken: session.accessToken,\n avatarUrl: session.avatarUrl,\n };\n next();\n } catch (err) {\n console.error(`[${SESSION_COOKIE_NAME}] requireAuth failed`, err);\n res.status(500).json({ error: \"session_error\" });\n }\n };\n}\n","import type { SessionOptions } from \"iron-session\";\n\nexport interface CodingTabSession {\n githubLogin?: string;\n avatarUrl?: string;\n accessToken?: string;\n oauthState?: string;\n oauthStateCreatedAt?: number;\n}\n\nexport const SESSION_COOKIE_NAME = \"coding_tab_session\";\n\nexport function buildSessionOptions(password: string, secure: boolean): SessionOptions {\n return {\n password,\n cookieName: SESSION_COOKIE_NAME,\n cookieOptions: {\n httpOnly: true,\n secure,\n sameSite: \"lax\",\n path: \"/\",\n maxAge: 60 * 60 * 24 * 30,\n },\n };\n}\n","import { Router, type Request, type Response } from \"express\";\nimport {\n Agent,\n CursorAgentError,\n type Run,\n type SDKAgent,\n type SDKMessage,\n} from \"@cursor/sdk\";\nimport { randomUUID } from \"node:crypto\";\nimport type { AuthenticatedRequest } from \"./authMiddleware.js\";\nimport { listAvailableModels, resolveModel } from \"./models.js\";\nimport {\n disposeSessionsForChat,\n getLiveSession,\n registerLiveSession,\n} from \"./sessions.js\";\nimport { sanitizeEvent, type ChatStorage } from \"./storage.js\";\nimport type {\n BranchInfo,\n ChatMode,\n ModelChoice,\n PrInfo,\n StoredChat,\n StoredTurn,\n StreamEvent,\n TimelineEvent,\n TurnStatus,\n} from \"../shared/types.js\";\n\nconst PLAN_INSTRUCTION = `You are operating in PLAN MODE.\n\nProduce a clear, concise markdown plan for the user's request. Do NOT modify any files. Do NOT call any file-editing or shell tools that mutate state. Read-only exploration tools (Read, Grep, Glob, semantic search) are encouraged.\n\nStructure the plan with:\n- Goal (1 line)\n- Approach (3-7 bullet points)\n- Files that will change (bulleted, with absolute or repo-relative paths)\n- Risks / things to verify after implementation\n\nWhen you are done, end your message with a single line: PLAN READY`;\n\nconst EXECUTE_INSTRUCTION = `You are now in AGENT MODE. Implement the plan you produced above. Make all required file changes, run any necessary commands, and prepare the changes for a pull request. When complete, summarize what changed in 3-5 bullets.`;\n\nexport interface AgentRoutesOptions {\n cursorApiKey: string;\n defaultRepo: { url: string; ref?: string };\n envName?: string;\n skipReviewerRequest?: boolean;\n storage: ChatStorage;\n}\n\nfunction sse(res: Response, event: StreamEvent) {\n res.write(`data: ${JSON.stringify(event)}\\n\\n`);\n}\n\nfunction parsePrUrl(url: string | undefined): PrInfo | undefined {\n if (!url) return undefined;\n const match = url.match(/github\\.com\\/([^/]+)\\/([^/]+)\\/pull\\/(\\d+)/);\n if (!match) return undefined;\n return { url, owner: match[1]!, repo: match[2]!, number: Number(match[3]!) };\n}\n\n/**\n * Strip a trailing `.git` and SSH-style prefixes so the URL is safe to drop\n * into an `<a href>` and reliably build `/pull/new/<branch>` from.\n */\nfunction repoWebUrl(repoUrl: string): string {\n const m = repoUrl.match(/github\\.com[/:]([^/]+)\\/([^/]+?)(?:\\.git)?\\/?$/);\n return m ? `https://github.com/${m[1]}/${m[2]}` : repoUrl.replace(/\\.git\\/?$/, \"\");\n}\n\n/**\n * Build the \"create a PR for this branch\" URL. `pull/new/<branch>` redirects\n * to GitHub's compare/PR-create page with the base branch auto-detected, so\n * we don't need to know whether the repo's default is `main` or `master`.\n */\nfunction buildBranchInfo(repoUrl: string, branch: string): BranchInfo {\n const web = repoWebUrl(repoUrl);\n return {\n repoUrl: web,\n branch,\n compareUrl: `${web}/pull/new/${encodeURIComponent(branch)}`,\n };\n}\n\nfunction modeInstructionPrefix(mode: ChatMode, prompt: string): string {\n return mode === \"plan\" ? `${PLAN_INSTRUCTION}\\n\\n---\\n\\nUser request:\\n${prompt}` : prompt;\n}\n\nfunction setupSseHeaders(res: Response) {\n res.set({\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache, no-transform\",\n Connection: \"keep-alive\",\n \"X-Accel-Buffering\": \"no\",\n });\n res.flushHeaders?.();\n}\n\nfunction deriveTitle(prompt: string): string {\n const trimmed = prompt.trim().replace(/\\s+/g, \" \");\n if (trimmed.length <= 60) return trimmed || \"New chat\";\n return `${trimmed.slice(0, 57)}…`;\n}\n\n/**\n * Builds an in-memory mirror of the assistant turn's `events[]` while the\n * SSE stream runs, so we can persist the full timeline at the end while also\n * folding deltas into the same `TimelineEvent` (so `text` chunks within one\n * model \"block\" stay together but a new block starts a new paragraph).\n */\nclass TurnBuffer {\n events: TimelineEvent[] = [];\n private lastWasText = false;\n\n pushText(text: string): TimelineEvent {\n if (this.lastWasText) {\n const last = this.events[this.events.length - 1]!;\n if (last.kind === \"text\") {\n last.text += text;\n return last;\n }\n }\n const evt: TimelineEvent = { kind: \"text\", id: randomUUID(), text };\n this.events.push(evt);\n this.lastWasText = true;\n return evt;\n }\n\n /**\n * Mark the next text event as starting a new block (called when the SDK\n * emits a non-streaming-delta boundary, e.g. a tool call or a new assistant\n * message). The next `pushText` will start a fresh paragraph.\n */\n endTextBlock(): void {\n this.lastWasText = false;\n }\n\n upsertTool(args: {\n callId: string;\n name: string;\n status: \"running\" | \"completed\" | \"error\";\n args?: unknown;\n result?: unknown;\n }): TimelineEvent {\n this.endTextBlock();\n const existing = this.events.find(\n (e): e is Extract<TimelineEvent, { kind: \"tool\" }> =>\n e.kind === \"tool\" && e.callId === args.callId,\n );\n if (existing) {\n existing.status = args.status;\n if (args.args !== undefined) existing.args = args.args;\n if (args.result !== undefined) existing.result = args.result;\n return existing;\n }\n const evt: TimelineEvent = {\n kind: \"tool\",\n id: randomUUID(),\n callId: args.callId,\n name: args.name,\n status: args.status,\n args: args.args,\n result: args.result,\n };\n this.events.push(evt);\n return evt;\n }\n\n snapshot(): TimelineEvent[] {\n return this.events.map(sanitizeEvent);\n }\n}\n\ninterface RunStreamContext {\n storage: ChatStorage;\n chat: StoredChat;\n turn: StoredTurn;\n buffer: TurnBuffer;\n}\n\nasync function streamRun(res: Response, run: Run, ctx: RunStreamContext): Promise<void> {\n const flushDebounceMs = 750;\n let flushPending = false;\n let lastFlush = 0;\n\n const persist = async (status?: TurnStatus, pr?: PrInfo, branch?: BranchInfo) => {\n try {\n await ctx.storage.patchTurn(ctx.chat.id, ctx.turn.id, {\n events: ctx.buffer.snapshot(),\n ...(status ? { status } : {}),\n ...(pr ? { pr } : {}),\n ...(branch ? { branch } : {}),\n });\n lastFlush = Date.now();\n } catch (err) {\n console.error(\"[coding-tab] patchTurn failed\", err);\n }\n };\n\n const scheduleFlush = () => {\n if (flushPending) return;\n flushPending = true;\n setTimeout(async () => {\n flushPending = false;\n const elapsed = Date.now() - lastFlush;\n if (elapsed < flushDebounceMs) return;\n await persist();\n }, flushDebounceMs).unref?.();\n };\n\n let finalStatus: TurnStatus = \"finished\";\n let finalPr: PrInfo | undefined;\n let finalBranch: BranchInfo | undefined;\n\n try {\n for await (const message of run.stream() as AsyncGenerator<SDKMessage, void>) {\n if (res.writableEnded) break;\n if (message.type === \"assistant\") {\n ctx.buffer.endTextBlock();\n for (const block of message.message.content) {\n if (block.type === \"text\" && block.text) {\n ctx.buffer.pushText(block.text);\n ctx.buffer.endTextBlock();\n sse(res, { kind: \"text\", text: block.text });\n } else if (block.type === \"tool_use\") {\n ctx.buffer.upsertTool({\n callId: block.id,\n name: block.name,\n status: \"running\",\n args: block.input,\n });\n sse(res, {\n kind: \"tool\",\n name: block.name,\n status: \"running\",\n callId: block.id,\n args: block.input,\n });\n }\n }\n } else if (message.type === \"thinking\") {\n sse(res, { kind: \"thinking\", text: message.text });\n } else if (message.type === \"tool_call\") {\n ctx.buffer.upsertTool({\n callId: message.call_id,\n name: message.name,\n status: message.status,\n args: message.args,\n result: message.result,\n });\n sse(res, {\n kind: \"tool\",\n name: message.name,\n status: message.status,\n callId: message.call_id,\n args: message.args,\n result: message.result,\n });\n } else if (message.type === \"status\") {\n sse(res, { kind: \"status\", status: message.status, message: message.message });\n }\n scheduleFlush();\n }\n const result = await run.wait();\n const branchWithPr = result.git?.branches?.find((b) => b.prUrl);\n finalPr = parsePrUrl(branchWithPr?.prUrl);\n // Even when no PR was opened (e.g. Cursor's auto-PR setting refused),\n // the agent may still have pushed a branch — surface that so the user\n // can open the PR themselves with one click.\n if (!finalPr) {\n const firstBranch = result.git?.branches?.find((b) => b.branch);\n if (firstBranch?.branch) {\n finalBranch = buildBranchInfo(firstBranch.repoUrl, firstBranch.branch);\n }\n }\n finalStatus = (result.status as TurnStatus) ?? \"finished\";\n sse(res, {\n kind: \"result\",\n status: result.status,\n pr: finalPr,\n branch: finalBranch,\n durationMs: result.durationMs,\n });\n } catch (err) {\n finalStatus = \"error\";\n const message = err instanceof Error ? err.message : String(err);\n const retryable = err instanceof CursorAgentError ? Boolean((err as { isRetryable?: boolean }).isRetryable) : false;\n sse(res, { kind: \"error\", message, retryable });\n } finally {\n await persist(finalStatus, finalPr, finalBranch);\n if (!res.writableEnded) res.end();\n }\n}\n\nasync function ensureAgentForChat(\n storage: ChatStorage,\n chat: StoredChat,\n user: { githubLogin: string; accessToken: string },\n opts: AgentRoutesOptions,\n modelChoice: ModelChoice,\n): Promise<{ agent: SDKAgent; agentId: string; resumed: boolean }> {\n const existing = getLiveSession(chat.id);\n if (existing && existing.githubLogin.toLowerCase() === user.githubLogin.toLowerCase()) {\n return { agent: existing.agent, agentId: existing.agentId, resumed: true };\n }\n\n const resolved = await resolveModel(opts.cursorApiKey, modelChoice);\n const cloud = {\n repos: [\n {\n url: chat.repoUrl,\n startingRef: chat.startingRef,\n },\n ],\n autoCreatePR: chat.mode === \"agent\",\n skipReviewerRequest: opts.skipReviewerRequest ?? true,\n envVars: { GITHUB_TOKEN: user.accessToken },\n ...(opts.envName ? { env: { type: \"cloud\" as const, name: opts.envName } } : {}),\n };\n\n if (chat.agentId) {\n try {\n const resumed = await Agent.resume(chat.agentId, {\n apiKey: opts.cursorApiKey,\n model: { id: resolved.cursorModelId },\n cloud,\n });\n registerLiveSession({\n chatId: chat.id,\n githubLogin: user.githubLogin,\n agent: resumed,\n agentId: resumed.agentId,\n });\n if (resumed.agentId !== chat.agentId) {\n await storage.patchChat(chat.id, user.githubLogin, { agentId: resumed.agentId });\n }\n return { agent: resumed, agentId: resumed.agentId, resumed: true };\n } catch (err) {\n console.warn(\n `[coding-tab] resume failed for chat=${chat.id} agentId=${chat.agentId}; creating fresh agent`,\n err instanceof Error ? err.message : err,\n );\n }\n }\n\n const fresh = await Agent.create({\n apiKey: opts.cursorApiKey,\n model: { id: resolved.cursorModelId },\n cloud,\n });\n registerLiveSession({\n chatId: chat.id,\n githubLogin: user.githubLogin,\n agent: fresh,\n agentId: fresh.agentId,\n });\n await storage.patchChat(chat.id, user.githubLogin, { agentId: fresh.agentId });\n return { agent: fresh, agentId: fresh.agentId, resumed: false };\n}\n\nasync function nextSeq(storage: ChatStorage, chatId: string, login: string): Promise<number> {\n const full = await storage.loadChat(chatId, login);\n if (!full) throw Object.assign(new Error(\"chat_not_found\"), { status: 404 });\n const max = full.turns.reduce((acc, t) => Math.max(acc, t.seq), -1);\n return max + 1;\n}\n\nexport function makeAgentRouter(opts: AgentRoutesOptions): Router {\n const router = Router();\n\n router.get(\"/models\", async (_req, res) => {\n try {\n const models = await listAvailableModels(opts.cursorApiKey);\n res.json({ models });\n } catch (err) {\n console.error(\"[coding-tab] /models failed\", err);\n res.status(500).json({ error: err instanceof Error ? err.message : \"models_failed\" });\n }\n });\n\n // Both /agent/start and /agent/send do the same thing now (require chatId,\n // resume or create the agent). /start is kept as an alias for compatibility.\n const handleSend = async (req: Request, res: Response, isExecute: boolean) => {\n const user = (req as AuthenticatedRequest).user;\n const body = (req.body ?? {}) as {\n chatId?: string;\n prompt?: string;\n mode?: ChatMode;\n };\n if (!body.chatId) {\n res.status(400).json({ error: \"missing_chatId\" });\n return;\n }\n if (!isExecute && (!body.prompt || (body.mode !== \"plan\" && body.mode !== \"agent\"))) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n\n const chat = await opts.storage.loadChat(body.chatId, user.githubLogin);\n if (!chat) {\n res.status(404).json({ error: \"chat_not_found\" });\n return;\n }\n\n setupSseHeaders(res);\n\n let assistantTurn: StoredTurn | null = null;\n try {\n // If the user changed mode in the composer, persist it on the chat row.\n if (!isExecute && body.mode && body.mode !== chat.mode) {\n chat.mode = body.mode;\n await opts.storage.patchChat(chat.id, user.githubLogin, { mode: body.mode });\n }\n\n // Persist the user turn (skip for execute since the user didn't type).\n let seq = await nextSeq(opts.storage, chat.id, user.githubLogin);\n if (!isExecute) {\n const userTurn: StoredTurn = {\n id: randomUUID(),\n chatId: chat.id,\n seq: seq++,\n role: \"user\",\n isPlan: chat.mode === \"plan\",\n status: \"finished\",\n events: [{ kind: \"text\", id: randomUUID(), text: body.prompt! }],\n prompt: body.prompt!,\n createdAt: Date.now(),\n };\n await opts.storage.appendTurn(userTurn);\n // First user turn becomes the chat title if still default.\n if (chat.title === \"New chat\") {\n const title = deriveTitle(body.prompt!);\n chat.title = title;\n await opts.storage.patchChat(chat.id, user.githubLogin, { title });\n }\n }\n\n assistantTurn = {\n id: randomUUID(),\n chatId: chat.id,\n seq,\n role: \"assistant\",\n isPlan: !isExecute && chat.mode === \"plan\",\n status: \"running\",\n events: [],\n createdAt: Date.now(),\n };\n await opts.storage.appendTurn(assistantTurn);\n\n const { agent } = await ensureAgentForChat(opts.storage, chat, user, opts, chat.model);\n const sendText = isExecute\n ? EXECUTE_INSTRUCTION\n : modeInstructionPrefix(body.mode!, body.prompt!);\n const run = await agent.send(sendText);\n sse(res, {\n kind: \"ready\",\n chatId: chat.id,\n turnId: assistantTurn.id,\n agentId: agent.agentId,\n runId: run.id,\n });\n console.log(\n `[coding-tab] ${isExecute ? \"execute\" : \"send\"} agent=${agent.agentId} run=${run.id} chat=${chat.id} login=${user.githubLogin}`,\n );\n\n const buffer = new TurnBuffer();\n await streamRun(res, run, {\n storage: opts.storage,\n chat,\n turn: assistantTurn,\n buffer,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const retryable = err instanceof CursorAgentError ? Boolean((err as { isRetryable?: boolean }).isRetryable) : false;\n console.error(`[coding-tab] /agent/${isExecute ? \"execute\" : \"send\"} failed`, err);\n sse(res, { kind: \"error\", message, retryable });\n if (assistantTurn) {\n await opts.storage\n .patchTurn(assistantTurn.chatId, assistantTurn.id, {\n status: \"error\",\n events: [\n ...assistantTurn.events,\n { kind: \"text\", id: randomUUID(), text: `[error] ${message}` },\n ],\n })\n .catch(() => {});\n }\n if (!res.writableEnded) res.end();\n }\n\n req.on(\"close\", () => {\n if (!res.writableEnded) res.end();\n });\n };\n\n router.post(\"/agent/start\", (req, res) => handleSend(req, res, false));\n router.post(\"/agent/send\", (req, res) => handleSend(req, res, false));\n router.post(\"/agent/execute\", (req, res) => handleSend(req, res, true));\n\n router.post(\"/agent/cancel\", async (req: Request, res: Response) => {\n const { chatId, runId } = (req.body ?? {}) as { chatId?: string; runId?: string };\n if (!chatId || !runId) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n const live = getLiveSession(chatId);\n if (!live) {\n res.status(404).json({ error: \"session_not_found\" });\n return;\n }\n try {\n await Agent.cancelRun(runId, {\n runtime: \"cloud\",\n agentId: live.agentId,\n apiKey: opts.cursorApiKey,\n });\n res.json({ ok: true });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(\"[coding-tab] /agent/cancel failed\", err);\n res.status(500).json({ error: message });\n }\n });\n\n router.post(\"/agent/dispose\", async (req: Request, res: Response) => {\n const { chatId } = (req.body ?? {}) as { chatId?: string };\n if (!chatId) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n await disposeSessionsForChat(chatId);\n res.json({ ok: true });\n });\n\n return router;\n}\n","import { Cursor, type SDKModel } from \"@cursor/sdk\";\nimport type { ModelChoice, ModelOption } from \"../shared/types.js\";\n\nconst SONNET_PATTERN = /sonnet/i;\nconst OPUS_PATTERN = /opus/i;\n\ninterface ModelMapEntry {\n cursorModelId: string;\n displayName: string;\n}\n\nlet cache: { fetchedAt: number; models: SDKModel[] } | null = null;\nconst CACHE_TTL_MS = 5 * 60 * 1000;\n\nasync function getModelList(apiKey: string): Promise<SDKModel[]> {\n if (cache && Date.now() - cache.fetchedAt < CACHE_TTL_MS) return cache.models;\n const models = await Cursor.models.list({ apiKey });\n cache = { fetchedAt: Date.now(), models };\n return models;\n}\n\nfunction pickBest(models: SDKModel[], pattern: RegExp): SDKModel | undefined {\n const matches = models.filter((m) => pattern.test(m.id) || pattern.test(m.displayName) || (m.aliases ?? []).some((a) => pattern.test(a)));\n if (matches.length === 0) return undefined;\n matches.sort((a, b) => b.id.localeCompare(a.id));\n return matches[0];\n}\n\nexport async function listAvailableModels(apiKey: string): Promise<ModelOption[]> {\n const models = await getModelList(apiKey);\n const out: ModelOption[] = [];\n const sonnet = pickBest(models, SONNET_PATTERN);\n if (sonnet) out.push({ choice: \"sonnet\", cursorModelId: sonnet.id, displayName: sonnet.displayName ?? sonnet.id });\n const opus = pickBest(models, OPUS_PATTERN);\n if (opus) out.push({ choice: \"opus\", cursorModelId: opus.id, displayName: opus.displayName ?? opus.id });\n return out;\n}\n\nexport async function resolveModel(apiKey: string, choice: ModelChoice): Promise<ModelMapEntry> {\n const models = await getModelList(apiKey);\n const pattern = choice === \"sonnet\" ? SONNET_PATTERN : OPUS_PATTERN;\n const picked = pickBest(models, pattern);\n if (!picked) {\n const available = models.map((m) => `${m.displayName} (${m.id})`).join(\", \");\n throw new Error(`No Cursor-routed model matches \"${choice}\". Available: ${available}`);\n }\n return { cursorModelId: picked.id, displayName: picked.displayName ?? picked.id };\n}\n","import type { SDKAgent } from \"@cursor/sdk\";\n\n/**\n * In-memory cache of live `Agent` handles, keyed by chatId. Persistent\n * chat metadata (including the cloud `agentId`) lives in `ChatStorage`; this\n * map exists only so the same `Agent.create()` instance can be reused across\n * multiple turns within a single Node process to avoid the cold-start\n * resume cost on every send.\n */\nexport interface LiveSession {\n chatId: string;\n githubLogin: string;\n agent: SDKAgent;\n agentId: string;\n createdAt: number;\n lastUsedAt: number;\n}\n\nconst SESSION_IDLE_TTL_MS = 30 * 60 * 1000;\nconst sessions = new Map<string, LiveSession>();\n\nlet sweeperStarted = false;\nfunction startSweeper() {\n if (sweeperStarted) return;\n sweeperStarted = true;\n setInterval(() => {\n const now = Date.now();\n for (const [id, s] of sessions) {\n if (now - s.lastUsedAt > SESSION_IDLE_TTL_MS) {\n disposeSessionsForChat(id).catch((e) =>\n console.error(\"[coding-tab] session sweep dispose failed\", e),\n );\n }\n }\n }, 5 * 60 * 1000).unref?.();\n}\n\nexport function registerLiveSession(opts: {\n chatId: string;\n githubLogin: string;\n agent: SDKAgent;\n agentId: string;\n}): LiveSession {\n startSweeper();\n // If a session already exists for this chat, dispose it first.\n const prev = sessions.get(opts.chatId);\n if (prev) {\n sessions.delete(opts.chatId);\n prev.agent[Symbol.asyncDispose]().catch(() => {});\n }\n const session: LiveSession = {\n chatId: opts.chatId,\n githubLogin: opts.githubLogin,\n agent: opts.agent,\n agentId: opts.agentId,\n createdAt: Date.now(),\n lastUsedAt: Date.now(),\n };\n sessions.set(opts.chatId, session);\n return session;\n}\n\nexport function getLiveSession(chatId: string): LiveSession | undefined {\n const s = sessions.get(chatId);\n if (s) s.lastUsedAt = Date.now();\n return s;\n}\n\nexport async function disposeSessionsForChat(chatId: string): Promise<void> {\n const s = sessions.get(chatId);\n if (!s) return;\n sessions.delete(chatId);\n try {\n await s.agent[Symbol.asyncDispose]();\n } catch (err) {\n console.error(\"[coding-tab] dispose failed\", err);\n }\n}\n","import { mkdir, readFile, readdir, rename, rm, writeFile } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport type {\n ChatListItem,\n FullChat,\n StoredChat,\n StoredTurn,\n TimelineEvent,\n} from \"../shared/types.js\";\n\n/**\n * Pluggable persistence layer for chats + turns. The default\n * `FileChatStorage` writes JSON files under a data directory; host apps can\n * swap in their own implementation (e.g. Postgres / Supabase) by passing\n * `storage` to `mountCodingTab`.\n */\nexport interface ChatStorage {\n listChats(login: string): Promise<ChatListItem[]>;\n loadChat(id: string, login: string): Promise<FullChat | null>;\n createChat(chat: StoredChat): Promise<void>;\n patchChat(\n id: string,\n login: string,\n patch: Partial<Omit<StoredChat, \"id\" | \"githubLogin\" | \"createdAt\">>,\n ): Promise<StoredChat | null>;\n appendTurn(turn: StoredTurn): Promise<void>;\n patchTurn(\n chatId: string,\n turnId: string,\n patch: Partial<Omit<StoredTurn, \"id\" | \"chatId\" | \"createdAt\">>,\n ): Promise<void>;\n deleteChat(id: string, login: string): Promise<boolean>;\n}\n\n// ───────── helpers ─────────\n\nfunction safeId(s: string): string {\n return s.replace(/[^a-zA-Z0-9_.-]/g, \"_\");\n}\n\nasync function ensureDir(p: string): Promise<void> {\n await mkdir(p, { recursive: true });\n}\n\nasync function readJson<T>(path: string): Promise<T | null> {\n try {\n const raw = await readFile(path, \"utf8\");\n return JSON.parse(raw) as T;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n }\n}\n\nasync function writeJsonAtomic(path: string, data: unknown): Promise<void> {\n await ensureDir(dirname(path));\n const tmp = `${path}.${randomUUID()}.tmp`;\n await writeFile(tmp, JSON.stringify(data, null, 2), \"utf8\");\n await rename(tmp, path);\n}\n\n/** Truncate args/result blobs that are too large to comfortably persist. */\nconst MAX_BLOB_BYTES = 32 * 1024;\nfunction truncateBlob(value: unknown): unknown {\n if (value === undefined || value === null) return value;\n try {\n const str = typeof value === \"string\" ? value : JSON.stringify(value);\n if (str.length <= MAX_BLOB_BYTES) return value;\n const head = str.slice(0, MAX_BLOB_BYTES);\n return `${head}\\n\\n…[truncated ${str.length - MAX_BLOB_BYTES} chars]`;\n } catch {\n return \"[unserializable]\";\n }\n}\n\nexport function sanitizeEvent(evt: TimelineEvent): TimelineEvent {\n if (evt.kind === \"tool\") {\n return {\n ...evt,\n args: truncateBlob(evt.args),\n result: truncateBlob(evt.result),\n };\n }\n return evt;\n}\n\n// ───────── default file-backed storage ─────────\n\nexport interface FileChatStorageOptions {\n dataDir: string;\n}\n\ninterface ChatFile {\n chat: StoredChat;\n turns: StoredTurn[];\n}\n\nexport class FileChatStorage implements ChatStorage {\n private readonly dataDir: string;\n /** Per-chat write mutex chain to keep concurrent appends consistent. */\n private readonly chains = new Map<string, Promise<unknown>>();\n\n constructor(opts: FileChatStorageOptions) {\n this.dataDir = resolve(opts.dataDir);\n }\n\n private chatPath(id: string): string {\n return join(this.dataDir, \"chats\", `${safeId(id)}.json`);\n }\n\n private indexPath(login: string): string {\n return join(this.dataDir, \"index\", `${safeId(login.toLowerCase())}.json`);\n }\n\n /** Serialize all reads/writes for a single chat through a chained promise. */\n private withChat<T>(id: string, fn: () => Promise<T>): Promise<T> {\n const prev = this.chains.get(id) ?? Promise.resolve();\n const next = prev.then(fn, fn);\n this.chains.set(\n id,\n next.finally(() => {\n if (this.chains.get(id) === next) this.chains.delete(id);\n }),\n );\n return next;\n }\n\n private async readChat(id: string): Promise<ChatFile | null> {\n return readJson<ChatFile>(this.chatPath(id));\n }\n\n private async writeChat(file: ChatFile): Promise<void> {\n await writeJsonAtomic(this.chatPath(file.chat.id), file);\n }\n\n private async readIndex(login: string): Promise<ChatListItem[]> {\n const list = await readJson<ChatListItem[]>(this.indexPath(login));\n return list ?? [];\n }\n\n private async writeIndex(login: string, items: ChatListItem[]): Promise<void> {\n items.sort((a, b) => b.updatedAt - a.updatedAt);\n await writeJsonAtomic(this.indexPath(login), items);\n }\n\n /**\n * Rebuild the index for a user by scanning every chat file. Used as a\n * self-heal path when the index gets out of sync (e.g. crash mid-write).\n */\n private async rebuildIndex(login: string): Promise<ChatListItem[]> {\n const dir = join(this.dataDir, \"chats\");\n let entries: string[] = [];\n try {\n entries = await readdir(dir);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n const items: ChatListItem[] = [];\n const target = login.toLowerCase();\n for (const name of entries) {\n if (!name.endsWith(\".json\")) continue;\n const file = await readJson<ChatFile>(join(dir, name));\n if (!file?.chat) continue;\n if (file.chat.githubLogin.toLowerCase() !== target) continue;\n items.push(this.toListItem(file.chat));\n }\n await this.writeIndex(login, items);\n return items;\n }\n\n private toListItem(c: StoredChat): ChatListItem {\n return {\n id: c.id,\n title: c.title,\n mode: c.mode,\n model: c.model,\n createdAt: c.createdAt,\n updatedAt: c.updatedAt,\n };\n }\n\n private async upsertIndex(chat: StoredChat): Promise<void> {\n const items = await this.readIndex(chat.githubLogin);\n const idx = items.findIndex((c) => c.id === chat.id);\n const item = this.toListItem(chat);\n if (idx >= 0) items[idx] = item;\n else items.push(item);\n await this.writeIndex(chat.githubLogin, items);\n }\n\n private async removeFromIndex(login: string, chatId: string): Promise<void> {\n const items = await this.readIndex(login);\n const filtered = items.filter((c) => c.id !== chatId);\n if (filtered.length !== items.length) {\n await this.writeIndex(login, filtered);\n }\n }\n\n async listChats(login: string): Promise<ChatListItem[]> {\n const items = await this.readIndex(login);\n if (items.length > 0) return items;\n // Self-heal: index missing or empty but chat files might exist.\n return this.rebuildIndex(login);\n }\n\n async loadChat(id: string, login: string): Promise<FullChat | null> {\n return this.withChat(id, async () => {\n const file = await this.readChat(id);\n if (!file) return null;\n if (file.chat.githubLogin.toLowerCase() !== login.toLowerCase()) {\n return null;\n }\n const turns = [...file.turns].sort((a, b) => a.seq - b.seq);\n return { ...file.chat, turns };\n });\n }\n\n async createChat(chat: StoredChat): Promise<void> {\n await this.withChat(chat.id, async () => {\n await this.writeChat({ chat, turns: [] });\n });\n await this.upsertIndex(chat);\n }\n\n async patchChat(\n id: string,\n login: string,\n patch: Partial<Omit<StoredChat, \"id\" | \"githubLogin\" | \"createdAt\">>,\n ): Promise<StoredChat | null> {\n const updated = await this.withChat(id, async () => {\n const file = await this.readChat(id);\n if (!file) return null;\n if (file.chat.githubLogin.toLowerCase() !== login.toLowerCase()) {\n return null;\n }\n const next: StoredChat = {\n ...file.chat,\n ...patch,\n updatedAt: Date.now(),\n };\n await this.writeChat({ chat: next, turns: file.turns });\n return next;\n });\n if (updated) await this.upsertIndex(updated);\n return updated;\n }\n\n async appendTurn(turn: StoredTurn): Promise<void> {\n const sanitized: StoredTurn = {\n ...turn,\n events: turn.events.map(sanitizeEvent),\n };\n let chat: StoredChat | null = null;\n await this.withChat(turn.chatId, async () => {\n const file = await this.readChat(turn.chatId);\n if (!file) throw new Error(`chat_not_found:${turn.chatId}`);\n file.turns = file.turns.filter((t) => t.id !== sanitized.id);\n file.turns.push(sanitized);\n file.chat = { ...file.chat, updatedAt: Date.now() };\n chat = file.chat;\n await this.writeChat(file);\n });\n if (chat) await this.upsertIndex(chat);\n }\n\n async patchTurn(\n chatId: string,\n turnId: string,\n patch: Partial<Omit<StoredTurn, \"id\" | \"chatId\" | \"createdAt\">>,\n ): Promise<void> {\n let chat: StoredChat | null = null;\n await this.withChat(chatId, async () => {\n const file = await this.readChat(chatId);\n if (!file) return;\n const idx = file.turns.findIndex((t) => t.id === turnId);\n if (idx < 0) return;\n const current = file.turns[idx]!;\n const events = patch.events ? patch.events.map(sanitizeEvent) : current.events;\n file.turns[idx] = { ...current, ...patch, events };\n file.chat = { ...file.chat, updatedAt: Date.now() };\n chat = file.chat;\n await this.writeChat(file);\n });\n if (chat) await this.upsertIndex(chat);\n }\n\n async deleteChat(id: string, login: string): Promise<boolean> {\n const removed = await this.withChat(id, async () => {\n const file = await this.readChat(id);\n if (!file) return false;\n if (file.chat.githubLogin.toLowerCase() !== login.toLowerCase()) {\n return false;\n }\n await rm(this.chatPath(id), { force: true });\n return true;\n });\n if (removed) await this.removeFromIndex(login, id);\n return removed;\n }\n}\n\nexport function createDefaultStorage(dataDir?: string): ChatStorage {\n const dir = dataDir ?? join(process.cwd(), \".coding-tab-data\");\n return new FileChatStorage({ dataDir: dir });\n}\n","import { Router, type Request, type Response } from \"express\";\nimport { randomUUID } from \"node:crypto\";\nimport type { AuthenticatedRequest } from \"./authMiddleware.js\";\nimport { disposeSessionsForChat } from \"./sessions.js\";\nimport type { ChatStorage } from \"./storage.js\";\nimport type {\n ChatMode,\n CreateChatRequest,\n ModelChoice,\n PatchChatRequest,\n StoredChat,\n} from \"../shared/types.js\";\n\nexport interface ChatRoutesOptions {\n storage: ChatStorage;\n defaultRepo: { url: string; ref?: string };\n}\n\nconst VALID_MODES: ChatMode[] = [\"plan\", \"agent\"];\nconst VALID_MODELS: ModelChoice[] = [\"sonnet\", \"opus\"];\n\nexport function makeChatRouter(opts: ChatRoutesOptions): Router {\n const router = Router();\n\n router.get(\"/chats\", async (req: Request, res: Response) => {\n const user = (req as AuthenticatedRequest).user;\n try {\n const items = await opts.storage.listChats(user.githubLogin);\n res.json({ chats: items });\n } catch (err) {\n console.error(\"[coding-tab] /chats list failed\", err);\n res.status(500).json({ error: err instanceof Error ? err.message : \"list_failed\" });\n }\n });\n\n router.post(\"/chats\", async (req: Request, res: Response) => {\n const user = (req as AuthenticatedRequest).user;\n const body = (req.body ?? {}) as CreateChatRequest;\n const mode: ChatMode = VALID_MODES.includes(body.mode as ChatMode) ? (body.mode as ChatMode) : \"plan\";\n const model: ModelChoice = VALID_MODELS.includes(body.model as ModelChoice) ? (body.model as ModelChoice) : \"sonnet\";\n const now = Date.now();\n const chat: StoredChat = {\n id: randomUUID(),\n githubLogin: user.githubLogin,\n title: (body.title ?? \"\").trim() || \"New chat\",\n mode,\n model,\n repoUrl: body.repoUrl?.trim() || opts.defaultRepo.url,\n startingRef: body.startingRef ?? opts.defaultRepo.ref,\n createdAt: now,\n updatedAt: now,\n };\n try {\n await opts.storage.createChat(chat);\n res.status(201).json({ chat });\n } catch (err) {\n console.error(\"[coding-tab] /chats create failed\", err);\n res.status(500).json({ error: err instanceof Error ? err.message : \"create_failed\" });\n }\n });\n\n router.get(\"/chats/:id\", async (req: Request, res: Response) => {\n const user = (req as AuthenticatedRequest).user;\n try {\n const full = await opts.storage.loadChat(req.params.id!, user.githubLogin);\n if (!full) {\n res.status(404).json({ error: \"chat_not_found\" });\n return;\n }\n res.json({ chat: full });\n } catch (err) {\n console.error(\"[coding-tab] /chats/:id load failed\", err);\n res.status(500).json({ error: err instanceof Error ? err.message : \"load_failed\" });\n }\n });\n\n router.patch(\"/chats/:id\", async (req: Request, res: Response) => {\n const user = (req as AuthenticatedRequest).user;\n const body = (req.body ?? {}) as PatchChatRequest;\n const patch: PatchChatRequest = {};\n if (typeof body.title === \"string\") patch.title = body.title.trim() || \"Untitled\";\n if (body.mode && VALID_MODES.includes(body.mode)) patch.mode = body.mode;\n if (body.model && VALID_MODELS.includes(body.model)) patch.model = body.model;\n try {\n const updated = await opts.storage.patchChat(req.params.id!, user.githubLogin, patch);\n if (!updated) {\n res.status(404).json({ error: \"chat_not_found\" });\n return;\n }\n res.json({ chat: updated });\n } catch (err) {\n console.error(\"[coding-tab] /chats/:id patch failed\", err);\n res.status(500).json({ error: err instanceof Error ? err.message : \"patch_failed\" });\n }\n });\n\n router.delete(\"/chats/:id\", async (req: Request, res: Response) => {\n const user = (req as AuthenticatedRequest).user;\n try {\n const removed = await opts.storage.deleteChat(req.params.id!, user.githubLogin);\n if (!removed) {\n res.status(404).json({ error: \"chat_not_found\" });\n return;\n }\n // Best-effort: tear down any in-memory live session for this chat.\n await disposeSessionsForChat(req.params.id!).catch(() => {});\n res.json({ ok: true });\n } catch (err) {\n console.error(\"[coding-tab] /chats/:id delete failed\", err);\n res.status(500).json({ error: err instanceof Error ? err.message : \"delete_failed\" });\n }\n });\n\n return router;\n}\n","import { Router, type Request, type Response } from \"express\";\nimport { Octokit } from \"@octokit/rest\";\nimport type { AuthenticatedRequest } from \"./authMiddleware.js\";\nimport type { MergeRequest, PrInfo } from \"../shared/types.js\";\n\nfunction parsePrUrl(prUrl: string): { owner: string; repo: string; number: number } | null {\n const match = prUrl.match(/github\\.com\\/([^/]+)\\/([^/]+)\\/pull\\/(\\d+)/);\n if (!match) return null;\n return { owner: match[1]!, repo: match[2]!, number: Number(match[3]!) };\n}\n\nexport function makeGitHubRouter(): Router {\n const router = Router();\n\n router.get(\"/pr/status\", async (req: Request, res: Response) => {\n const prUrl = typeof req.query.prUrl === \"string\" ? req.query.prUrl : null;\n if (!prUrl) {\n res.status(400).json({ error: \"missing_prUrl\" });\n return;\n }\n const parsed = parsePrUrl(prUrl);\n if (!parsed) {\n res.status(400).json({ error: \"invalid_prUrl\" });\n return;\n }\n const user = (req as AuthenticatedRequest).user;\n const octokit = new Octokit({ auth: user.accessToken });\n try {\n const pr = await octokit.pulls.get({ owner: parsed.owner, repo: parsed.repo, pull_number: parsed.number });\n const info: PrInfo = {\n url: pr.data.html_url,\n owner: parsed.owner,\n repo: parsed.repo,\n number: parsed.number,\n branch: pr.data.head?.ref,\n state: pr.data.merged ? \"merged\" : (pr.data.state as \"open\" | \"closed\"),\n title: pr.data.title,\n body: pr.data.body ?? undefined,\n };\n res.json({ pr: info, mergeable: pr.data.mergeable, mergeable_state: pr.data.mergeable_state });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n res.status(500).json({ error: message });\n }\n });\n\n router.post(\"/pr/merge\", async (req: Request, res: Response) => {\n const body = (req.body ?? {}) as MergeRequest;\n if (!body.prUrl) {\n res.status(400).json({ error: \"missing_prUrl\" });\n return;\n }\n const parsed = parsePrUrl(body.prUrl);\n if (!parsed) {\n res.status(400).json({ error: \"invalid_prUrl\" });\n return;\n }\n const user = (req as AuthenticatedRequest).user;\n const octokit = new Octokit({ auth: user.accessToken });\n try {\n const pr = await octokit.pulls.get({\n owner: parsed.owner,\n repo: parsed.repo,\n pull_number: parsed.number,\n });\n if (pr.data.draft) {\n await octokit.graphql<{ markPullRequestReadyForReview: { pullRequest: { id: string } } }>(\n `mutation($id: ID!) {\n markPullRequestReadyForReview(input: { pullRequestId: $id }) {\n pullRequest { id }\n }\n }`,\n { id: pr.data.node_id },\n );\n }\n const merge = await octokit.pulls.merge({\n owner: parsed.owner,\n repo: parsed.repo,\n pull_number: parsed.number,\n merge_method: body.mergeMethod ?? \"squash\",\n });\n res.json({ sha: merge.data.sha, merged: merge.data.merged });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const status = (err as { status?: number }).status ?? 500;\n console.error(\"[coding-tab] /pr/merge failed\", err);\n res.status(status >= 400 && status < 600 ? status : 500).json({ error: message });\n }\n });\n\n return router;\n}\n","import { Router, type Request, type Response } from \"express\";\nimport { readFile } from \"node:fs/promises\";\nimport { resolve, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst here = dirname(fileURLToPath(import.meta.url));\n\nconst ASSET_CANDIDATES = [\n resolve(here, \"browser.js\"),\n resolve(here, \"..\", \"dist\", \"browser.js\"),\n resolve(here, \"..\", \"browser.js\"),\n];\nconst STYLE_CANDIDATES = [\n resolve(here, \"style.css\"),\n resolve(here, \"..\", \"dist\", \"style.css\"),\n resolve(here, \"..\", \"style.css\"),\n];\n\nasync function readFirst(paths: string[]): Promise<{ path: string; data: Buffer }> {\n let lastErr: unknown;\n for (const p of paths) {\n try {\n const data = await readFile(p);\n return { path: p, data };\n } catch (err) {\n lastErr = err;\n }\n }\n throw lastErr instanceof Error ? lastErr : new Error(\"asset not found\");\n}\n\nexport function makeAssetRouter(basePath: string): Router {\n const router = Router();\n\n router.get(\"/browser.js\", async (_req, res) => {\n try {\n const { data } = await readFirst(ASSET_CANDIDATES);\n res.set(\"Content-Type\", \"application/javascript; charset=utf-8\");\n res.set(\"Cache-Control\", \"public, max-age=0, must-revalidate\");\n res.send(data);\n } catch (err) {\n res.status(500).send(\"// coding-tab browser bundle missing — did you run `npm run build`?\");\n }\n });\n\n router.get(\"/style.css\", async (_req, res) => {\n try {\n const { data } = await readFirst(STYLE_CANDIDATES);\n res.set(\"Content-Type\", \"text/css; charset=utf-8\");\n res.set(\"Cache-Control\", \"public, max-age=0, must-revalidate\");\n res.send(data);\n } catch {\n res.status(404).send(\"/* style.css missing */\");\n }\n });\n\n router.get(\"/\", async (_req: Request, res: Response) => {\n const html = renderHostHtml(basePath);\n res.set(\"Content-Type\", \"text/html; charset=utf-8\");\n res.send(html);\n });\n\n return router;\n}\n\nfunction renderHostHtml(basePath: string): string {\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, viewport-fit=cover\" />\n <title>Coding Tab</title>\n <link rel=\"stylesheet\" href=\"${basePath}/style.css\" />\n </head>\n <body>\n <div id=\"coding-tab-root\"></div>\n <script src=\"${basePath}/browser.js\"></script>\n <script>\n window.CodingTab.mountCodingTab(\n document.getElementById(\"coding-tab-root\"),\n { apiBase: ${JSON.stringify(basePath)} },\n );\n </script>\n </body>\n</html>`;\n}\n"],"mappings":"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","names":["import_express","import_express","import_sdk","import_node_crypto","import_node_crypto","import_express","import_node_crypto","import_express","parsePrUrl","import_express","import_promises","import_node_path","express"]}
|
package/dist/server.d.cts
CHANGED
|
@@ -31,6 +31,17 @@ interface PrInfo {
|
|
|
31
31
|
title?: string;
|
|
32
32
|
body?: string;
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Branch the agent pushed during a run. Surfaced when the cloud `pr_management`
|
|
36
|
+
* tool DIDN'T open a PR (e.g. because Cursor's auto-PR setting is restrictive
|
|
37
|
+
* or the team-level approval gate is enabled). The client uses `compareUrl`
|
|
38
|
+
* to render a one-click "Create PR on GitHub" fallback.
|
|
39
|
+
*/
|
|
40
|
+
interface BranchInfo {
|
|
41
|
+
repoUrl: string;
|
|
42
|
+
branch: string;
|
|
43
|
+
compareUrl: string;
|
|
44
|
+
}
|
|
34
45
|
/**
|
|
35
46
|
* A chronological event inside an assistant turn. Either a paragraph of text
|
|
36
47
|
* (one per `block.text` from the SDK assistant message) or a tool invocation
|
|
@@ -61,6 +72,12 @@ interface StoredTurn {
|
|
|
61
72
|
status?: TurnStatus;
|
|
62
73
|
events: TimelineEvent[];
|
|
63
74
|
pr?: PrInfo;
|
|
75
|
+
/**
|
|
76
|
+
* Set on assistant turns that pushed commits but didn't end up opening a PR.
|
|
77
|
+
* Lets the UI render a "Create PR on GitHub" fallback button so the user
|
|
78
|
+
* isn't blocked when Cursor's PR creation gate refuses.
|
|
79
|
+
*/
|
|
80
|
+
branch?: BranchInfo;
|
|
64
81
|
createdAt: number;
|
|
65
82
|
/** For user turns: the raw prompt typed into the composer. */
|
|
66
83
|
prompt?: string;
|
|
@@ -153,6 +170,7 @@ type StreamEvent = {
|
|
|
153
170
|
kind: "result";
|
|
154
171
|
status: "finished" | "error" | "cancelled";
|
|
155
172
|
pr?: PrInfo;
|
|
173
|
+
branch?: BranchInfo;
|
|
156
174
|
durationMs?: number;
|
|
157
175
|
} | {
|
|
158
176
|
kind: "error";
|
package/dist/server.d.ts
CHANGED
|
@@ -31,6 +31,17 @@ interface PrInfo {
|
|
|
31
31
|
title?: string;
|
|
32
32
|
body?: string;
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Branch the agent pushed during a run. Surfaced when the cloud `pr_management`
|
|
36
|
+
* tool DIDN'T open a PR (e.g. because Cursor's auto-PR setting is restrictive
|
|
37
|
+
* or the team-level approval gate is enabled). The client uses `compareUrl`
|
|
38
|
+
* to render a one-click "Create PR on GitHub" fallback.
|
|
39
|
+
*/
|
|
40
|
+
interface BranchInfo {
|
|
41
|
+
repoUrl: string;
|
|
42
|
+
branch: string;
|
|
43
|
+
compareUrl: string;
|
|
44
|
+
}
|
|
34
45
|
/**
|
|
35
46
|
* A chronological event inside an assistant turn. Either a paragraph of text
|
|
36
47
|
* (one per `block.text` from the SDK assistant message) or a tool invocation
|
|
@@ -61,6 +72,12 @@ interface StoredTurn {
|
|
|
61
72
|
status?: TurnStatus;
|
|
62
73
|
events: TimelineEvent[];
|
|
63
74
|
pr?: PrInfo;
|
|
75
|
+
/**
|
|
76
|
+
* Set on assistant turns that pushed commits but didn't end up opening a PR.
|
|
77
|
+
* Lets the UI render a "Create PR on GitHub" fallback button so the user
|
|
78
|
+
* isn't blocked when Cursor's PR creation gate refuses.
|
|
79
|
+
*/
|
|
80
|
+
branch?: BranchInfo;
|
|
64
81
|
createdAt: number;
|
|
65
82
|
/** For user turns: the raw prompt typed into the composer. */
|
|
66
83
|
prompt?: string;
|
|
@@ -153,6 +170,7 @@ type StreamEvent = {
|
|
|
153
170
|
kind: "result";
|
|
154
171
|
status: "finished" | "error" | "cancelled";
|
|
155
172
|
pr?: PrInfo;
|
|
173
|
+
branch?: BranchInfo;
|
|
156
174
|
durationMs?: number;
|
|
157
175
|
} | {
|
|
158
176
|
kind: "error";
|
package/dist/server.js
CHANGED
|
@@ -512,6 +512,18 @@ function parsePrUrl(url) {
|
|
|
512
512
|
if (!match) return void 0;
|
|
513
513
|
return { url, owner: match[1], repo: match[2], number: Number(match[3]) };
|
|
514
514
|
}
|
|
515
|
+
function repoWebUrl(repoUrl) {
|
|
516
|
+
const m = repoUrl.match(/github\.com[/:]([^/]+)\/([^/]+?)(?:\.git)?\/?$/);
|
|
517
|
+
return m ? `https://github.com/${m[1]}/${m[2]}` : repoUrl.replace(/\.git\/?$/, "");
|
|
518
|
+
}
|
|
519
|
+
function buildBranchInfo(repoUrl, branch) {
|
|
520
|
+
const web = repoWebUrl(repoUrl);
|
|
521
|
+
return {
|
|
522
|
+
repoUrl: web,
|
|
523
|
+
branch,
|
|
524
|
+
compareUrl: `${web}/pull/new/${encodeURIComponent(branch)}`
|
|
525
|
+
};
|
|
526
|
+
}
|
|
515
527
|
function modeInstructionPrefix(mode, prompt) {
|
|
516
528
|
return mode === "plan" ? `${PLAN_INSTRUCTION}
|
|
517
529
|
|
|
@@ -589,12 +601,13 @@ async function streamRun(res, run, ctx) {
|
|
|
589
601
|
const flushDebounceMs = 750;
|
|
590
602
|
let flushPending = false;
|
|
591
603
|
let lastFlush = 0;
|
|
592
|
-
const persist = async (status, pr) => {
|
|
604
|
+
const persist = async (status, pr, branch) => {
|
|
593
605
|
try {
|
|
594
606
|
await ctx.storage.patchTurn(ctx.chat.id, ctx.turn.id, {
|
|
595
607
|
events: ctx.buffer.snapshot(),
|
|
596
608
|
...status ? { status } : {},
|
|
597
|
-
...pr ? { pr } : {}
|
|
609
|
+
...pr ? { pr } : {},
|
|
610
|
+
...branch ? { branch } : {}
|
|
598
611
|
});
|
|
599
612
|
lastFlush = Date.now();
|
|
600
613
|
} catch (err) {
|
|
@@ -613,6 +626,7 @@ async function streamRun(res, run, ctx) {
|
|
|
613
626
|
};
|
|
614
627
|
let finalStatus = "finished";
|
|
615
628
|
let finalPr;
|
|
629
|
+
let finalBranch;
|
|
616
630
|
try {
|
|
617
631
|
for await (const message of run.stream()) {
|
|
618
632
|
if (res.writableEnded) break;
|
|
@@ -663,13 +677,20 @@ async function streamRun(res, run, ctx) {
|
|
|
663
677
|
scheduleFlush();
|
|
664
678
|
}
|
|
665
679
|
const result = await run.wait();
|
|
666
|
-
const
|
|
667
|
-
finalPr = parsePrUrl(prUrl);
|
|
680
|
+
const branchWithPr = result.git?.branches?.find((b) => b.prUrl);
|
|
681
|
+
finalPr = parsePrUrl(branchWithPr?.prUrl);
|
|
682
|
+
if (!finalPr) {
|
|
683
|
+
const firstBranch = result.git?.branches?.find((b) => b.branch);
|
|
684
|
+
if (firstBranch?.branch) {
|
|
685
|
+
finalBranch = buildBranchInfo(firstBranch.repoUrl, firstBranch.branch);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
668
688
|
finalStatus = result.status ?? "finished";
|
|
669
689
|
sse(res, {
|
|
670
690
|
kind: "result",
|
|
671
691
|
status: result.status,
|
|
672
692
|
pr: finalPr,
|
|
693
|
+
branch: finalBranch,
|
|
673
694
|
durationMs: result.durationMs
|
|
674
695
|
});
|
|
675
696
|
} catch (err) {
|
|
@@ -678,7 +699,7 @@ async function streamRun(res, run, ctx) {
|
|
|
678
699
|
const retryable = err instanceof CursorAgentError ? Boolean(err.isRetryable) : false;
|
|
679
700
|
sse(res, { kind: "error", message, retryable });
|
|
680
701
|
} finally {
|
|
681
|
-
await persist(finalStatus, finalPr);
|
|
702
|
+
await persist(finalStatus, finalPr, finalBranch);
|
|
682
703
|
if (!res.writableEnded) res.end();
|
|
683
704
|
}
|
|
684
705
|
}
|