@tstax/coding-tab 0.1.0 → 0.1.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/README.md CHANGED
@@ -64,6 +64,10 @@ That's it on the server. Visit `https://<your-app>/coding-tab/` and the tab is f
64
64
  | `ALLOWED_GITHUB_LOGIN` | Comma-separated GitHub usernames allowed to sign in |
65
65
  | `PUBLIC_BASE_URL` | Your app's external URL, used to build the OAuth callback |
66
66
 
67
+ ## Repo selection
68
+
69
+ The repo the agent works against is whatever you pass in `defaultRepo.url` server-side. The client widget reads this from `/auth/me` and locks the URL field to that repo (rendered as a clickable `org/repo` pill in the header), so users can't accidentally point the agent at a different codebase. To change the repo, change the server config — there's intentionally no UI override.
70
+
67
71
  ## Security model
68
72
 
69
73
  - **GitHub OAuth, single sign-in.** The user signs in with GitHub once. The OAuth access token authenticates the session AND authorizes clone/push/PR/merge — no separate PAT to manage.
@@ -91,7 +95,7 @@ Public:
91
95
  - `GET /auth/login` — start OAuth
92
96
  - `GET /auth/callback` — OAuth return
93
97
  - `POST /auth/logout`
94
- - `GET /auth/me` — 401 if not signed in, otherwise `{ githubLogin, avatarUrl? }`
98
+ - `GET /auth/me` — 401 if not signed in, otherwise `{ githubLogin, avatarUrl?, defaultRepoUrl?, defaultRepoRef? }`
95
99
 
96
100
  Authenticated (require valid session):
97
101
  - `GET /models` — discovered Sonnet/Opus IDs
package/dist/browser.js CHANGED
@@ -1,26 +1,26 @@
1
- "use strict";var CodingTab=(()=>{var v=Object.defineProperty;var A=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var j=Object.prototype.hasOwnProperty;var N=(i,o)=>{for(var c in o)v(i,c,{get:o[c],enumerable:!0})},B=(i,o,c,a)=>{if(o&&typeof o=="object"||typeof o=="function")for(let m of U(o))!j.call(i,m)&&m!==c&&v(i,m,{get:()=>o[m],enumerable:!(a=A(o,m))||a.enumerable});return i};var G=i=>B(v({},"__esModule",{value:!0}),i);var K={};N(K,{mountCodingTab:()=>w});var D='<svg width="18" height="18" viewBox="0 0 16 16" fill="currentColor"><path d="M8 0C3.58 0 0 3.58 0 8a8 8 0 005.47 7.59c.4.07.55-.17.55-.38v-1.34c-2.23.48-2.7-1.07-2.7-1.07-.36-.92-.89-1.16-.89-1.16-.73-.5.05-.49.05-.49.81.06 1.23.83 1.23.83.72 1.23 1.88.87 2.34.66.07-.52.28-.87.5-1.07-1.78-.2-3.65-.89-3.65-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.13 0 0 .67-.21 2.2.82a7.65 7.65 0 014 0c1.53-1.04 2.2-.82 2.2-.82.44 1.11.16 1.93.08 2.13.51.56.82 1.28.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.74.54 1.49v2.21c0 .21.15.46.55.38A8 8 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>';function g(i){return i.replace(/[&<>"']/g,o=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[o])}function J(i){return g(i).replace(/`([^`\n]+)`/g,'<code style="background:rgba(255,255,255,0.06);padding:1px 4px;border-radius:3px;font-family:ui-monospace,monospace;font-size:0.9em">$1</code>').replace(/\*\*([^*\n]+)\*\*/g,"<strong>$1</strong>")}function w(i,o){let c=o.apiBase.replace(/\/$/,""),a=document.createElement("div");a.className="coding-tab",i.innerHTML="",i.appendChild(a);let m=!1;function M(){if(m||(m=!0,document.querySelector('link[data-coding-tab="style"]')))return;let e=document.createElement("link");e.rel="stylesheet",e.href=`${c}/style.css`,e.dataset.codingTab="style",document.head.appendChild(e)}M();let t={me:null,models:[],mode:o.defaultMode??"plan",model:o.defaultModel??"sonnet",repoUrl:o.defaultRepo??"",sessionId:null,activeRunId:null,turns:[],isStreaming:!1},p=null;async function y(e,n){let s=await fetch(`${c}${e}`,{credentials:"include",headers:{"Content-Type":"application/json",...n?.headers??{}},...n});if(!s.ok)throw Object.assign(new Error(`${s.status} ${s.statusText}`),{status:s.status});return await s.json()}function l(){if(!t.me){a.innerHTML=`
1
+ "use strict";var CodingTab=(()=>{var v=Object.defineProperty;var j=Object.getOwnPropertyDescriptor;var N=Object.getOwnPropertyNames;var B=Object.prototype.hasOwnProperty;var G=(i,o)=>{for(var u in o)v(i,u,{get:o[u],enumerable:!0})},D=(i,o,u,a)=>{if(o&&typeof o=="object"||typeof o=="function")for(let p of N(o))!B.call(i,p)&&p!==u&&v(i,p,{get:()=>o[p],enumerable:!(a=j(o,p))||a.enumerable});return i};var J=i=>D(v({},"__esModule",{value:!0}),i);var z={};G(z,{mountCodingTab:()=>w});var T='<svg width="18" height="18" viewBox="0 0 16 16" fill="currentColor"><path d="M8 0C3.58 0 0 3.58 0 8a8 8 0 005.47 7.59c.4.07.55-.17.55-.38v-1.34c-2.23.48-2.7-1.07-2.7-1.07-.36-.92-.89-1.16-.89-1.16-.73-.5.05-.49.05-.49.81.06 1.23.83 1.23.83.72 1.23 1.88.87 2.34.66.07-.52.28-.87.5-1.07-1.78-.2-3.65-.89-3.65-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.13 0 0 .67-.21 2.2.82a7.65 7.65 0 014 0c1.53-1.04 2.2-.82 2.2-.82.44 1.11.16 1.93.08 2.13.51.56.82 1.28.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.74.54 1.49v2.21c0 .21.15.46.55.38A8 8 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>';function d(i){return i.replace(/[&<>"']/g,o=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[o])}function K(i){return d(i).replace(/`([^`\n]+)`/g,'<code style="background:rgba(255,255,255,0.06);padding:1px 4px;border-radius:3px;font-family:ui-monospace,monospace;font-size:0.9em">$1</code>').replace(/\*\*([^*\n]+)\*\*/g,"<strong>$1</strong>")}function w(i,o){let u=o.apiBase.replace(/\/$/,""),a=document.createElement("div");a.className="coding-tab",i.innerHTML="",i.appendChild(a);let p=!1;function M(){if(p||(p=!0,document.querySelector('link[data-coding-tab="style"]')))return;let e=document.createElement("link");e.rel="stylesheet",e.href=`${u}/style.css`,e.dataset.codingTab="style",document.head.appendChild(e)}M();let t={me:null,models:[],mode:o.defaultMode??"plan",model:o.defaultModel??"sonnet",repoUrl:o.defaultRepo??"",repoLocked:!1,sessionId:null,activeRunId:null,turns:[],isStreaming:!1};function x(e){let n=e.match(/github\.com[/:]([^/]+)\/([^/]+?)(?:\.git)?\/?$/);return n?`${n[1]}/${n[2]}`:e}let m=null;async function y(e,n){let s=await fetch(`${u}${e}`,{credentials:"include",headers:{"Content-Type":"application/json",...n?.headers??{}},...n});if(!s.ok)throw Object.assign(new Error(`${s.status} ${s.statusText}`),{status:s.status});return await s.json()}function c(){if(!t.me){a.innerHTML=`
2
2
  <div class="coding-tab__signin">
3
3
  <h2>Coding Tab</h2>
4
4
  <p>Sign in with GitHub to start a coding session against your repo. Your token is used to clone, push, and open pull requests on your behalf.</p>
5
- <a href="${c}/auth/login">${D}<span>Sign in with GitHub</span></a>
5
+ <a href="${u}/auth/login">${T}<span>Sign in with GitHub</span></a>
6
6
  </div>
7
7
  `;return}let e=`
8
8
  <div class="coding-tab__header">
9
9
  <select data-role="model">
10
- ${t.models.map(u=>`<option value="${u.choice}" ${t.model===u.choice?"selected":""}>${g(u.displayName)}</option>`).join("")}
10
+ ${t.models.map(g=>`<option value="${g.choice}" ${t.model===g.choice?"selected":""}>${d(g.displayName)}</option>`).join("")}
11
11
  </select>
12
12
  <div class="coding-tab__mode">
13
13
  <button data-mode="plan" class="${t.mode==="plan"?"is-active":""}">Plan</button>
14
14
  <button data-mode="agent" class="${t.mode==="agent"?"is-active":""}">Agent</button>
15
15
  </div>
16
- <input data-role="repo" placeholder="https://github.com/org/repo" value="${g(t.repoUrl)}" style="flex:1;min-width:200px" />
16
+ ${t.repoLocked&&t.repoUrl?`<a class="coding-tab__repo-locked" href="${d(t.repoUrl)}" target="_blank" rel="noreferrer" title="Locked to this app's repo (set by the server)">${T}<span>${d(x(t.repoUrl))}</span></a>`:`<input data-role="repo" placeholder="https://github.com/org/repo" value="${d(t.repoUrl)}" style="flex:1;min-width:200px" />`}
17
17
  <div class="coding-tab__user">
18
18
  ${t.me.avatarUrl?`<img src="${t.me.avatarUrl}" alt="" />`:""}
19
- <span>@${g(t.me.githubLogin)}</span>
19
+ <span>@${d(t.me.githubLogin)}</span>
20
20
  <button data-role="logout">Sign out</button>
21
21
  </div>
22
22
  </div>
23
- `,n=t.turns.length===0?'<div class="coding-tab__notice info">Pick a mode (Plan or Agent), choose a model, and type a request below to get started.</div>':t.turns.map(x).join(""),s=t.mode==="agent",r=t.turns.length===0?'Ask anything (e.g. "add dark mode that follows the OS setting")':t.mode==="plan"?"Refine the plan, or ask another planning question":"Send another instruction";a.innerHTML=`
23
+ `,n=t.turns.length===0?'<div class="coding-tab__notice info">Pick a mode (Plan or Agent), choose a model, and type a request below to get started.</div>':t.turns.map(E).join(""),s=t.mode==="agent",r=t.turns.length===0?'Ask anything (e.g. "add dark mode that follows the OS setting")':t.mode==="plan"?"Refine the plan, or ask another planning question":"Send another instruction";a.innerHTML=`
24
24
  ${e}
25
25
  <div class="coding-tab__thread" data-role="thread">${n}</div>
26
26
  <div class="coding-tab__composer">
@@ -28,33 +28,33 @@
28
28
  <button class="coding-tab__btn primary" data-role="send" ${t.isStreaming?"disabled":""}>${t.isStreaming?"Working\u2026":"Send"}</button>
29
29
  ${t.isStreaming?'<button class="coding-tab__btn danger" data-role="cancel">Stop</button>':""}
30
30
  </div>
31
- `;let d=a.querySelector("[data-role=thread]");d&&(d.scrollTop=d.scrollHeight),E()}function x(e){if(e.role==="user")return`<div class="coding-tab__msg user">
31
+ `;let l=a.querySelector("[data-role=thread]");l&&(l.scrollTop=l.scrollHeight),C()}function E(e){if(e.role==="user")return`<div class="coding-tab__msg user">
32
32
  <div class="coding-tab__msg-role">You \xB7 ${e.isPlan?"Plan":"Agent"}</div>
33
- <div class="coding-tab__msg-body">${g(e.text)}</div>
33
+ <div class="coding-tab__msg-body">${d(e.text)}</div>
34
34
  </div>`;let n=e.tools.length===0?"":`
35
35
  <div class="coding-tab__msg-tools">
36
- ${e.tools.map(u=>`<div class="coding-tab__tool" data-status="${u.status}"><span class="dot"></span><span>${g(u.name)}</span></div>`).join("")}
37
- </div>`,s=e.status&&e.status!=="finished"?`<div class="coding-tab__status">${g(e.status)}</div>`:"",r=e.showExecute&&!t.isStreaming?`
36
+ ${e.tools.map(g=>`<div class="coding-tab__tool" data-status="${g.status}"><span class="dot"></span><span>${d(g.name)}</span></div>`).join("")}
37
+ </div>`,s=e.status&&e.status!=="finished"?`<div class="coding-tab__status">${d(e.status)}</div>`:"",r=e.showExecute&&!t.isStreaming?`
38
38
  <div class="coding-tab__plan-actions">
39
39
  <button class="coding-tab__btn primary" data-role="execute" data-turn="${e.id}">Execute plan</button>
40
- </div>`:"",d=e.pr?k(e.pr):"";return`<div class="coding-tab__msg assistant">
40
+ </div>`:"",l=e.pr?I(e.pr):"";return`<div class="coding-tab__msg assistant">
41
41
  <div class="coding-tab__msg-role">Coding Tab \xB7 ${e.isPlan?"Plan":"Agent"} ${s}</div>
42
- <div class="coding-tab__msg-body">${J(e.text||(e.tools.length>0?"":"\u2026"))}${n}${r}${d}</div>
43
- </div>`}function k(e){return`<div class="coding-tab__pr">
44
- <div class="coding-tab__pr-title">PR #${e.number} opened in ${g(e.owner)}/${g(e.repo)}</div>
45
- ${e.title?`<div>${g(e.title)}</div>`:""}
42
+ <div class="coding-tab__msg-body">${K(e.text||(e.tools.length>0?"":"\u2026"))}${n}${r}${l}</div>
43
+ </div>`}function I(e){return`<div class="coding-tab__pr">
44
+ <div class="coding-tab__pr-title">PR #${e.number} opened in ${d(e.owner)}/${d(e.repo)}</div>
45
+ ${e.title?`<div>${d(e.title)}</div>`:""}
46
46
  <div style="display:flex;gap:8px;flex-wrap:wrap">
47
47
  <a class="coding-tab__btn" href="${e.url}" target="_blank" rel="noreferrer">Open in GitHub</a>
48
48
  <button class="coding-tab__btn primary" data-role="merge" data-pr="${e.url}">Merge & Redeploy</button>
49
49
  </div>
50
- </div>`}function E(){a.querySelector('[data-role="model"]')?.addEventListener("change",e=>{t.model=e.target.value}),a.querySelectorAll("[data-mode]").forEach(e=>{e.addEventListener("click",()=>{t.mode=e.dataset.mode,l()})}),a.querySelector('[data-role="repo"]')?.addEventListener("change",e=>{t.repoUrl=e.target.value.trim()}),a.querySelector('[data-role="logout"]')?.addEventListener("click",async()=>{await fetch(`${c}/auth/logout`,{method:"POST",credentials:"include"}).catch(()=>{}),t.me=null,t.sessionId=null,t.turns=[],l()}),a.querySelector('[data-role="send"]')?.addEventListener("click",()=>$()),a.querySelector('[data-role="prompt"]')?.addEventListener("keydown",e=>{let n=e;n.key==="Enter"&&(n.metaKey||n.ctrlKey)&&(n.preventDefault(),$())}),a.querySelector('[data-role="cancel"]')?.addEventListener("click",()=>C()),a.querySelectorAll('[data-role="execute"]').forEach(e=>e.addEventListener("click",()=>I())),a.querySelectorAll('[data-role="merge"]').forEach(e=>{e.addEventListener("click",()=>P(e.dataset.pr))})}async function $(){let e=a.querySelector('[data-role="prompt"]'),n=e?.value.trim()??"";if(!n||t.isStreaming)return;e&&(e.value="");let s={id:h(),role:"user",text:n,tools:[],isPlan:t.mode==="plan"};t.turns.push(s);let r={id:h(),role:"assistant",text:"",tools:[],isPlan:t.mode==="plan",status:"running"};t.turns.push(r),t.isStreaming=!0,l();try{let d=!t.sessionId,u=d?"/agent/start":"/agent/send",f=d?{prompt:n,mode:t.mode,model:t.model,repoUrl:t.repoUrl||void 0,startingRef:o.defaultRef}:{prompt:n,mode:t.mode,sessionId:t.sessionId};await S(u,f,r)}catch(d){r.text+=`
50
+ </div>`}function C(){a.querySelector('[data-role="model"]')?.addEventListener("change",e=>{t.model=e.target.value}),a.querySelectorAll("[data-mode]").forEach(e=>{e.addEventListener("click",()=>{t.mode=e.dataset.mode,c()})}),a.querySelector('[data-role="repo"]')?.addEventListener("change",e=>{t.repoUrl=e.target.value.trim()}),a.querySelector('[data-role="logout"]')?.addEventListener("click",async()=>{await fetch(`${u}/auth/logout`,{method:"POST",credentials:"include"}).catch(()=>{}),t.me=null,t.sessionId=null,t.turns=[],c()}),a.querySelector('[data-role="send"]')?.addEventListener("click",()=>$()),a.querySelector('[data-role="prompt"]')?.addEventListener("keydown",e=>{let n=e;n.key==="Enter"&&(n.metaKey||n.ctrlKey)&&(n.preventDefault(),$())}),a.querySelector('[data-role="cancel"]')?.addEventListener("click",()=>P()),a.querySelectorAll('[data-role="execute"]').forEach(e=>e.addEventListener("click",()=>L())),a.querySelectorAll('[data-role="merge"]').forEach(e=>{e.addEventListener("click",()=>R(e.dataset.pr))})}async function $(){let e=a.querySelector('[data-role="prompt"]'),n=e?.value.trim()??"";if(!n||t.isStreaming)return;e&&(e.value="");let s={id:h(),role:"user",text:n,tools:[],isPlan:t.mode==="plan"};t.turns.push(s);let r={id:h(),role:"assistant",text:"",tools:[],isPlan:t.mode==="plan",status:"running"};t.turns.push(r),t.isStreaming=!0,c();try{let l=!t.sessionId,g=l?"/agent/start":"/agent/send",f=l?{prompt:n,mode:t.mode,model:t.model,repoUrl:t.repoUrl||void 0,startingRef:o.defaultRef}:{prompt:n,mode:t.mode,sessionId:t.sessionId};await S(g,f,r)}catch(l){r.text+=`
51
51
 
52
- [error] ${d instanceof Error?d.message:String(d)}`,r.status="error"}finally{t.isStreaming=!1,p=null,l()}}async function I(){if(!t.sessionId||t.isStreaming)return;let e={id:h(),role:"assistant",text:"",tools:[],isPlan:!1,status:"running"};t.turns.push(e),t.isStreaming=!0,l();try{await S("/agent/execute",{sessionId:t.sessionId},e)}catch(n){e.text+=`
52
+ [error] ${l instanceof Error?l.message:String(l)}`,r.status="error"}finally{t.isStreaming=!1,m=null,c()}}async function L(){if(!t.sessionId||t.isStreaming)return;let e={id:h(),role:"assistant",text:"",tools:[],isPlan:!1,status:"running"};t.turns.push(e),t.isStreaming=!0,c();try{await S("/agent/execute",{sessionId:t.sessionId},e)}catch(n){e.text+=`
53
53
 
54
- [error] ${n instanceof Error?n.message:String(n)}`,e.status="error"}finally{t.isStreaming=!1,p=null,l()}}async function C(){if(!t.sessionId||!t.activeRunId){p?.abort();return}try{await y("/agent/cancel",{method:"POST",body:JSON.stringify({sessionId:t.sessionId,runId:t.activeRunId})})}catch(e){console.error("[coding-tab] cancel failed",e)}p?.abort()}async function P(e){try{let n=await y("/pr/merge",{method:"POST",body:JSON.stringify({prUrl:e,mergeMethod:"squash"})}),s={id:h(),role:"assistant",text:n.merged?`Merged commit \`${n.sha.slice(0,7)}\` into the default branch. Railway should redeploy on the next push hook.`:"Merge call returned, but `merged=false`. Check the PR on GitHub.",tools:[],isPlan:!1,status:n.merged?"finished":"error"};t.turns.push(s)}catch(n){let s={id:h(),role:"assistant",text:`[merge failed] ${n instanceof Error?n.message:String(n)}`,tools:[],isPlan:!1,status:"error"};t.turns.push(s)}finally{l()}}async function S(e,n,s){p=new AbortController;let r=await fetch(`${c}${e}`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json",Accept:"text/event-stream"},body:JSON.stringify(n),signal:p.signal});if(!r.ok||!r.body)throw new Error(`${r.status} ${r.statusText}`);let d=r.body.getReader(),u=new TextDecoder,f="";for(;;){let{value:L,done:q}=await d.read();if(q)break;f+=u.decode(L,{stream:!0});let _=f.split(`
54
+ [error] ${n instanceof Error?n.message:String(n)}`,e.status="error"}finally{t.isStreaming=!1,m=null,c()}}async function P(){if(!t.sessionId||!t.activeRunId){m?.abort();return}try{await y("/agent/cancel",{method:"POST",body:JSON.stringify({sessionId:t.sessionId,runId:t.activeRunId})})}catch(e){console.error("[coding-tab] cancel failed",e)}m?.abort()}async function R(e){try{let n=await y("/pr/merge",{method:"POST",body:JSON.stringify({prUrl:e,mergeMethod:"squash"})}),s={id:h(),role:"assistant",text:n.merged?`Merged commit \`${n.sha.slice(0,7)}\` into the default branch. Railway should redeploy on the next push hook.`:"Merge call returned, but `merged=false`. Check the PR on GitHub.",tools:[],isPlan:!1,status:n.merged?"finished":"error"};t.turns.push(s)}catch(n){let s={id:h(),role:"assistant",text:`[merge failed] ${n instanceof Error?n.message:String(n)}`,tools:[],isPlan:!1,status:"error"};t.turns.push(s)}finally{c()}}async function S(e,n,s){m=new AbortController;let r=await fetch(`${u}${e}`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json",Accept:"text/event-stream"},body:JSON.stringify(n),signal:m.signal});if(!r.ok||!r.body)throw new Error(`${r.status} ${r.statusText}`);let l=r.body.getReader(),g=new TextDecoder,f="";for(;;){let{value:q,done:O}=await l.read();if(O)break;f+=g.decode(q,{stream:!0});let _=f.split(`
55
55
 
56
- `);f=_.pop()??"";for(let O of _){let T=O.split(`
57
- `).find(b=>b.startsWith("data: "));if(T)try{let b=JSON.parse(T.slice(6));H(b,s),l()}catch(b){console.warn("[coding-tab] bad sse event",b)}}}}function H(e,n){switch(e.kind){case"ready":e.sessionId&&(t.sessionId=e.sessionId),t.activeRunId=e.runId;break;case"text":n.text+=e.text;break;case"thinking":break;case"tool":{let s=n.tools.find(r=>r.id===e.callId);s?s.status=e.status:n.tools.push({id:e.callId,name:e.name,status:e.status});break}case"status":break;case"result":n.status=e.status,e.pr&&(n.pr=e.pr),n.isPlan&&e.status==="finished"&&(n.showExecute=!0),t.activeRunId=null;break;case"error":n.text+=`
56
+ `);f=_.pop()??"";for(let A of _){let k=A.split(`
57
+ `).find(b=>b.startsWith("data: "));if(k)try{let b=JSON.parse(k.slice(6));H(b,s),c()}catch(b){console.warn("[coding-tab] bad sse event",b)}}}}function H(e,n){switch(e.kind){case"ready":e.sessionId&&(t.sessionId=e.sessionId),t.activeRunId=e.runId;break;case"text":n.text+=e.text;break;case"thinking":break;case"tool":{let s=n.tools.find(r=>r.id===e.callId);s?s.status=e.status:n.tools.push({id:e.callId,name:e.name,status:e.status});break}case"status":break;case"result":n.status=e.status,e.pr&&(n.pr=e.pr),n.isPlan&&e.status==="finished"&&(n.showExecute=!0),t.activeRunId=null;break;case"error":n.text+=`
58
58
 
59
- [error] ${e.message}`,n.status="error",t.activeRunId=null;break}}function h(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():Math.random().toString(36).slice(2)}async function R(){try{let e=await y("/auth/me");t.me=e}catch(e){let n=e.status;if(n===401||n===403){t.me=null,l();return}console.error("[coding-tab] /auth/me failed",e),t.me=null,l();return}try{let{models:e}=await y("/models");t.models=e,e.length>0&&!e.find(n=>n.choice===t.model)&&(t.model=e[0].choice)}catch(e){console.error("[coding-tab] /models failed",e),t.models=[{choice:"sonnet",cursorModelId:"auto",displayName:"Sonnet (fallback)"},{choice:"opus",cursorModelId:"auto",displayName:"Opus (fallback)"}]}l()}return R(),{destroy(){p?.abort(),i.removeChild(a)}}}typeof window<"u"&&(window.CodingTab={mountCodingTab:w});return G(K);})();
59
+ [error] ${e.message}`,n.status="error",t.activeRunId=null;break}}function h(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():Math.random().toString(36).slice(2)}async function U(){try{let e=await y("/auth/me");t.me=e,e.defaultRepoUrl&&(t.repoUrl=e.defaultRepoUrl,t.repoLocked=!0)}catch(e){let n=e.status;if(n===401||n===403){t.me=null,c();return}console.error("[coding-tab] /auth/me failed",e),t.me=null,c();return}try{let{models:e}=await y("/models");t.models=e,e.length>0&&!e.find(n=>n.choice===t.model)&&(t.model=e[0].choice)}catch(e){console.error("[coding-tab] /models failed",e),t.models=[{choice:"sonnet",cursorModelId:"auto",displayName:"Sonnet (fallback)"},{choice:"opus",cursorModelId:"auto",displayName:"Opus (fallback)"}]}c()}return U(),{destroy(){m?.abort(),i.removeChild(a)}}}typeof window<"u"&&(window.CodingTab={mountCodingTab:w});return J(z);})();
60
60
  //# sourceMappingURL=browser.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client/tab.ts"],"sourcesContent":["import type {\n ChatMode,\n MeResponse,\n ModelChoice,\n ModelOption,\n PrInfo,\n StreamEvent,\n} from \"../shared/types.js\";\n\ninterface MountOptions {\n apiBase: string;\n defaultRepo?: string;\n defaultRef?: string;\n defaultMode?: ChatMode;\n defaultModel?: ModelChoice;\n}\n\ninterface MountHandle {\n destroy(): void;\n}\n\ninterface ChatTurn {\n id: string;\n role: \"user\" | \"assistant\";\n text: string;\n tools: Array<{ id: string; name: string; status: string }>;\n pr?: PrInfo;\n isPlan: boolean;\n status?: \"running\" | \"finished\" | \"error\" | \"cancelled\";\n showExecute?: boolean;\n}\n\nconst ICON_GITHUB = `<svg width=\"18\" height=\"18\" viewBox=\"0 0 16 16\" fill=\"currentColor\"><path d=\"M8 0C3.58 0 0 3.58 0 8a8 8 0 005.47 7.59c.4.07.55-.17.55-.38v-1.34c-2.23.48-2.7-1.07-2.7-1.07-.36-.92-.89-1.16-.89-1.16-.73-.5.05-.49.05-.49.81.06 1.23.83 1.23.83.72 1.23 1.88.87 2.34.66.07-.52.28-.87.5-1.07-1.78-.2-3.65-.89-3.65-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.13 0 0 .67-.21 2.2.82a7.65 7.65 0 014 0c1.53-1.04 2.2-.82 2.2-.82.44 1.11.16 1.93.08 2.13.51.56.82 1.28.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.74.54 1.49v2.21c0 .21.15.46.55.38A8 8 0 0016 8c0-4.42-3.58-8-8-8z\"/></svg>`;\n\nfunction escapeHtml(s: string): string {\n return s.replace(/[&<>\"']/g, (c) => ({ \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\", \"'\": \"&#39;\" }[c] as string));\n}\n\nfunction renderMarkdownLite(text: string): string {\n const escaped = escapeHtml(text);\n return escaped\n .replace(/`([^`\\n]+)`/g, '<code style=\"background:rgba(255,255,255,0.06);padding:1px 4px;border-radius:3px;font-family:ui-monospace,monospace;font-size:0.9em\">$1</code>')\n .replace(/\\*\\*([^*\\n]+)\\*\\*/g, \"<strong>$1</strong>\");\n}\n\nexport function mountCodingTab(el: HTMLElement, options: MountOptions): MountHandle {\n const apiBase = options.apiBase.replace(/\\/$/, \"\");\n const root = document.createElement(\"div\");\n root.className = \"coding-tab\";\n el.innerHTML = \"\";\n el.appendChild(root);\n\n let stylesheetInjected = false;\n function injectStylesheet() {\n if (stylesheetInjected) return;\n stylesheetInjected = true;\n if (document.querySelector(`link[data-coding-tab=\"style\"]`)) return;\n const link = document.createElement(\"link\");\n link.rel = \"stylesheet\";\n link.href = `${apiBase}/style.css`;\n link.dataset.codingTab = \"style\";\n document.head.appendChild(link);\n }\n injectStylesheet();\n\n const state: {\n me: MeResponse | null;\n models: ModelOption[];\n mode: ChatMode;\n model: ModelChoice;\n repoUrl: string;\n sessionId: string | null;\n activeRunId: string | null;\n turns: ChatTurn[];\n isStreaming: boolean;\n } = {\n me: null,\n models: [],\n mode: options.defaultMode ?? \"plan\",\n model: options.defaultModel ?? \"sonnet\",\n repoUrl: options.defaultRepo ?? \"\",\n sessionId: null,\n activeRunId: null,\n turns: [],\n isStreaming: false,\n };\n\n let activeAbort: AbortController | null = null;\n\n async function api<T = unknown>(path: string, init?: RequestInit): Promise<T> {\n const resp = await fetch(`${apiBase}${path}`, {\n credentials: \"include\",\n headers: { \"Content-Type\": \"application/json\", ...(init?.headers ?? {}) },\n ...init,\n });\n if (!resp.ok) throw Object.assign(new Error(`${resp.status} ${resp.statusText}`), { status: resp.status });\n return (await resp.json()) as T;\n }\n\n function render() {\n if (!state.me) {\n root.innerHTML = `\n <div class=\"coding-tab__signin\">\n <h2>Coding Tab</h2>\n <p>Sign in with GitHub to start a coding session against your repo. Your token is used to clone, push, and open pull requests on your behalf.</p>\n <a href=\"${apiBase}/auth/login\">${ICON_GITHUB}<span>Sign in with GitHub</span></a>\n </div>\n `;\n return;\n }\n\n const headerHtml = `\n <div class=\"coding-tab__header\">\n <select data-role=\"model\">\n ${state.models.map((m) => `<option value=\"${m.choice}\" ${state.model === m.choice ? \"selected\" : \"\"}>${escapeHtml(m.displayName)}</option>`).join(\"\")}\n </select>\n <div class=\"coding-tab__mode\">\n <button data-mode=\"plan\" class=\"${state.mode === \"plan\" ? \"is-active\" : \"\"}\">Plan</button>\n <button data-mode=\"agent\" class=\"${state.mode === \"agent\" ? \"is-active\" : \"\"}\">Agent</button>\n </div>\n <input data-role=\"repo\" placeholder=\"https://github.com/org/repo\" value=\"${escapeHtml(state.repoUrl)}\" style=\"flex:1;min-width:200px\" />\n <div class=\"coding-tab__user\">\n ${state.me.avatarUrl ? `<img src=\"${state.me.avatarUrl}\" alt=\"\" />` : \"\"}\n <span>@${escapeHtml(state.me.githubLogin)}</span>\n <button data-role=\"logout\">Sign out</button>\n </div>\n </div>\n `;\n\n const threadHtml = state.turns.length === 0\n ? `<div class=\"coding-tab__notice info\">Pick a mode (Plan or Agent), choose a model, and type a request below to get started.</div>`\n : state.turns.map(renderTurn).join(\"\");\n\n const isAgentMode = state.mode === \"agent\";\n const placeholder = state.turns.length === 0\n ? `Ask anything (e.g. \"add dark mode that follows the OS setting\")`\n : state.mode === \"plan\" ? \"Refine the plan, or ask another planning question\" : \"Send another instruction\";\n\n root.innerHTML = `\n ${headerHtml}\n <div class=\"coding-tab__thread\" data-role=\"thread\">${threadHtml}</div>\n <div class=\"coding-tab__composer\">\n <textarea data-role=\"prompt\" placeholder=\"${placeholder}\" rows=\"2\"></textarea>\n <button class=\"coding-tab__btn primary\" data-role=\"send\" ${state.isStreaming ? \"disabled\" : \"\"}>${state.isStreaming ? \"Working…\" : \"Send\"}</button>\n ${state.isStreaming ? `<button class=\"coding-tab__btn danger\" data-role=\"cancel\">Stop</button>` : \"\"}\n </div>\n `;\n\n const thread = root.querySelector(\"[data-role=thread]\") as HTMLElement | null;\n if (thread) thread.scrollTop = thread.scrollHeight;\n\n bindEvents();\n }\n\n function renderTurn(turn: ChatTurn): string {\n if (turn.role === \"user\") {\n return `<div class=\"coding-tab__msg user\">\n <div class=\"coding-tab__msg-role\">You · ${turn.isPlan ? \"Plan\" : \"Agent\"}</div>\n <div class=\"coding-tab__msg-body\">${escapeHtml(turn.text)}</div>\n </div>`;\n }\n const toolsHtml = turn.tools.length === 0 ? \"\" : `\n <div class=\"coding-tab__msg-tools\">\n ${turn.tools.map((t) => `<div class=\"coding-tab__tool\" data-status=\"${t.status}\"><span class=\"dot\"></span><span>${escapeHtml(t.name)}</span></div>`).join(\"\")}\n </div>`;\n const statusBadge = turn.status && turn.status !== \"finished\"\n ? `<div class=\"coding-tab__status\">${escapeHtml(turn.status)}</div>` : \"\";\n const planActions = turn.showExecute && !state.isStreaming ? `\n <div class=\"coding-tab__plan-actions\">\n <button class=\"coding-tab__btn primary\" data-role=\"execute\" data-turn=\"${turn.id}\">Execute plan</button>\n </div>` : \"\";\n const prHtml = turn.pr ? renderPr(turn.pr) : \"\";\n return `<div class=\"coding-tab__msg assistant\">\n <div class=\"coding-tab__msg-role\">Coding Tab · ${turn.isPlan ? \"Plan\" : \"Agent\"} ${statusBadge}</div>\n <div class=\"coding-tab__msg-body\">${renderMarkdownLite(turn.text || (turn.tools.length > 0 ? \"\" : \"…\"))}${toolsHtml}${planActions}${prHtml}</div>\n </div>`;\n }\n\n function renderPr(pr: PrInfo): string {\n return `<div class=\"coding-tab__pr\">\n <div class=\"coding-tab__pr-title\">PR #${pr.number} opened in ${escapeHtml(pr.owner)}/${escapeHtml(pr.repo)}</div>\n ${pr.title ? `<div>${escapeHtml(pr.title)}</div>` : \"\"}\n <div style=\"display:flex;gap:8px;flex-wrap:wrap\">\n <a class=\"coding-tab__btn\" href=\"${pr.url}\" target=\"_blank\" rel=\"noreferrer\">Open in GitHub</a>\n <button class=\"coding-tab__btn primary\" data-role=\"merge\" data-pr=\"${pr.url}\">Merge & Redeploy</button>\n </div>\n </div>`;\n }\n\n function bindEvents() {\n root.querySelector(`[data-role=\"model\"]`)?.addEventListener(\"change\", (e) => {\n state.model = (e.target as HTMLSelectElement).value as ModelChoice;\n });\n root.querySelectorAll(`[data-mode]`).forEach((b) => {\n b.addEventListener(\"click\", () => {\n state.mode = (b as HTMLElement).dataset.mode as ChatMode;\n render();\n });\n });\n root.querySelector(`[data-role=\"repo\"]`)?.addEventListener(\"change\", (e) => {\n state.repoUrl = (e.target as HTMLInputElement).value.trim();\n });\n root.querySelector(`[data-role=\"logout\"]`)?.addEventListener(\"click\", async () => {\n await fetch(`${apiBase}/auth/logout`, { method: \"POST\", credentials: \"include\" }).catch(() => {});\n state.me = null;\n state.sessionId = null;\n state.turns = [];\n render();\n });\n root.querySelector(`[data-role=\"send\"]`)?.addEventListener(\"click\", () => onSend());\n root.querySelector(`[data-role=\"prompt\"]`)?.addEventListener(\"keydown\", (e: Event) => {\n const ev = e as KeyboardEvent;\n if (ev.key === \"Enter\" && (ev.metaKey || ev.ctrlKey)) {\n ev.preventDefault();\n onSend();\n }\n });\n root.querySelector(`[data-role=\"cancel\"]`)?.addEventListener(\"click\", () => onCancel());\n root.querySelectorAll(`[data-role=\"execute\"]`).forEach((b) =>\n b.addEventListener(\"click\", () => onExecute()),\n );\n root.querySelectorAll(`[data-role=\"merge\"]`).forEach((b) => {\n b.addEventListener(\"click\", () => onMerge((b as HTMLElement).dataset.pr!));\n });\n }\n\n async function onSend() {\n const promptEl = root.querySelector(`[data-role=\"prompt\"]`) as HTMLTextAreaElement | null;\n const prompt = promptEl?.value.trim() ?? \"\";\n if (!prompt || state.isStreaming) return;\n if (promptEl) promptEl.value = \"\";\n\n const userTurn: ChatTurn = {\n id: cryptoRandomId(),\n role: \"user\",\n text: prompt,\n tools: [],\n isPlan: state.mode === \"plan\",\n };\n state.turns.push(userTurn);\n\n const assistantTurn: ChatTurn = {\n id: cryptoRandomId(),\n role: \"assistant\",\n text: \"\",\n tools: [],\n isPlan: state.mode === \"plan\",\n status: \"running\",\n };\n state.turns.push(assistantTurn);\n state.isStreaming = true;\n render();\n\n try {\n const isFirst = !state.sessionId;\n const path = isFirst ? \"/agent/start\" : \"/agent/send\";\n const body = isFirst\n ? { prompt, mode: state.mode, model: state.model, repoUrl: state.repoUrl || undefined, startingRef: options.defaultRef }\n : { prompt, mode: state.mode, sessionId: state.sessionId };\n await streamSse(path, body, assistantTurn);\n } catch (err) {\n assistantTurn.text += `\\n\\n[error] ${err instanceof Error ? err.message : String(err)}`;\n assistantTurn.status = \"error\";\n } finally {\n state.isStreaming = false;\n activeAbort = null;\n render();\n }\n }\n\n async function onExecute() {\n if (!state.sessionId || state.isStreaming) return;\n const turn: ChatTurn = {\n id: cryptoRandomId(),\n role: \"assistant\",\n text: \"\",\n tools: [],\n isPlan: false,\n status: \"running\",\n };\n state.turns.push(turn);\n state.isStreaming = true;\n render();\n try {\n await streamSse(\"/agent/execute\", { sessionId: state.sessionId }, turn);\n } catch (err) {\n turn.text += `\\n\\n[error] ${err instanceof Error ? err.message : String(err)}`;\n turn.status = \"error\";\n } finally {\n state.isStreaming = false;\n activeAbort = null;\n render();\n }\n }\n\n async function onCancel() {\n if (!state.sessionId || !state.activeRunId) {\n activeAbort?.abort();\n return;\n }\n try {\n await api(\"/agent/cancel\", {\n method: \"POST\",\n body: JSON.stringify({ sessionId: state.sessionId, runId: state.activeRunId }),\n });\n } catch (err) {\n console.error(\"[coding-tab] cancel failed\", err);\n }\n activeAbort?.abort();\n }\n\n async function onMerge(prUrl: string) {\n try {\n const result = await api<{ sha: string; merged: boolean }>(\"/pr/merge\", {\n method: \"POST\",\n body: JSON.stringify({ prUrl, mergeMethod: \"squash\" }),\n });\n const turn: ChatTurn = {\n id: cryptoRandomId(),\n role: \"assistant\",\n text: result.merged\n ? `Merged commit \\`${result.sha.slice(0, 7)}\\` into the default branch. Railway should redeploy on the next push hook.`\n : `Merge call returned, but \\`merged=false\\`. Check the PR on GitHub.`,\n tools: [],\n isPlan: false,\n status: result.merged ? \"finished\" : \"error\",\n };\n state.turns.push(turn);\n } catch (err) {\n const turn: ChatTurn = {\n id: cryptoRandomId(),\n role: \"assistant\",\n text: `[merge failed] ${err instanceof Error ? err.message : String(err)}`,\n tools: [],\n isPlan: false,\n status: \"error\",\n };\n state.turns.push(turn);\n } finally {\n render();\n }\n }\n\n async function streamSse(path: string, body: unknown, turn: ChatTurn): Promise<void> {\n activeAbort = new AbortController();\n const resp = await fetch(`${apiBase}${path}`, {\n method: \"POST\",\n credentials: \"include\",\n headers: { \"Content-Type\": \"application/json\", Accept: \"text/event-stream\" },\n body: JSON.stringify(body),\n signal: activeAbort.signal,\n });\n if (!resp.ok || !resp.body) {\n throw new Error(`${resp.status} ${resp.statusText}`);\n }\n const reader = resp.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const events = buffer.split(\"\\n\\n\");\n buffer = events.pop() ?? \"\";\n for (const ev of events) {\n const dataLine = ev.split(\"\\n\").find((l) => l.startsWith(\"data: \"));\n if (!dataLine) continue;\n try {\n const evt = JSON.parse(dataLine.slice(6)) as StreamEvent;\n handleStream(evt, turn);\n render();\n } catch (e) {\n console.warn(\"[coding-tab] bad sse event\", e);\n }\n }\n }\n }\n\n function handleStream(evt: StreamEvent, turn: ChatTurn) {\n switch (evt.kind) {\n case \"ready\":\n if (evt.sessionId) state.sessionId = evt.sessionId;\n state.activeRunId = evt.runId;\n break;\n case \"text\":\n turn.text += evt.text;\n break;\n case \"thinking\":\n break;\n case \"tool\": {\n const existing = turn.tools.find((t) => t.id === evt.callId);\n if (existing) {\n existing.status = evt.status;\n } else {\n turn.tools.push({ id: evt.callId, name: evt.name, status: evt.status });\n }\n break;\n }\n case \"status\":\n break;\n case \"result\":\n turn.status = evt.status;\n if (evt.pr) turn.pr = evt.pr;\n if (turn.isPlan && evt.status === \"finished\") turn.showExecute = true;\n state.activeRunId = null;\n break;\n case \"error\":\n turn.text += `\\n\\n[error] ${evt.message}`;\n turn.status = \"error\";\n state.activeRunId = null;\n break;\n }\n }\n\n function cryptoRandomId(): string {\n return (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto)\n ? crypto.randomUUID()\n : Math.random().toString(36).slice(2);\n }\n\n async function bootstrap() {\n try {\n const me = await api<MeResponse>(\"/auth/me\");\n state.me = me;\n } catch (err) {\n const status = (err as { status?: number }).status;\n if (status === 401 || status === 403) {\n state.me = null;\n render();\n return;\n }\n console.error(\"[coding-tab] /auth/me failed\", err);\n state.me = null;\n render();\n return;\n }\n try {\n const { models } = await api<{ models: ModelOption[] }>(\"/models\");\n state.models = models;\n if (models.length > 0 && !models.find((m) => m.choice === state.model)) {\n state.model = models[0]!.choice;\n }\n } catch (err) {\n console.error(\"[coding-tab] /models failed\", err);\n state.models = [\n { choice: \"sonnet\", cursorModelId: \"auto\", displayName: \"Sonnet (fallback)\" },\n { choice: \"opus\", cursorModelId: \"auto\", displayName: \"Opus (fallback)\" },\n ];\n }\n render();\n }\n\n bootstrap();\n\n return {\n destroy() {\n activeAbort?.abort();\n el.removeChild(root);\n },\n };\n}\n\nif (typeof window !== \"undefined\") {\n (window as unknown as { CodingTab?: { mountCodingTab: typeof mountCodingTab } }).CodingTab = { mountCodingTab };\n}\n"],"mappings":"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","names":["tab_exports","__export","mountCodingTab","ICON_GITHUB","escapeHtml","s","c","renderMarkdownLite","text","el","options","apiBase","root","stylesheetInjected","injectStylesheet","link","state","activeAbort","api","path","init","resp","render","headerHtml","m","threadHtml","renderTurn","isAgentMode","placeholder","thread","bindEvents","turn","toolsHtml","t","statusBadge","planActions","prHtml","renderPr","pr","b","onSend","ev","onCancel","onExecute","onMerge","promptEl","prompt","userTurn","cryptoRandomId","assistantTurn","isFirst","body","streamSse","err","prUrl","result","reader","decoder","buffer","value","done","events","dataLine","l","evt","handleStream","e","existing","bootstrap","me","status","models"]}
1
+ {"version":3,"sources":["../src/client/tab.ts"],"sourcesContent":["import type {\n ChatMode,\n MeResponse,\n ModelChoice,\n ModelOption,\n PrInfo,\n StreamEvent,\n} from \"../shared/types.js\";\n\ninterface MountOptions {\n apiBase: string;\n defaultRepo?: string;\n defaultRef?: string;\n defaultMode?: ChatMode;\n defaultModel?: ModelChoice;\n}\n\ninterface MountHandle {\n destroy(): void;\n}\n\ninterface ChatTurn {\n id: string;\n role: \"user\" | \"assistant\";\n text: string;\n tools: Array<{ id: string; name: string; status: string }>;\n pr?: PrInfo;\n isPlan: boolean;\n status?: \"running\" | \"finished\" | \"error\" | \"cancelled\";\n showExecute?: boolean;\n}\n\nconst ICON_GITHUB = `<svg width=\"18\" height=\"18\" viewBox=\"0 0 16 16\" fill=\"currentColor\"><path d=\"M8 0C3.58 0 0 3.58 0 8a8 8 0 005.47 7.59c.4.07.55-.17.55-.38v-1.34c-2.23.48-2.7-1.07-2.7-1.07-.36-.92-.89-1.16-.89-1.16-.73-.5.05-.49.05-.49.81.06 1.23.83 1.23.83.72 1.23 1.88.87 2.34.66.07-.52.28-.87.5-1.07-1.78-.2-3.65-.89-3.65-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.13 0 0 .67-.21 2.2.82a7.65 7.65 0 014 0c1.53-1.04 2.2-.82 2.2-.82.44 1.11.16 1.93.08 2.13.51.56.82 1.28.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.74.54 1.49v2.21c0 .21.15.46.55.38A8 8 0 0016 8c0-4.42-3.58-8-8-8z\"/></svg>`;\n\nfunction escapeHtml(s: string): string {\n return s.replace(/[&<>\"']/g, (c) => ({ \"&\": \"&amp;\", \"<\": \"&lt;\", \">\": \"&gt;\", '\"': \"&quot;\", \"'\": \"&#39;\" }[c] as string));\n}\n\nfunction renderMarkdownLite(text: string): string {\n const escaped = escapeHtml(text);\n return escaped\n .replace(/`([^`\\n]+)`/g, '<code style=\"background:rgba(255,255,255,0.06);padding:1px 4px;border-radius:3px;font-family:ui-monospace,monospace;font-size:0.9em\">$1</code>')\n .replace(/\\*\\*([^*\\n]+)\\*\\*/g, \"<strong>$1</strong>\");\n}\n\nexport function mountCodingTab(el: HTMLElement, options: MountOptions): MountHandle {\n const apiBase = options.apiBase.replace(/\\/$/, \"\");\n const root = document.createElement(\"div\");\n root.className = \"coding-tab\";\n el.innerHTML = \"\";\n el.appendChild(root);\n\n let stylesheetInjected = false;\n function injectStylesheet() {\n if (stylesheetInjected) return;\n stylesheetInjected = true;\n if (document.querySelector(`link[data-coding-tab=\"style\"]`)) return;\n const link = document.createElement(\"link\");\n link.rel = \"stylesheet\";\n link.href = `${apiBase}/style.css`;\n link.dataset.codingTab = \"style\";\n document.head.appendChild(link);\n }\n injectStylesheet();\n\n const state: {\n me: MeResponse | null;\n models: ModelOption[];\n mode: ChatMode;\n model: ModelChoice;\n repoUrl: string;\n repoLocked: boolean;\n sessionId: string | null;\n activeRunId: string | null;\n turns: ChatTurn[];\n isStreaming: boolean;\n } = {\n me: null,\n models: [],\n mode: options.defaultMode ?? \"plan\",\n model: options.defaultModel ?? \"sonnet\",\n repoUrl: options.defaultRepo ?? \"\",\n repoLocked: false,\n sessionId: null,\n activeRunId: null,\n turns: [],\n isStreaming: false,\n };\n\n function repoSlug(url: string): string {\n const m = url.match(/github\\.com[/:]([^/]+)\\/([^/]+?)(?:\\.git)?\\/?$/);\n return m ? `${m[1]}/${m[2]}` : url;\n }\n\n let activeAbort: AbortController | null = null;\n\n async function api<T = unknown>(path: string, init?: RequestInit): Promise<T> {\n const resp = await fetch(`${apiBase}${path}`, {\n credentials: \"include\",\n headers: { \"Content-Type\": \"application/json\", ...(init?.headers ?? {}) },\n ...init,\n });\n if (!resp.ok) throw Object.assign(new Error(`${resp.status} ${resp.statusText}`), { status: resp.status });\n return (await resp.json()) as T;\n }\n\n function render() {\n if (!state.me) {\n root.innerHTML = `\n <div class=\"coding-tab__signin\">\n <h2>Coding Tab</h2>\n <p>Sign in with GitHub to start a coding session against your repo. Your token is used to clone, push, and open pull requests on your behalf.</p>\n <a href=\"${apiBase}/auth/login\">${ICON_GITHUB}<span>Sign in with GitHub</span></a>\n </div>\n `;\n return;\n }\n\n const headerHtml = `\n <div class=\"coding-tab__header\">\n <select data-role=\"model\">\n ${state.models.map((m) => `<option value=\"${m.choice}\" ${state.model === m.choice ? \"selected\" : \"\"}>${escapeHtml(m.displayName)}</option>`).join(\"\")}\n </select>\n <div class=\"coding-tab__mode\">\n <button data-mode=\"plan\" class=\"${state.mode === \"plan\" ? \"is-active\" : \"\"}\">Plan</button>\n <button data-mode=\"agent\" class=\"${state.mode === \"agent\" ? \"is-active\" : \"\"}\">Agent</button>\n </div>\n ${state.repoLocked && state.repoUrl\n ? `<a class=\"coding-tab__repo-locked\" href=\"${escapeHtml(state.repoUrl)}\" target=\"_blank\" rel=\"noreferrer\" title=\"Locked to this app's repo (set by the server)\">${ICON_GITHUB}<span>${escapeHtml(repoSlug(state.repoUrl))}</span></a>`\n : `<input data-role=\"repo\" placeholder=\"https://github.com/org/repo\" value=\"${escapeHtml(state.repoUrl)}\" style=\"flex:1;min-width:200px\" />`}\n <div class=\"coding-tab__user\">\n ${state.me.avatarUrl ? `<img src=\"${state.me.avatarUrl}\" alt=\"\" />` : \"\"}\n <span>@${escapeHtml(state.me.githubLogin)}</span>\n <button data-role=\"logout\">Sign out</button>\n </div>\n </div>\n `;\n\n const threadHtml = state.turns.length === 0\n ? `<div class=\"coding-tab__notice info\">Pick a mode (Plan or Agent), choose a model, and type a request below to get started.</div>`\n : state.turns.map(renderTurn).join(\"\");\n\n const isAgentMode = state.mode === \"agent\";\n const placeholder = state.turns.length === 0\n ? `Ask anything (e.g. \"add dark mode that follows the OS setting\")`\n : state.mode === \"plan\" ? \"Refine the plan, or ask another planning question\" : \"Send another instruction\";\n\n root.innerHTML = `\n ${headerHtml}\n <div class=\"coding-tab__thread\" data-role=\"thread\">${threadHtml}</div>\n <div class=\"coding-tab__composer\">\n <textarea data-role=\"prompt\" placeholder=\"${placeholder}\" rows=\"2\"></textarea>\n <button class=\"coding-tab__btn primary\" data-role=\"send\" ${state.isStreaming ? \"disabled\" : \"\"}>${state.isStreaming ? \"Working…\" : \"Send\"}</button>\n ${state.isStreaming ? `<button class=\"coding-tab__btn danger\" data-role=\"cancel\">Stop</button>` : \"\"}\n </div>\n `;\n\n const thread = root.querySelector(\"[data-role=thread]\") as HTMLElement | null;\n if (thread) thread.scrollTop = thread.scrollHeight;\n\n bindEvents();\n }\n\n function renderTurn(turn: ChatTurn): string {\n if (turn.role === \"user\") {\n return `<div class=\"coding-tab__msg user\">\n <div class=\"coding-tab__msg-role\">You · ${turn.isPlan ? \"Plan\" : \"Agent\"}</div>\n <div class=\"coding-tab__msg-body\">${escapeHtml(turn.text)}</div>\n </div>`;\n }\n const toolsHtml = turn.tools.length === 0 ? \"\" : `\n <div class=\"coding-tab__msg-tools\">\n ${turn.tools.map((t) => `<div class=\"coding-tab__tool\" data-status=\"${t.status}\"><span class=\"dot\"></span><span>${escapeHtml(t.name)}</span></div>`).join(\"\")}\n </div>`;\n const statusBadge = turn.status && turn.status !== \"finished\"\n ? `<div class=\"coding-tab__status\">${escapeHtml(turn.status)}</div>` : \"\";\n const planActions = turn.showExecute && !state.isStreaming ? `\n <div class=\"coding-tab__plan-actions\">\n <button class=\"coding-tab__btn primary\" data-role=\"execute\" data-turn=\"${turn.id}\">Execute plan</button>\n </div>` : \"\";\n const prHtml = turn.pr ? renderPr(turn.pr) : \"\";\n return `<div class=\"coding-tab__msg assistant\">\n <div class=\"coding-tab__msg-role\">Coding Tab · ${turn.isPlan ? \"Plan\" : \"Agent\"} ${statusBadge}</div>\n <div class=\"coding-tab__msg-body\">${renderMarkdownLite(turn.text || (turn.tools.length > 0 ? \"\" : \"…\"))}${toolsHtml}${planActions}${prHtml}</div>\n </div>`;\n }\n\n function renderPr(pr: PrInfo): string {\n return `<div class=\"coding-tab__pr\">\n <div class=\"coding-tab__pr-title\">PR #${pr.number} opened in ${escapeHtml(pr.owner)}/${escapeHtml(pr.repo)}</div>\n ${pr.title ? `<div>${escapeHtml(pr.title)}</div>` : \"\"}\n <div style=\"display:flex;gap:8px;flex-wrap:wrap\">\n <a class=\"coding-tab__btn\" href=\"${pr.url}\" target=\"_blank\" rel=\"noreferrer\">Open in GitHub</a>\n <button class=\"coding-tab__btn primary\" data-role=\"merge\" data-pr=\"${pr.url}\">Merge & Redeploy</button>\n </div>\n </div>`;\n }\n\n function bindEvents() {\n root.querySelector(`[data-role=\"model\"]`)?.addEventListener(\"change\", (e) => {\n state.model = (e.target as HTMLSelectElement).value as ModelChoice;\n });\n root.querySelectorAll(`[data-mode]`).forEach((b) => {\n b.addEventListener(\"click\", () => {\n state.mode = (b as HTMLElement).dataset.mode as ChatMode;\n render();\n });\n });\n root.querySelector(`[data-role=\"repo\"]`)?.addEventListener(\"change\", (e) => {\n state.repoUrl = (e.target as HTMLInputElement).value.trim();\n });\n root.querySelector(`[data-role=\"logout\"]`)?.addEventListener(\"click\", async () => {\n await fetch(`${apiBase}/auth/logout`, { method: \"POST\", credentials: \"include\" }).catch(() => {});\n state.me = null;\n state.sessionId = null;\n state.turns = [];\n render();\n });\n root.querySelector(`[data-role=\"send\"]`)?.addEventListener(\"click\", () => onSend());\n root.querySelector(`[data-role=\"prompt\"]`)?.addEventListener(\"keydown\", (e: Event) => {\n const ev = e as KeyboardEvent;\n if (ev.key === \"Enter\" && (ev.metaKey || ev.ctrlKey)) {\n ev.preventDefault();\n onSend();\n }\n });\n root.querySelector(`[data-role=\"cancel\"]`)?.addEventListener(\"click\", () => onCancel());\n root.querySelectorAll(`[data-role=\"execute\"]`).forEach((b) =>\n b.addEventListener(\"click\", () => onExecute()),\n );\n root.querySelectorAll(`[data-role=\"merge\"]`).forEach((b) => {\n b.addEventListener(\"click\", () => onMerge((b as HTMLElement).dataset.pr!));\n });\n }\n\n async function onSend() {\n const promptEl = root.querySelector(`[data-role=\"prompt\"]`) as HTMLTextAreaElement | null;\n const prompt = promptEl?.value.trim() ?? \"\";\n if (!prompt || state.isStreaming) return;\n if (promptEl) promptEl.value = \"\";\n\n const userTurn: ChatTurn = {\n id: cryptoRandomId(),\n role: \"user\",\n text: prompt,\n tools: [],\n isPlan: state.mode === \"plan\",\n };\n state.turns.push(userTurn);\n\n const assistantTurn: ChatTurn = {\n id: cryptoRandomId(),\n role: \"assistant\",\n text: \"\",\n tools: [],\n isPlan: state.mode === \"plan\",\n status: \"running\",\n };\n state.turns.push(assistantTurn);\n state.isStreaming = true;\n render();\n\n try {\n const isFirst = !state.sessionId;\n const path = isFirst ? \"/agent/start\" : \"/agent/send\";\n const body = isFirst\n ? { prompt, mode: state.mode, model: state.model, repoUrl: state.repoUrl || undefined, startingRef: options.defaultRef }\n : { prompt, mode: state.mode, sessionId: state.sessionId };\n await streamSse(path, body, assistantTurn);\n } catch (err) {\n assistantTurn.text += `\\n\\n[error] ${err instanceof Error ? err.message : String(err)}`;\n assistantTurn.status = \"error\";\n } finally {\n state.isStreaming = false;\n activeAbort = null;\n render();\n }\n }\n\n async function onExecute() {\n if (!state.sessionId || state.isStreaming) return;\n const turn: ChatTurn = {\n id: cryptoRandomId(),\n role: \"assistant\",\n text: \"\",\n tools: [],\n isPlan: false,\n status: \"running\",\n };\n state.turns.push(turn);\n state.isStreaming = true;\n render();\n try {\n await streamSse(\"/agent/execute\", { sessionId: state.sessionId }, turn);\n } catch (err) {\n turn.text += `\\n\\n[error] ${err instanceof Error ? err.message : String(err)}`;\n turn.status = \"error\";\n } finally {\n state.isStreaming = false;\n activeAbort = null;\n render();\n }\n }\n\n async function onCancel() {\n if (!state.sessionId || !state.activeRunId) {\n activeAbort?.abort();\n return;\n }\n try {\n await api(\"/agent/cancel\", {\n method: \"POST\",\n body: JSON.stringify({ sessionId: state.sessionId, runId: state.activeRunId }),\n });\n } catch (err) {\n console.error(\"[coding-tab] cancel failed\", err);\n }\n activeAbort?.abort();\n }\n\n async function onMerge(prUrl: string) {\n try {\n const result = await api<{ sha: string; merged: boolean }>(\"/pr/merge\", {\n method: \"POST\",\n body: JSON.stringify({ prUrl, mergeMethod: \"squash\" }),\n });\n const turn: ChatTurn = {\n id: cryptoRandomId(),\n role: \"assistant\",\n text: result.merged\n ? `Merged commit \\`${result.sha.slice(0, 7)}\\` into the default branch. Railway should redeploy on the next push hook.`\n : `Merge call returned, but \\`merged=false\\`. Check the PR on GitHub.`,\n tools: [],\n isPlan: false,\n status: result.merged ? \"finished\" : \"error\",\n };\n state.turns.push(turn);\n } catch (err) {\n const turn: ChatTurn = {\n id: cryptoRandomId(),\n role: \"assistant\",\n text: `[merge failed] ${err instanceof Error ? err.message : String(err)}`,\n tools: [],\n isPlan: false,\n status: \"error\",\n };\n state.turns.push(turn);\n } finally {\n render();\n }\n }\n\n async function streamSse(path: string, body: unknown, turn: ChatTurn): Promise<void> {\n activeAbort = new AbortController();\n const resp = await fetch(`${apiBase}${path}`, {\n method: \"POST\",\n credentials: \"include\",\n headers: { \"Content-Type\": \"application/json\", Accept: \"text/event-stream\" },\n body: JSON.stringify(body),\n signal: activeAbort.signal,\n });\n if (!resp.ok || !resp.body) {\n throw new Error(`${resp.status} ${resp.statusText}`);\n }\n const reader = resp.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const events = buffer.split(\"\\n\\n\");\n buffer = events.pop() ?? \"\";\n for (const ev of events) {\n const dataLine = ev.split(\"\\n\").find((l) => l.startsWith(\"data: \"));\n if (!dataLine) continue;\n try {\n const evt = JSON.parse(dataLine.slice(6)) as StreamEvent;\n handleStream(evt, turn);\n render();\n } catch (e) {\n console.warn(\"[coding-tab] bad sse event\", e);\n }\n }\n }\n }\n\n function handleStream(evt: StreamEvent, turn: ChatTurn) {\n switch (evt.kind) {\n case \"ready\":\n if (evt.sessionId) state.sessionId = evt.sessionId;\n state.activeRunId = evt.runId;\n break;\n case \"text\":\n turn.text += evt.text;\n break;\n case \"thinking\":\n break;\n case \"tool\": {\n const existing = turn.tools.find((t) => t.id === evt.callId);\n if (existing) {\n existing.status = evt.status;\n } else {\n turn.tools.push({ id: evt.callId, name: evt.name, status: evt.status });\n }\n break;\n }\n case \"status\":\n break;\n case \"result\":\n turn.status = evt.status;\n if (evt.pr) turn.pr = evt.pr;\n if (turn.isPlan && evt.status === \"finished\") turn.showExecute = true;\n state.activeRunId = null;\n break;\n case \"error\":\n turn.text += `\\n\\n[error] ${evt.message}`;\n turn.status = \"error\";\n state.activeRunId = null;\n break;\n }\n }\n\n function cryptoRandomId(): string {\n return (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto)\n ? crypto.randomUUID()\n : Math.random().toString(36).slice(2);\n }\n\n async function bootstrap() {\n try {\n const me = await api<MeResponse>(\"/auth/me\");\n state.me = me;\n if (me.defaultRepoUrl) {\n state.repoUrl = me.defaultRepoUrl;\n state.repoLocked = true;\n }\n } catch (err) {\n const status = (err as { status?: number }).status;\n if (status === 401 || status === 403) {\n state.me = null;\n render();\n return;\n }\n console.error(\"[coding-tab] /auth/me failed\", err);\n state.me = null;\n render();\n return;\n }\n try {\n const { models } = await api<{ models: ModelOption[] }>(\"/models\");\n state.models = models;\n if (models.length > 0 && !models.find((m) => m.choice === state.model)) {\n state.model = models[0]!.choice;\n }\n } catch (err) {\n console.error(\"[coding-tab] /models failed\", err);\n state.models = [\n { choice: \"sonnet\", cursorModelId: \"auto\", displayName: \"Sonnet (fallback)\" },\n { choice: \"opus\", cursorModelId: \"auto\", displayName: \"Opus (fallback)\" },\n ];\n }\n render();\n }\n\n bootstrap();\n\n return {\n destroy() {\n activeAbort?.abort();\n el.removeChild(root);\n },\n };\n}\n\nif (typeof window !== \"undefined\") {\n (window as unknown as { CodingTab?: { mountCodingTab: typeof mountCodingTab } }).CodingTab = { mountCodingTab };\n}\n"],"mappings":"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","names":["tab_exports","__export","mountCodingTab","ICON_GITHUB","escapeHtml","s","c","renderMarkdownLite","text","el","options","apiBase","root","stylesheetInjected","injectStylesheet","link","state","repoSlug","url","m","activeAbort","api","path","init","resp","render","headerHtml","threadHtml","renderTurn","isAgentMode","placeholder","thread","bindEvents","turn","toolsHtml","t","statusBadge","planActions","prHtml","renderPr","pr","b","onSend","ev","onCancel","onExecute","onMerge","promptEl","prompt","userTurn","cryptoRandomId","assistantTurn","isFirst","body","streamSse","err","prUrl","result","reader","decoder","buffer","value","done","events","dataLine","l","evt","handleStream","e","existing","bootstrap","me","status","models"]}
package/dist/server.cjs CHANGED
@@ -190,7 +190,12 @@ function makeAuthRouter(opts) {
190
190
  res.status(403).json({ error: "not_authorized" });
191
191
  return;
192
192
  }
193
- res.json({ githubLogin: session.githubLogin, avatarUrl: session.avatarUrl });
193
+ res.json({
194
+ githubLogin: session.githubLogin,
195
+ avatarUrl: session.avatarUrl,
196
+ defaultRepoUrl: opts.defaultRepoUrl,
197
+ defaultRepoRef: opts.defaultRepoRef
198
+ });
194
199
  });
195
200
  return router;
196
201
  }
@@ -561,6 +566,21 @@ function makeGitHubRouter() {
561
566
  const user = req.user;
562
567
  const octokit = new import_rest.Octokit({ auth: user.accessToken });
563
568
  try {
569
+ const pr = await octokit.pulls.get({
570
+ owner: parsed.owner,
571
+ repo: parsed.repo,
572
+ pull_number: parsed.number
573
+ });
574
+ if (pr.data.draft) {
575
+ await octokit.graphql(
576
+ `mutation($id: ID!) {
577
+ markPullRequestReadyForReview(input: { pullRequestId: $id }) {
578
+ pullRequest { id }
579
+ }
580
+ }`,
581
+ { id: pr.data.node_id }
582
+ );
583
+ }
564
584
  const merge = await octokit.pulls.merge({
565
585
  owner: parsed.owner,
566
586
  repo: parsed.repo,
@@ -570,8 +590,9 @@ function makeGitHubRouter() {
570
590
  res.json({ sha: merge.data.sha, merged: merge.data.merged });
571
591
  } catch (err) {
572
592
  const message = err instanceof Error ? err.message : String(err);
593
+ const status = err.status ?? 500;
573
594
  console.error("[coding-tab] /pr/merge failed", err);
574
- res.status(500).json({ error: message });
595
+ res.status(status >= 400 && status < 600 ? status : 500).json({ error: message });
575
596
  }
576
597
  });
577
598
  return router;
@@ -611,7 +632,7 @@ function makeAssetRouter(basePath) {
611
632
  try {
612
633
  const { data } = await readFirst(ASSET_CANDIDATES);
613
634
  res.set("Content-Type", "application/javascript; charset=utf-8");
614
- res.set("Cache-Control", "public, max-age=300");
635
+ res.set("Cache-Control", "public, max-age=0, must-revalidate");
615
636
  res.send(data);
616
637
  } catch (err) {
617
638
  res.status(500).send("// coding-tab browser bundle missing \u2014 did you run `npm run build`?");
@@ -621,7 +642,7 @@ function makeAssetRouter(basePath) {
621
642
  try {
622
643
  const { data } = await readFirst(STYLE_CANDIDATES);
623
644
  res.set("Content-Type", "text/css; charset=utf-8");
624
- res.set("Cache-Control", "public, max-age=300");
645
+ res.set("Cache-Control", "public, max-age=0, must-revalidate");
625
646
  res.send(data);
626
647
  } catch {
627
648
  res.status(404).send("/* style.css missing */");
@@ -681,7 +702,9 @@ function mountCodingTab(app, options) {
681
702
  oauth: options.githubOAuth,
682
703
  sessionPassword: options.sessionPassword,
683
704
  secure,
684
- basePath
705
+ basePath,
706
+ defaultRepoUrl: options.defaultRepo.url,
707
+ defaultRepoRef: options.defaultRepo.ref
685
708
  });
686
709
  router.use(authRouter);
687
710
  const requireAuth = makeRequireAuth({
@@ -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/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 { makeGitHubRouter } from \"./githubRoutes.js\";\nimport { makeRequireAuth } from \"./authMiddleware.js\";\nimport { makeAssetRouter } from \"./staticAssets.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\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 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 });\n router.use(authRouter);\n\n const requireAuth = makeRequireAuth({\n sessionPassword: options.sessionPassword,\n secure,\n allowedLogins: options.githubOAuth.allowedLogins,\n });\n\n const agentRouter = makeAgentRouter({\n cursorApiKey: options.cursorApiKey,\n defaultRepo: options.defaultRepo,\n envName: options.envName,\n skipReviewerRequest: options.skipReviewerRequest,\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 {\n ChatMode,\n ModelChoice,\n ModelOption,\n StreamEvent,\n PrInfo,\n StartAgentRequest,\n StartAgentResponse,\n SendMessageRequest,\n SendMessageResponse,\n ExecuteRequest,\n MergeRequest,\n MergeResponse,\n MeResponse,\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}\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({ githubLogin: session.githubLogin, avatarUrl: session.avatarUrl });\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 { Agent, CursorAgentError, type Run, type SDKMessage } from \"@cursor/sdk\";\nimport type { AuthenticatedRequest } from \"./authMiddleware.js\";\nimport { listAvailableModels, resolveModel } from \"./models.js\";\nimport {\n disposeSession,\n getSession,\n registerSession,\n} from \"./sessions.js\";\nimport type { ChatMode, PrInfo, StreamEvent } 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}\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\nasync function streamRun(res: Response, run: Run): Promise<void> {\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 for (const block of message.message.content) {\n if (block.type === \"text\" && block.text) sse(res, { kind: \"text\", text: block.text });\n else if (block.type === \"tool_use\") sse(res, { kind: \"tool\", name: block.name, status: \"running\", callId: block.id, args: block.input });\n }\n } else if (message.type === \"thinking\") {\n sse(res, { kind: \"thinking\", text: message.text });\n } else if (message.type === \"tool_call\") {\n sse(res, { kind: \"tool\", name: message.name, status: message.status, callId: message.call_id, args: message.args, result: message.result });\n } else if (message.type === \"status\") {\n sse(res, { kind: \"status\", status: message.status, message: message.message });\n }\n }\n const result = await run.wait();\n const prUrl = result.git?.branches?.find((b) => b.prUrl)?.prUrl;\n sse(res, {\n kind: \"result\",\n status: result.status,\n pr: parsePrUrl(prUrl),\n durationMs: result.durationMs,\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 sse(res, { kind: \"error\", message, retryable });\n } finally {\n if (!res.writableEnded) res.end();\n }\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 router.post(\"/agent/start\", async (req: Request, res: Response) => {\n const user = (req as AuthenticatedRequest).user;\n const { prompt, mode, model, repoUrl, startingRef } = (req.body ?? {}) as {\n prompt?: string;\n mode?: ChatMode;\n model?: \"sonnet\" | \"opus\";\n repoUrl?: string;\n startingRef?: string;\n };\n if (!prompt || (mode !== \"plan\" && mode !== \"agent\") || (model !== \"sonnet\" && model !== \"opus\")) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n\n setupSseHeaders(res);\n\n let agent;\n try {\n const resolved = await resolveModel(opts.cursorApiKey, model);\n agent = await Agent.create({\n apiKey: opts.cursorApiKey,\n model: { id: resolved.cursorModelId },\n cloud: {\n repos: [\n {\n url: repoUrl ?? opts.defaultRepo.url,\n startingRef: startingRef ?? opts.defaultRepo.ref,\n },\n ],\n autoCreatePR: 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 const session = registerSession({\n githubLogin: user.githubLogin,\n agent,\n repoUrl: repoUrl ?? opts.defaultRepo.url,\n startingRef: startingRef ?? opts.defaultRepo.ref,\n });\n\n const run = await agent.send(modeInstructionPrefix(mode, prompt));\n sse(res, { kind: \"ready\", sessionId: session.id, agentId: agent.agentId, runId: run.id });\n console.log(`[coding-tab] start agent=${agent.agentId} run=${run.id} session=${session.id} login=${user.githubLogin}`);\n await streamRun(res, run);\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/start failed\", err);\n sse(res, { kind: \"error\", message, retryable });\n if (!res.writableEnded) res.end();\n if (agent) await agent[Symbol.asyncDispose]().catch(() => {});\n }\n\n req.on(\"close\", () => {\n if (!res.writableEnded) res.end();\n });\n });\n\n router.post(\"/agent/send\", async (req: Request, res: Response) => {\n const { sessionId, prompt, mode } = (req.body ?? {}) as {\n sessionId?: string;\n prompt?: string;\n mode?: ChatMode;\n };\n if (!sessionId || !prompt || (mode !== \"plan\" && mode !== \"agent\")) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n const session = getSession(sessionId);\n if (!session) {\n res.status(404).json({ error: \"session_not_found\" });\n return;\n }\n\n setupSseHeaders(res);\n\n try {\n const run = await session.agent.send(modeInstructionPrefix(mode, prompt));\n sse(res, { kind: \"ready\", sessionId: session.id, agentId: session.agent.agentId, runId: run.id });\n console.log(`[coding-tab] send agent=${session.agent.agentId} run=${run.id} session=${session.id}`);\n await streamRun(res, run);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(\"[coding-tab] /agent/send failed\", err);\n sse(res, { kind: \"error\", message });\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/execute\", async (req: Request, res: Response) => {\n const { sessionId } = (req.body ?? {}) as { sessionId?: string };\n if (!sessionId) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n const session = getSession(sessionId);\n if (!session) {\n res.status(404).json({ error: \"session_not_found\" });\n return;\n }\n\n setupSseHeaders(res);\n\n try {\n const run = await session.agent.send(EXECUTE_INSTRUCTION);\n sse(res, { kind: \"ready\", sessionId: session.id, agentId: session.agent.agentId, runId: run.id });\n console.log(`[coding-tab] execute agent=${session.agent.agentId} run=${run.id} session=${session.id}`);\n await streamRun(res, run);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(\"[coding-tab] /agent/execute failed\", err);\n sse(res, { kind: \"error\", message });\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/cancel\", async (req: Request, res: Response) => {\n const { sessionId, runId } = (req.body ?? {}) as { sessionId?: string; runId?: string };\n if (!sessionId || !runId) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n const session = getSession(sessionId);\n if (!session) {\n res.status(404).json({ error: \"session_not_found\" });\n return;\n }\n try {\n await Agent.cancelRun(runId, { runtime: \"cloud\", agentId: session.agent.agentId, apiKey: opts.cursorApiKey });\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 { sessionId } = (req.body ?? {}) as { sessionId?: string };\n if (!sessionId) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n await disposeSession(sessionId);\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\";\nimport { randomUUID } from \"node:crypto\";\n\nexport interface AgentSession {\n id: string;\n githubLogin: string;\n agent: SDKAgent;\n repoUrl: string;\n startingRef?: string;\n createdAt: number;\n lastUsedAt: number;\n lastPlanText?: string;\n lastPrUrl?: string;\n}\n\nconst SESSION_IDLE_TTL_MS = 30 * 60 * 1000;\nconst sessions = new Map<string, AgentSession>();\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 disposeSession(id).catch((e) => console.error(\"[coding-tab] session sweep dispose failed\", e));\n }\n }\n }, 5 * 60 * 1000).unref?.();\n}\n\nexport function registerSession(opts: { githubLogin: string; agent: SDKAgent; repoUrl: string; startingRef?: string }): AgentSession {\n startSweeper();\n const id = randomUUID();\n const session: AgentSession = {\n id,\n githubLogin: opts.githubLogin,\n agent: opts.agent,\n repoUrl: opts.repoUrl,\n startingRef: opts.startingRef,\n createdAt: Date.now(),\n lastUsedAt: Date.now(),\n };\n sessions.set(id, session);\n return session;\n}\n\nexport function getSession(id: string): AgentSession | undefined {\n const s = sessions.get(id);\n if (s) s.lastUsedAt = Date.now();\n return s;\n}\n\nexport function listSessions(login: string): AgentSession[] {\n return [...sessions.values()].filter((s) => s.githubLogin === login);\n}\n\nexport async function disposeSession(id: string): Promise<void> {\n const s = sessions.get(id);\n if (!s) return;\n sessions.delete(id);\n try {\n await s.agent[Symbol.asyncDispose]();\n } catch (err) {\n console.error(\"[coding-tab] dispose failed\", err);\n }\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 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 console.error(\"[coding-tab] /pr/merge failed\", err);\n res.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=300\");\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=300\");\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","getSession","getSession","import_express","parsePrUrl","import_express","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/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 { makeGitHubRouter } from \"./githubRoutes.js\";\nimport { makeRequireAuth } from \"./authMiddleware.js\";\nimport { makeAssetRouter } from \"./staticAssets.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\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 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 agentRouter = makeAgentRouter({\n cursorApiKey: options.cursorApiKey,\n defaultRepo: options.defaultRepo,\n envName: options.envName,\n skipReviewerRequest: options.skipReviewerRequest,\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 {\n ChatMode,\n ModelChoice,\n ModelOption,\n StreamEvent,\n PrInfo,\n StartAgentRequest,\n StartAgentResponse,\n SendMessageRequest,\n SendMessageResponse,\n ExecuteRequest,\n MergeRequest,\n MergeResponse,\n MeResponse,\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 { Agent, CursorAgentError, type Run, type SDKMessage } from \"@cursor/sdk\";\nimport type { AuthenticatedRequest } from \"./authMiddleware.js\";\nimport { listAvailableModels, resolveModel } from \"./models.js\";\nimport {\n disposeSession,\n getSession,\n registerSession,\n} from \"./sessions.js\";\nimport type { ChatMode, PrInfo, StreamEvent } 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}\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\nasync function streamRun(res: Response, run: Run): Promise<void> {\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 for (const block of message.message.content) {\n if (block.type === \"text\" && block.text) sse(res, { kind: \"text\", text: block.text });\n else if (block.type === \"tool_use\") sse(res, { kind: \"tool\", name: block.name, status: \"running\", callId: block.id, args: block.input });\n }\n } else if (message.type === \"thinking\") {\n sse(res, { kind: \"thinking\", text: message.text });\n } else if (message.type === \"tool_call\") {\n sse(res, { kind: \"tool\", name: message.name, status: message.status, callId: message.call_id, args: message.args, result: message.result });\n } else if (message.type === \"status\") {\n sse(res, { kind: \"status\", status: message.status, message: message.message });\n }\n }\n const result = await run.wait();\n const prUrl = result.git?.branches?.find((b) => b.prUrl)?.prUrl;\n sse(res, {\n kind: \"result\",\n status: result.status,\n pr: parsePrUrl(prUrl),\n durationMs: result.durationMs,\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 sse(res, { kind: \"error\", message, retryable });\n } finally {\n if (!res.writableEnded) res.end();\n }\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 router.post(\"/agent/start\", async (req: Request, res: Response) => {\n const user = (req as AuthenticatedRequest).user;\n const { prompt, mode, model, repoUrl, startingRef } = (req.body ?? {}) as {\n prompt?: string;\n mode?: ChatMode;\n model?: \"sonnet\" | \"opus\";\n repoUrl?: string;\n startingRef?: string;\n };\n if (!prompt || (mode !== \"plan\" && mode !== \"agent\") || (model !== \"sonnet\" && model !== \"opus\")) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n\n setupSseHeaders(res);\n\n let agent;\n try {\n const resolved = await resolveModel(opts.cursorApiKey, model);\n agent = await Agent.create({\n apiKey: opts.cursorApiKey,\n model: { id: resolved.cursorModelId },\n cloud: {\n repos: [\n {\n url: repoUrl ?? opts.defaultRepo.url,\n startingRef: startingRef ?? opts.defaultRepo.ref,\n },\n ],\n autoCreatePR: 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 const session = registerSession({\n githubLogin: user.githubLogin,\n agent,\n repoUrl: repoUrl ?? opts.defaultRepo.url,\n startingRef: startingRef ?? opts.defaultRepo.ref,\n });\n\n const run = await agent.send(modeInstructionPrefix(mode, prompt));\n sse(res, { kind: \"ready\", sessionId: session.id, agentId: agent.agentId, runId: run.id });\n console.log(`[coding-tab] start agent=${agent.agentId} run=${run.id} session=${session.id} login=${user.githubLogin}`);\n await streamRun(res, run);\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/start failed\", err);\n sse(res, { kind: \"error\", message, retryable });\n if (!res.writableEnded) res.end();\n if (agent) await agent[Symbol.asyncDispose]().catch(() => {});\n }\n\n req.on(\"close\", () => {\n if (!res.writableEnded) res.end();\n });\n });\n\n router.post(\"/agent/send\", async (req: Request, res: Response) => {\n const { sessionId, prompt, mode } = (req.body ?? {}) as {\n sessionId?: string;\n prompt?: string;\n mode?: ChatMode;\n };\n if (!sessionId || !prompt || (mode !== \"plan\" && mode !== \"agent\")) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n const session = getSession(sessionId);\n if (!session) {\n res.status(404).json({ error: \"session_not_found\" });\n return;\n }\n\n setupSseHeaders(res);\n\n try {\n const run = await session.agent.send(modeInstructionPrefix(mode, prompt));\n sse(res, { kind: \"ready\", sessionId: session.id, agentId: session.agent.agentId, runId: run.id });\n console.log(`[coding-tab] send agent=${session.agent.agentId} run=${run.id} session=${session.id}`);\n await streamRun(res, run);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(\"[coding-tab] /agent/send failed\", err);\n sse(res, { kind: \"error\", message });\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/execute\", async (req: Request, res: Response) => {\n const { sessionId } = (req.body ?? {}) as { sessionId?: string };\n if (!sessionId) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n const session = getSession(sessionId);\n if (!session) {\n res.status(404).json({ error: \"session_not_found\" });\n return;\n }\n\n setupSseHeaders(res);\n\n try {\n const run = await session.agent.send(EXECUTE_INSTRUCTION);\n sse(res, { kind: \"ready\", sessionId: session.id, agentId: session.agent.agentId, runId: run.id });\n console.log(`[coding-tab] execute agent=${session.agent.agentId} run=${run.id} session=${session.id}`);\n await streamRun(res, run);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(\"[coding-tab] /agent/execute failed\", err);\n sse(res, { kind: \"error\", message });\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/cancel\", async (req: Request, res: Response) => {\n const { sessionId, runId } = (req.body ?? {}) as { sessionId?: string; runId?: string };\n if (!sessionId || !runId) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n const session = getSession(sessionId);\n if (!session) {\n res.status(404).json({ error: \"session_not_found\" });\n return;\n }\n try {\n await Agent.cancelRun(runId, { runtime: \"cloud\", agentId: session.agent.agentId, apiKey: opts.cursorApiKey });\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 { sessionId } = (req.body ?? {}) as { sessionId?: string };\n if (!sessionId) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n await disposeSession(sessionId);\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\";\nimport { randomUUID } from \"node:crypto\";\n\nexport interface AgentSession {\n id: string;\n githubLogin: string;\n agent: SDKAgent;\n repoUrl: string;\n startingRef?: string;\n createdAt: number;\n lastUsedAt: number;\n lastPlanText?: string;\n lastPrUrl?: string;\n}\n\nconst SESSION_IDLE_TTL_MS = 30 * 60 * 1000;\nconst sessions = new Map<string, AgentSession>();\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 disposeSession(id).catch((e) => console.error(\"[coding-tab] session sweep dispose failed\", e));\n }\n }\n }, 5 * 60 * 1000).unref?.();\n}\n\nexport function registerSession(opts: { githubLogin: string; agent: SDKAgent; repoUrl: string; startingRef?: string }): AgentSession {\n startSweeper();\n const id = randomUUID();\n const session: AgentSession = {\n id,\n githubLogin: opts.githubLogin,\n agent: opts.agent,\n repoUrl: opts.repoUrl,\n startingRef: opts.startingRef,\n createdAt: Date.now(),\n lastUsedAt: Date.now(),\n };\n sessions.set(id, session);\n return session;\n}\n\nexport function getSession(id: string): AgentSession | undefined {\n const s = sessions.get(id);\n if (s) s.lastUsedAt = Date.now();\n return s;\n}\n\nexport function listSessions(login: string): AgentSession[] {\n return [...sessions.values()].filter((s) => s.githubLogin === login);\n}\n\nexport async function disposeSession(id: string): Promise<void> {\n const s = sessions.get(id);\n if (!s) return;\n sessions.delete(id);\n try {\n await s.agent[Symbol.asyncDispose]();\n } catch (err) {\n console.error(\"[coding-tab] dispose failed\", err);\n }\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","getSession","getSession","import_express","parsePrUrl","import_express","express"]}
package/dist/server.d.cts CHANGED
@@ -13,6 +13,8 @@ type ModelChoice = "sonnet" | "opus";
13
13
  interface MeResponse {
14
14
  githubLogin: string;
15
15
  avatarUrl?: string;
16
+ defaultRepoUrl?: string;
17
+ defaultRepoRef?: string;
16
18
  }
17
19
  interface ModelOption {
18
20
  choice: ModelChoice;
package/dist/server.d.ts CHANGED
@@ -13,6 +13,8 @@ type ModelChoice = "sonnet" | "opus";
13
13
  interface MeResponse {
14
14
  githubLogin: string;
15
15
  avatarUrl?: string;
16
+ defaultRepoUrl?: string;
17
+ defaultRepoRef?: string;
16
18
  }
17
19
  interface ModelOption {
18
20
  choice: ModelChoice;
package/dist/server.js CHANGED
@@ -150,7 +150,12 @@ function makeAuthRouter(opts) {
150
150
  res.status(403).json({ error: "not_authorized" });
151
151
  return;
152
152
  }
153
- res.json({ githubLogin: session.githubLogin, avatarUrl: session.avatarUrl });
153
+ res.json({
154
+ githubLogin: session.githubLogin,
155
+ avatarUrl: session.avatarUrl,
156
+ defaultRepoUrl: opts.defaultRepoUrl,
157
+ defaultRepoRef: opts.defaultRepoRef
158
+ });
154
159
  });
155
160
  return router;
156
161
  }
@@ -521,6 +526,21 @@ function makeGitHubRouter() {
521
526
  const user = req.user;
522
527
  const octokit = new Octokit({ auth: user.accessToken });
523
528
  try {
529
+ const pr = await octokit.pulls.get({
530
+ owner: parsed.owner,
531
+ repo: parsed.repo,
532
+ pull_number: parsed.number
533
+ });
534
+ if (pr.data.draft) {
535
+ await octokit.graphql(
536
+ `mutation($id: ID!) {
537
+ markPullRequestReadyForReview(input: { pullRequestId: $id }) {
538
+ pullRequest { id }
539
+ }
540
+ }`,
541
+ { id: pr.data.node_id }
542
+ );
543
+ }
524
544
  const merge = await octokit.pulls.merge({
525
545
  owner: parsed.owner,
526
546
  repo: parsed.repo,
@@ -530,8 +550,9 @@ function makeGitHubRouter() {
530
550
  res.json({ sha: merge.data.sha, merged: merge.data.merged });
531
551
  } catch (err) {
532
552
  const message = err instanceof Error ? err.message : String(err);
553
+ const status = err.status ?? 500;
533
554
  console.error("[coding-tab] /pr/merge failed", err);
534
- res.status(500).json({ error: message });
555
+ res.status(status >= 400 && status < 600 ? status : 500).json({ error: message });
535
556
  }
536
557
  });
537
558
  return router;
@@ -571,7 +592,7 @@ function makeAssetRouter(basePath) {
571
592
  try {
572
593
  const { data } = await readFirst(ASSET_CANDIDATES);
573
594
  res.set("Content-Type", "application/javascript; charset=utf-8");
574
- res.set("Cache-Control", "public, max-age=300");
595
+ res.set("Cache-Control", "public, max-age=0, must-revalidate");
575
596
  res.send(data);
576
597
  } catch (err) {
577
598
  res.status(500).send("// coding-tab browser bundle missing \u2014 did you run `npm run build`?");
@@ -581,7 +602,7 @@ function makeAssetRouter(basePath) {
581
602
  try {
582
603
  const { data } = await readFirst(STYLE_CANDIDATES);
583
604
  res.set("Content-Type", "text/css; charset=utf-8");
584
- res.set("Cache-Control", "public, max-age=300");
605
+ res.set("Cache-Control", "public, max-age=0, must-revalidate");
585
606
  res.send(data);
586
607
  } catch {
587
608
  res.status(404).send("/* style.css missing */");
@@ -641,7 +662,9 @@ function mountCodingTab(app, options) {
641
662
  oauth: options.githubOAuth,
642
663
  sessionPassword: options.sessionPassword,
643
664
  secure,
644
- basePath
665
+ basePath,
666
+ defaultRepoUrl: options.defaultRepo.url,
667
+ defaultRepoRef: options.defaultRepo.ref
645
668
  });
646
669
  router.use(authRouter);
647
670
  const requireAuth = makeRequireAuth({
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server/index.ts","../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/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 { makeGitHubRouter } from \"./githubRoutes.js\";\nimport { makeRequireAuth } from \"./authMiddleware.js\";\nimport { makeAssetRouter } from \"./staticAssets.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\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 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 });\n router.use(authRouter);\n\n const requireAuth = makeRequireAuth({\n sessionPassword: options.sessionPassword,\n secure,\n allowedLogins: options.githubOAuth.allowedLogins,\n });\n\n const agentRouter = makeAgentRouter({\n cursorApiKey: options.cursorApiKey,\n defaultRepo: options.defaultRepo,\n envName: options.envName,\n skipReviewerRequest: options.skipReviewerRequest,\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 {\n ChatMode,\n ModelChoice,\n ModelOption,\n StreamEvent,\n PrInfo,\n StartAgentRequest,\n StartAgentResponse,\n SendMessageRequest,\n SendMessageResponse,\n ExecuteRequest,\n MergeRequest,\n MergeResponse,\n MeResponse,\n} from \"../shared/types.js\";\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}\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({ githubLogin: session.githubLogin, avatarUrl: session.avatarUrl });\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 { Agent, CursorAgentError, type Run, type SDKMessage } from \"@cursor/sdk\";\nimport type { AuthenticatedRequest } from \"./authMiddleware.js\";\nimport { listAvailableModels, resolveModel } from \"./models.js\";\nimport {\n disposeSession,\n getSession,\n registerSession,\n} from \"./sessions.js\";\nimport type { ChatMode, PrInfo, StreamEvent } 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}\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\nasync function streamRun(res: Response, run: Run): Promise<void> {\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 for (const block of message.message.content) {\n if (block.type === \"text\" && block.text) sse(res, { kind: \"text\", text: block.text });\n else if (block.type === \"tool_use\") sse(res, { kind: \"tool\", name: block.name, status: \"running\", callId: block.id, args: block.input });\n }\n } else if (message.type === \"thinking\") {\n sse(res, { kind: \"thinking\", text: message.text });\n } else if (message.type === \"tool_call\") {\n sse(res, { kind: \"tool\", name: message.name, status: message.status, callId: message.call_id, args: message.args, result: message.result });\n } else if (message.type === \"status\") {\n sse(res, { kind: \"status\", status: message.status, message: message.message });\n }\n }\n const result = await run.wait();\n const prUrl = result.git?.branches?.find((b) => b.prUrl)?.prUrl;\n sse(res, {\n kind: \"result\",\n status: result.status,\n pr: parsePrUrl(prUrl),\n durationMs: result.durationMs,\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 sse(res, { kind: \"error\", message, retryable });\n } finally {\n if (!res.writableEnded) res.end();\n }\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 router.post(\"/agent/start\", async (req: Request, res: Response) => {\n const user = (req as AuthenticatedRequest).user;\n const { prompt, mode, model, repoUrl, startingRef } = (req.body ?? {}) as {\n prompt?: string;\n mode?: ChatMode;\n model?: \"sonnet\" | \"opus\";\n repoUrl?: string;\n startingRef?: string;\n };\n if (!prompt || (mode !== \"plan\" && mode !== \"agent\") || (model !== \"sonnet\" && model !== \"opus\")) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n\n setupSseHeaders(res);\n\n let agent;\n try {\n const resolved = await resolveModel(opts.cursorApiKey, model);\n agent = await Agent.create({\n apiKey: opts.cursorApiKey,\n model: { id: resolved.cursorModelId },\n cloud: {\n repos: [\n {\n url: repoUrl ?? opts.defaultRepo.url,\n startingRef: startingRef ?? opts.defaultRepo.ref,\n },\n ],\n autoCreatePR: 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 const session = registerSession({\n githubLogin: user.githubLogin,\n agent,\n repoUrl: repoUrl ?? opts.defaultRepo.url,\n startingRef: startingRef ?? opts.defaultRepo.ref,\n });\n\n const run = await agent.send(modeInstructionPrefix(mode, prompt));\n sse(res, { kind: \"ready\", sessionId: session.id, agentId: agent.agentId, runId: run.id });\n console.log(`[coding-tab] start agent=${agent.agentId} run=${run.id} session=${session.id} login=${user.githubLogin}`);\n await streamRun(res, run);\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/start failed\", err);\n sse(res, { kind: \"error\", message, retryable });\n if (!res.writableEnded) res.end();\n if (agent) await agent[Symbol.asyncDispose]().catch(() => {});\n }\n\n req.on(\"close\", () => {\n if (!res.writableEnded) res.end();\n });\n });\n\n router.post(\"/agent/send\", async (req: Request, res: Response) => {\n const { sessionId, prompt, mode } = (req.body ?? {}) as {\n sessionId?: string;\n prompt?: string;\n mode?: ChatMode;\n };\n if (!sessionId || !prompt || (mode !== \"plan\" && mode !== \"agent\")) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n const session = getSession(sessionId);\n if (!session) {\n res.status(404).json({ error: \"session_not_found\" });\n return;\n }\n\n setupSseHeaders(res);\n\n try {\n const run = await session.agent.send(modeInstructionPrefix(mode, prompt));\n sse(res, { kind: \"ready\", sessionId: session.id, agentId: session.agent.agentId, runId: run.id });\n console.log(`[coding-tab] send agent=${session.agent.agentId} run=${run.id} session=${session.id}`);\n await streamRun(res, run);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(\"[coding-tab] /agent/send failed\", err);\n sse(res, { kind: \"error\", message });\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/execute\", async (req: Request, res: Response) => {\n const { sessionId } = (req.body ?? {}) as { sessionId?: string };\n if (!sessionId) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n const session = getSession(sessionId);\n if (!session) {\n res.status(404).json({ error: \"session_not_found\" });\n return;\n }\n\n setupSseHeaders(res);\n\n try {\n const run = await session.agent.send(EXECUTE_INSTRUCTION);\n sse(res, { kind: \"ready\", sessionId: session.id, agentId: session.agent.agentId, runId: run.id });\n console.log(`[coding-tab] execute agent=${session.agent.agentId} run=${run.id} session=${session.id}`);\n await streamRun(res, run);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(\"[coding-tab] /agent/execute failed\", err);\n sse(res, { kind: \"error\", message });\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/cancel\", async (req: Request, res: Response) => {\n const { sessionId, runId } = (req.body ?? {}) as { sessionId?: string; runId?: string };\n if (!sessionId || !runId) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n const session = getSession(sessionId);\n if (!session) {\n res.status(404).json({ error: \"session_not_found\" });\n return;\n }\n try {\n await Agent.cancelRun(runId, { runtime: \"cloud\", agentId: session.agent.agentId, apiKey: opts.cursorApiKey });\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 { sessionId } = (req.body ?? {}) as { sessionId?: string };\n if (!sessionId) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n await disposeSession(sessionId);\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\";\nimport { randomUUID } from \"node:crypto\";\n\nexport interface AgentSession {\n id: string;\n githubLogin: string;\n agent: SDKAgent;\n repoUrl: string;\n startingRef?: string;\n createdAt: number;\n lastUsedAt: number;\n lastPlanText?: string;\n lastPrUrl?: string;\n}\n\nconst SESSION_IDLE_TTL_MS = 30 * 60 * 1000;\nconst sessions = new Map<string, AgentSession>();\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 disposeSession(id).catch((e) => console.error(\"[coding-tab] session sweep dispose failed\", e));\n }\n }\n }, 5 * 60 * 1000).unref?.();\n}\n\nexport function registerSession(opts: { githubLogin: string; agent: SDKAgent; repoUrl: string; startingRef?: string }): AgentSession {\n startSweeper();\n const id = randomUUID();\n const session: AgentSession = {\n id,\n githubLogin: opts.githubLogin,\n agent: opts.agent,\n repoUrl: opts.repoUrl,\n startingRef: opts.startingRef,\n createdAt: Date.now(),\n lastUsedAt: Date.now(),\n };\n sessions.set(id, session);\n return session;\n}\n\nexport function getSession(id: string): AgentSession | undefined {\n const s = sessions.get(id);\n if (s) s.lastUsedAt = Date.now();\n return s;\n}\n\nexport function listSessions(login: string): AgentSession[] {\n return [...sessions.values()].filter((s) => s.githubLogin === login);\n}\n\nexport async function disposeSession(id: string): Promise<void> {\n const s = sessions.get(id);\n if (!s) return;\n sessions.delete(id);\n try {\n await s.agent[Symbol.asyncDispose]();\n } catch (err) {\n console.error(\"[coding-tab] dispose failed\", err);\n }\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 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 console.error(\"[coding-tab] /pr/merge failed\", err);\n res.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=300\");\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=300\");\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":["Router","getSession","Router","getSession","Router","parsePrUrl","Router"]}
1
+ {"version":3,"sources":["../src/server/index.ts","../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/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 { makeGitHubRouter } from \"./githubRoutes.js\";\nimport { makeRequireAuth } from \"./authMiddleware.js\";\nimport { makeAssetRouter } from \"./staticAssets.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\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 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 agentRouter = makeAgentRouter({\n cursorApiKey: options.cursorApiKey,\n defaultRepo: options.defaultRepo,\n envName: options.envName,\n skipReviewerRequest: options.skipReviewerRequest,\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 {\n ChatMode,\n ModelChoice,\n ModelOption,\n StreamEvent,\n PrInfo,\n StartAgentRequest,\n StartAgentResponse,\n SendMessageRequest,\n SendMessageResponse,\n ExecuteRequest,\n MergeRequest,\n MergeResponse,\n MeResponse,\n} from \"../shared/types.js\";\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 { Agent, CursorAgentError, type Run, type SDKMessage } from \"@cursor/sdk\";\nimport type { AuthenticatedRequest } from \"./authMiddleware.js\";\nimport { listAvailableModels, resolveModel } from \"./models.js\";\nimport {\n disposeSession,\n getSession,\n registerSession,\n} from \"./sessions.js\";\nimport type { ChatMode, PrInfo, StreamEvent } 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}\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\nasync function streamRun(res: Response, run: Run): Promise<void> {\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 for (const block of message.message.content) {\n if (block.type === \"text\" && block.text) sse(res, { kind: \"text\", text: block.text });\n else if (block.type === \"tool_use\") sse(res, { kind: \"tool\", name: block.name, status: \"running\", callId: block.id, args: block.input });\n }\n } else if (message.type === \"thinking\") {\n sse(res, { kind: \"thinking\", text: message.text });\n } else if (message.type === \"tool_call\") {\n sse(res, { kind: \"tool\", name: message.name, status: message.status, callId: message.call_id, args: message.args, result: message.result });\n } else if (message.type === \"status\") {\n sse(res, { kind: \"status\", status: message.status, message: message.message });\n }\n }\n const result = await run.wait();\n const prUrl = result.git?.branches?.find((b) => b.prUrl)?.prUrl;\n sse(res, {\n kind: \"result\",\n status: result.status,\n pr: parsePrUrl(prUrl),\n durationMs: result.durationMs,\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 sse(res, { kind: \"error\", message, retryable });\n } finally {\n if (!res.writableEnded) res.end();\n }\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 router.post(\"/agent/start\", async (req: Request, res: Response) => {\n const user = (req as AuthenticatedRequest).user;\n const { prompt, mode, model, repoUrl, startingRef } = (req.body ?? {}) as {\n prompt?: string;\n mode?: ChatMode;\n model?: \"sonnet\" | \"opus\";\n repoUrl?: string;\n startingRef?: string;\n };\n if (!prompt || (mode !== \"plan\" && mode !== \"agent\") || (model !== \"sonnet\" && model !== \"opus\")) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n\n setupSseHeaders(res);\n\n let agent;\n try {\n const resolved = await resolveModel(opts.cursorApiKey, model);\n agent = await Agent.create({\n apiKey: opts.cursorApiKey,\n model: { id: resolved.cursorModelId },\n cloud: {\n repos: [\n {\n url: repoUrl ?? opts.defaultRepo.url,\n startingRef: startingRef ?? opts.defaultRepo.ref,\n },\n ],\n autoCreatePR: 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 const session = registerSession({\n githubLogin: user.githubLogin,\n agent,\n repoUrl: repoUrl ?? opts.defaultRepo.url,\n startingRef: startingRef ?? opts.defaultRepo.ref,\n });\n\n const run = await agent.send(modeInstructionPrefix(mode, prompt));\n sse(res, { kind: \"ready\", sessionId: session.id, agentId: agent.agentId, runId: run.id });\n console.log(`[coding-tab] start agent=${agent.agentId} run=${run.id} session=${session.id} login=${user.githubLogin}`);\n await streamRun(res, run);\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/start failed\", err);\n sse(res, { kind: \"error\", message, retryable });\n if (!res.writableEnded) res.end();\n if (agent) await agent[Symbol.asyncDispose]().catch(() => {});\n }\n\n req.on(\"close\", () => {\n if (!res.writableEnded) res.end();\n });\n });\n\n router.post(\"/agent/send\", async (req: Request, res: Response) => {\n const { sessionId, prompt, mode } = (req.body ?? {}) as {\n sessionId?: string;\n prompt?: string;\n mode?: ChatMode;\n };\n if (!sessionId || !prompt || (mode !== \"plan\" && mode !== \"agent\")) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n const session = getSession(sessionId);\n if (!session) {\n res.status(404).json({ error: \"session_not_found\" });\n return;\n }\n\n setupSseHeaders(res);\n\n try {\n const run = await session.agent.send(modeInstructionPrefix(mode, prompt));\n sse(res, { kind: \"ready\", sessionId: session.id, agentId: session.agent.agentId, runId: run.id });\n console.log(`[coding-tab] send agent=${session.agent.agentId} run=${run.id} session=${session.id}`);\n await streamRun(res, run);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(\"[coding-tab] /agent/send failed\", err);\n sse(res, { kind: \"error\", message });\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/execute\", async (req: Request, res: Response) => {\n const { sessionId } = (req.body ?? {}) as { sessionId?: string };\n if (!sessionId) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n const session = getSession(sessionId);\n if (!session) {\n res.status(404).json({ error: \"session_not_found\" });\n return;\n }\n\n setupSseHeaders(res);\n\n try {\n const run = await session.agent.send(EXECUTE_INSTRUCTION);\n sse(res, { kind: \"ready\", sessionId: session.id, agentId: session.agent.agentId, runId: run.id });\n console.log(`[coding-tab] execute agent=${session.agent.agentId} run=${run.id} session=${session.id}`);\n await streamRun(res, run);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.error(\"[coding-tab] /agent/execute failed\", err);\n sse(res, { kind: \"error\", message });\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/cancel\", async (req: Request, res: Response) => {\n const { sessionId, runId } = (req.body ?? {}) as { sessionId?: string; runId?: string };\n if (!sessionId || !runId) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n const session = getSession(sessionId);\n if (!session) {\n res.status(404).json({ error: \"session_not_found\" });\n return;\n }\n try {\n await Agent.cancelRun(runId, { runtime: \"cloud\", agentId: session.agent.agentId, apiKey: opts.cursorApiKey });\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 { sessionId } = (req.body ?? {}) as { sessionId?: string };\n if (!sessionId) {\n res.status(400).json({ error: \"invalid_request\" });\n return;\n }\n await disposeSession(sessionId);\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\";\nimport { randomUUID } from \"node:crypto\";\n\nexport interface AgentSession {\n id: string;\n githubLogin: string;\n agent: SDKAgent;\n repoUrl: string;\n startingRef?: string;\n createdAt: number;\n lastUsedAt: number;\n lastPlanText?: string;\n lastPrUrl?: string;\n}\n\nconst SESSION_IDLE_TTL_MS = 30 * 60 * 1000;\nconst sessions = new Map<string, AgentSession>();\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 disposeSession(id).catch((e) => console.error(\"[coding-tab] session sweep dispose failed\", e));\n }\n }\n }, 5 * 60 * 1000).unref?.();\n}\n\nexport function registerSession(opts: { githubLogin: string; agent: SDKAgent; repoUrl: string; startingRef?: string }): AgentSession {\n startSweeper();\n const id = randomUUID();\n const session: AgentSession = {\n id,\n githubLogin: opts.githubLogin,\n agent: opts.agent,\n repoUrl: opts.repoUrl,\n startingRef: opts.startingRef,\n createdAt: Date.now(),\n lastUsedAt: Date.now(),\n };\n sessions.set(id, session);\n return session;\n}\n\nexport function getSession(id: string): AgentSession | undefined {\n const s = sessions.get(id);\n if (s) s.lastUsedAt = Date.now();\n return s;\n}\n\nexport function listSessions(login: string): AgentSession[] {\n return [...sessions.values()].filter((s) => s.githubLogin === login);\n}\n\nexport async function disposeSession(id: string): Promise<void> {\n const s = sessions.get(id);\n if (!s) return;\n sessions.delete(id);\n try {\n await s.agent[Symbol.asyncDispose]();\n } catch (err) {\n console.error(\"[coding-tab] dispose failed\", err);\n }\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":";AAAA,OAAO,aAA4C;;;ACAnD,SAAS,cAA2C;AACpD,SAAS,mBAAmB;;;ACA5B,SAAS,sBAAsB;;;ACSxB,IAAM,sBAAsB;AAE5B,SAAS,oBAAoB,UAAkB,QAAiC;AACrF,SAAO;AAAA,IACL;AAAA,IACA,YAAY;AAAA,IACZ,eAAe;AAAA,MACb,UAAU;AAAA,MACV;AAAA,MACA,UAAU;AAAA,MACV,MAAM;AAAA,MACN,QAAQ,KAAK,KAAK,KAAK;AAAA,IACzB;AAAA,EACF;AACF;;;ADNO,SAAS,WACd,KACA,KACA,MACA;AACA,SAAO,eAAiC,KAAK,KAAK,oBAAoB,KAAK,iBAAiB,KAAK,MAAM,CAAC;AAC1G;AAEO,SAAS,gBAAgB,MAA6B;AAC3D,QAAM,UAAU,IAAI,IAAI,KAAK,cAAc,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AACtE,SAAO,eAAe,YAAY,KAAc,KAAe,MAAoB;AACjF,QAAI;AACF,YAAM,UAAU,MAAM,WAAW,KAAK,KAAK,IAAI;AAC/C,UAAI,CAAC,QAAQ,eAAe,CAAC,QAAQ,aAAa;AAChD,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,kBAAkB,CAAC;AACjD;AAAA,MACF;AACA,UAAI,QAAQ,OAAO,KAAK,CAAC,QAAQ,IAAI,QAAQ,YAAY,YAAY,CAAC,GAAG;AACvE,YAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAChD;AAAA,MACF;AACA,MAAC,IAA6B,OAAO;AAAA,QACnC,aAAa,QAAQ;AAAA,QACrB,aAAa,QAAQ;AAAA,QACrB,WAAW,QAAQ;AAAA,MACrB;AACA,WAAK;AAAA,IACP,SAAS,KAAK;AACZ,cAAQ,MAAM,IAAI,mBAAmB,wBAAwB,GAAG;AAChE,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,gBAAgB,CAAC;AAAA,IACjD;AAAA,EACF;AACF;;;AD7BA,IAAM,qBAAqB,KAAK,KAAK;AAE9B,SAAS,eAAe,MAAiC;AAC9D,QAAM,SAAS,OAAO;AACtB,QAAM,UAAU,IAAI,IAAI,KAAK,MAAM,cAAc,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAC5E,QAAM,SAAS,KAAK,MAAM,UAAU,CAAC,QAAQ,WAAW;AAExD,SAAO,IAAI,eAAe,OAAO,KAAc,QAAkB;AAC/D,UAAM,UAAU,MAAM,WAAW,KAAK,KAAK,IAAI;AAC/C,UAAM,QAAQ,YAAY,EAAE,EAAE,SAAS,KAAK;AAC5C,YAAQ,aAAa;AACrB,YAAQ,sBAAsB,KAAK,IAAI;AACvC,UAAM,QAAQ,KAAK;AAEnB,UAAM,MAAM,IAAI,IAAI,0CAA0C;AAC9D,QAAI,aAAa,IAAI,aAAa,KAAK,MAAM,QAAQ;AACrD,QAAI,aAAa,IAAI,gBAAgB,KAAK,MAAM,WAAW;AAC3D,QAAI,aAAa,IAAI,SAAS,OAAO,KAAK,GAAG,CAAC;AAC9C,QAAI,aAAa,IAAI,SAAS,KAAK;AACnC,QAAI,aAAa,IAAI,gBAAgB,OAAO;AAC5C,QAAI,SAAS,KAAK,IAAI,SAAS,CAAC;AAAA,EAClC,CAAC;AAED,SAAO,IAAI,kBAAkB,OAAO,KAAc,QAAkB;AAClE,UAAM,UAAU,MAAM,WAAW,KAAK,KAAK,IAAI;AAC/C,UAAM,OAAO,OAAO,IAAI,MAAM,SAAS,WAAW,IAAI,MAAM,OAAO;AACnE,UAAM,QAAQ,OAAO,IAAI,MAAM,UAAU,WAAW,IAAI,MAAM,QAAQ;AAEtE,UAAM,gBAAgB,QAAQ;AAC9B,UAAM,WAAW,QAAQ,sBAAsB,KAAK,IAAI,IAAI,QAAQ,sBAAsB,OAAO;AACjG,YAAQ,aAAa;AACrB,YAAQ,sBAAsB;AAE9B,QAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,iBAAiB,UAAU,iBAAiB,WAAW,oBAAoB;AACjG,YAAM,QAAQ,KAAK;AACnB,UAAI,OAAO,GAAG,EAAE,KAAK,wDAAwD;AAC7E;AAAA,IACF;AAEA,QAAI;AACF,YAAM,YAAY,MAAM,MAAM,+CAA+C;AAAA,QAC3E,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,QAAQ;AAAA,UACR,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB,WAAW,KAAK,MAAM;AAAA,UACtB,eAAe,KAAK,MAAM;AAAA,UAC1B;AAAA,UACA,cAAc,KAAK,MAAM;AAAA,QAC3B,CAAC;AAAA,MACH,CAAC;AACD,YAAM,YAAa,MAAM,UAAU,KAAK;AAOxC,UAAI,CAAC,UAAU,cAAc;AAC3B,cAAM,QAAQ,KAAK;AACnB,YAAI,OAAO,GAAG,EAAE,KAAK,iCAAiC,UAAU,qBAAqB,UAAU,SAAS,SAAS,EAAE;AACnH;AAAA,MACF;AAEA,YAAM,WAAW,MAAM,MAAM,+BAA+B;AAAA,QAC1D,SAAS;AAAA,UACP,eAAe,UAAU,UAAU,YAAY;AAAA,UAC/C,QAAQ;AAAA,UACR,wBAAwB;AAAA,QAC1B;AAAA,MACF,CAAC;AACD,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,QAAQ,KAAK;AACnB,YAAI,OAAO,GAAG,EAAE,KAAK,8BAA8B,SAAS,MAAM,EAAE;AACpE;AAAA,MACF;AACA,YAAM,OAAQ,MAAM,SAAS,KAAK;AAElC,UAAI,QAAQ,OAAO,KAAK,CAAC,QAAQ,IAAI,KAAK,MAAM,YAAY,CAAC,GAAG;AAC9D,cAAM,QAAQ,KAAK;AACnB,YAAI,OAAO,GAAG,EAAE,KAAK,IAAI,KAAK,KAAK,wCAAwC;AAC3E;AAAA,MACF;AAEA,cAAQ,cAAc,KAAK;AAC3B,cAAQ,YAAY,KAAK;AACzB,cAAQ,cAAc,UAAU;AAChC,YAAM,QAAQ,KAAK;AAEnB,UAAI,SAAS,KAAK,KAAK,WAAW,GAAG;AAAA,IACvC,SAAS,KAAK;AACZ,cAAQ,MAAM,sCAAsC,GAAG;AACvD,UAAI,OAAO,GAAG,EAAE,KAAK,2CAA2C;AAAA,IAClE;AAAA,EACF,CAAC;AAED,SAAO,KAAK,gBAAgB,OAAO,KAAc,QAAkB;AACjE,UAAM,UAAU,MAAM,WAAW,KAAK,KAAK,IAAI;AAC/C,YAAQ,QAAQ;AAChB,QAAI,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,EACvB,CAAC;AAED,SAAO,IAAI,YAAY,OAAO,KAAc,QAAkB;AAC5D,UAAM,UAAU,MAAM,WAAW,KAAK,KAAK,IAAI;AAC/C,QAAI,CAAC,QAAQ,eAAe,CAAC,QAAQ,aAAa;AAChD,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,kBAAkB,CAAC;AACjD;AAAA,IACF;AACA,QAAI,QAAQ,OAAO,KAAK,CAAC,QAAQ,IAAI,QAAQ,YAAY,YAAY,CAAC,GAAG;AACvE,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAChD;AAAA,IACF;AACA,QAAI,KAAK;AAAA,MACP,aAAa,QAAQ;AAAA,MACrB,WAAW,QAAQ;AAAA,MACnB,gBAAgB,KAAK;AAAA,MACrB,gBAAgB,KAAK;AAAA,IACvB,CAAC;AAAA,EACH,CAAC;AAED,SAAO;AACT;;;AGhJA,SAAS,UAAAA,eAA2C;AACpD,SAAS,OAAO,wBAAmD;;;ACDnE,SAAS,cAA6B;AAGtC,IAAM,iBAAiB;AACvB,IAAM,eAAe;AAOrB,IAAI,QAA0D;AAC9D,IAAM,eAAe,IAAI,KAAK;AAE9B,eAAe,aAAa,QAAqC;AAC/D,MAAI,SAAS,KAAK,IAAI,IAAI,MAAM,YAAY,aAAc,QAAO,MAAM;AACvE,QAAM,SAAS,MAAM,OAAO,OAAO,KAAK,EAAE,OAAO,CAAC;AAClD,UAAQ,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO;AACxC,SAAO;AACT;AAEA,SAAS,SAAS,QAAoB,SAAuC;AAC3E,QAAM,UAAU,OAAO,OAAO,CAAC,MAAM,QAAQ,KAAK,EAAE,EAAE,KAAK,QAAQ,KAAK,EAAE,WAAW,MAAM,EAAE,WAAW,CAAC,GAAG,KAAK,CAAC,MAAM,QAAQ,KAAK,CAAC,CAAC,CAAC;AACxI,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,UAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,GAAG,cAAc,EAAE,EAAE,CAAC;AAC/C,SAAO,QAAQ,CAAC;AAClB;AAEA,eAAsB,oBAAoB,QAAwC;AAChF,QAAM,SAAS,MAAM,aAAa,MAAM;AACxC,QAAM,MAAqB,CAAC;AAC5B,QAAM,SAAS,SAAS,QAAQ,cAAc;AAC9C,MAAI,OAAQ,KAAI,KAAK,EAAE,QAAQ,UAAU,eAAe,OAAO,IAAI,aAAa,OAAO,eAAe,OAAO,GAAG,CAAC;AACjH,QAAM,OAAO,SAAS,QAAQ,YAAY;AAC1C,MAAI,KAAM,KAAI,KAAK,EAAE,QAAQ,QAAQ,eAAe,KAAK,IAAI,aAAa,KAAK,eAAe,KAAK,GAAG,CAAC;AACvG,SAAO;AACT;AAEA,eAAsB,aAAa,QAAgB,QAA6C;AAC9F,QAAM,SAAS,MAAM,aAAa,MAAM;AACxC,QAAM,UAAU,WAAW,WAAW,iBAAiB;AACvD,QAAM,SAAS,SAAS,QAAQ,OAAO;AACvC,MAAI,CAAC,QAAQ;AACX,UAAM,YAAY,OAAO,IAAI,CAAC,MAAM,GAAG,EAAE,WAAW,KAAK,EAAE,EAAE,GAAG,EAAE,KAAK,IAAI;AAC3E,UAAM,IAAI,MAAM,mCAAmC,MAAM,iBAAiB,SAAS,EAAE;AAAA,EACvF;AACA,SAAO,EAAE,eAAe,OAAO,IAAI,aAAa,OAAO,eAAe,OAAO,GAAG;AAClF;;;AC9CA,SAAS,kBAAkB;AAc3B,IAAM,sBAAsB,KAAK,KAAK;AACtC,IAAM,WAAW,oBAAI,IAA0B;AAE/C,IAAI,iBAAiB;AACrB,SAAS,eAAe;AACtB,MAAI,eAAgB;AACpB,mBAAiB;AACjB,cAAY,MAAM;AAChB,UAAM,MAAM,KAAK,IAAI;AACrB,eAAW,CAAC,IAAI,CAAC,KAAK,UAAU;AAC9B,UAAI,MAAM,EAAE,aAAa,qBAAqB;AAC5C,uBAAe,EAAE,EAAE,MAAM,CAAC,MAAM,QAAQ,MAAM,6CAA6C,CAAC,CAAC;AAAA,MAC/F;AAAA,IACF;AAAA,EACF,GAAG,IAAI,KAAK,GAAI,EAAE,QAAQ;AAC5B;AAEO,SAAS,gBAAgB,MAAqG;AACnI,eAAa;AACb,QAAM,KAAK,WAAW;AACtB,QAAM,UAAwB;AAAA,IAC5B;AAAA,IACA,aAAa,KAAK;AAAA,IAClB,OAAO,KAAK;AAAA,IACZ,SAAS,KAAK;AAAA,IACd,aAAa,KAAK;AAAA,IAClB,WAAW,KAAK,IAAI;AAAA,IACpB,YAAY,KAAK,IAAI;AAAA,EACvB;AACA,WAAS,IAAI,IAAI,OAAO;AACxB,SAAO;AACT;AAEO,SAASC,YAAW,IAAsC;AAC/D,QAAM,IAAI,SAAS,IAAI,EAAE;AACzB,MAAI,EAAG,GAAE,aAAa,KAAK,IAAI;AAC/B,SAAO;AACT;AAMA,eAAsB,eAAe,IAA2B;AAC9D,QAAM,IAAI,SAAS,IAAI,EAAE;AACzB,MAAI,CAAC,EAAG;AACR,WAAS,OAAO,EAAE;AAClB,MAAI;AACF,UAAM,EAAE,MAAM,OAAO,YAAY,EAAE;AAAA,EACrC,SAAS,KAAK;AACZ,YAAQ,MAAM,+BAA+B,GAAG;AAAA,EAClD;AACF;;;AFxDA,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYzB,IAAM,sBAAsB;AAS5B,SAAS,IAAI,KAAe,OAAoB;AAC9C,MAAI,MAAM,SAAS,KAAK,UAAU,KAAK,CAAC;AAAA;AAAA,CAAM;AAChD;AAEA,SAAS,WAAW,KAA6C;AAC/D,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,QAAQ,IAAI,MAAM,4CAA4C;AACpE,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,EAAE,KAAK,OAAO,MAAM,CAAC,GAAI,MAAM,MAAM,CAAC,GAAI,QAAQ,OAAO,MAAM,CAAC,CAAE,EAAE;AAC7E;AAEA,SAAS,sBAAsB,MAAgB,QAAwB;AACrE,SAAO,SAAS,SAAS,GAAG,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA,EAA6B,MAAM,KAAK;AACtF;AAEA,SAAS,gBAAgB,KAAe;AACtC,MAAI,IAAI;AAAA,IACN,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,YAAY;AAAA,IACZ,qBAAqB;AAAA,EACvB,CAAC;AACD,MAAI,eAAe;AACrB;AAEA,eAAe,UAAU,KAAe,KAAyB;AAC/D,MAAI;AACF,qBAAiB,WAAW,IAAI,OAAO,GAAuC;AAC5E,UAAI,IAAI,cAAe;AACvB,UAAI,QAAQ,SAAS,aAAa;AAChC,mBAAW,SAAS,QAAQ,QAAQ,SAAS;AAC3C,cAAI,MAAM,SAAS,UAAU,MAAM,KAAM,KAAI,KAAK,EAAE,MAAM,QAAQ,MAAM,MAAM,KAAK,CAAC;AAAA,mBAC3E,MAAM,SAAS,WAAY,KAAI,KAAK,EAAE,MAAM,QAAQ,MAAM,MAAM,MAAM,QAAQ,WAAW,QAAQ,MAAM,IAAI,MAAM,MAAM,MAAM,CAAC;AAAA,QACzI;AAAA,MACF,WAAW,QAAQ,SAAS,YAAY;AACtC,YAAI,KAAK,EAAE,MAAM,YAAY,MAAM,QAAQ,KAAK,CAAC;AAAA,MACnD,WAAW,QAAQ,SAAS,aAAa;AACvC,YAAI,KAAK,EAAE,MAAM,QAAQ,MAAM,QAAQ,MAAM,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,SAAS,MAAM,QAAQ,MAAM,QAAQ,QAAQ,OAAO,CAAC;AAAA,MAC5I,WAAW,QAAQ,SAAS,UAAU;AACpC,YAAI,KAAK,EAAE,MAAM,UAAU,QAAQ,QAAQ,QAAQ,SAAS,QAAQ,QAAQ,CAAC;AAAA,MAC/E;AAAA,IACF;AACA,UAAM,SAAS,MAAM,IAAI,KAAK;AAC9B,UAAM,QAAQ,OAAO,KAAK,UAAU,KAAK,CAAC,MAAM,EAAE,KAAK,GAAG;AAC1D,QAAI,KAAK;AAAA,MACP,MAAM;AAAA,MACN,QAAQ,OAAO;AAAA,MACf,IAAI,WAAW,KAAK;AAAA,MACpB,YAAY,OAAO;AAAA,IACrB,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,UAAM,YAAY,eAAe,mBAAmB,QAAS,IAAkC,WAAW,IAAI;AAC9G,QAAI,KAAK,EAAE,MAAM,SAAS,SAAS,UAAU,CAAC;AAAA,EAChD,UAAE;AACA,QAAI,CAAC,IAAI,cAAe,KAAI,IAAI;AAAA,EAClC;AACF;AAEO,SAAS,gBAAgB,MAAkC;AAChE,QAAM,SAASC,QAAO;AAEtB,SAAO,IAAI,WAAW,OAAO,MAAM,QAAQ;AACzC,QAAI;AACF,YAAM,SAAS,MAAM,oBAAoB,KAAK,YAAY;AAC1D,UAAI,KAAK,EAAE,OAAO,CAAC;AAAA,IACrB,SAAS,KAAK;AACZ,cAAQ,MAAM,+BAA+B,GAAG;AAChD,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,eAAe,QAAQ,IAAI,UAAU,gBAAgB,CAAC;AAAA,IACtF;AAAA,EACF,CAAC;AAED,SAAO,KAAK,gBAAgB,OAAO,KAAc,QAAkB;AACjE,UAAM,OAAQ,IAA6B;AAC3C,UAAM,EAAE,QAAQ,MAAM,OAAO,SAAS,YAAY,IAAK,IAAI,QAAQ,CAAC;AAOpE,QAAI,CAAC,UAAW,SAAS,UAAU,SAAS,WAAa,UAAU,YAAY,UAAU,QAAS;AAChG,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,kBAAkB,CAAC;AACjD;AAAA,IACF;AAEA,oBAAgB,GAAG;AAEnB,QAAI;AACJ,QAAI;AACF,YAAM,WAAW,MAAM,aAAa,KAAK,cAAc,KAAK;AAC5D,cAAQ,MAAM,MAAM,OAAO;AAAA,QACzB,QAAQ,KAAK;AAAA,QACb,OAAO,EAAE,IAAI,SAAS,cAAc;AAAA,QACpC,OAAO;AAAA,UACL,OAAO;AAAA,YACL;AAAA,cACE,KAAK,WAAW,KAAK,YAAY;AAAA,cACjC,aAAa,eAAe,KAAK,YAAY;AAAA,YAC/C;AAAA,UACF;AAAA,UACA,cAAc,SAAS;AAAA,UACvB,qBAAqB,KAAK,uBAAuB;AAAA,UACjD,SAAS,EAAE,cAAc,KAAK,YAAY;AAAA,UAC1C,GAAI,KAAK,UAAU,EAAE,KAAK,EAAE,MAAM,SAAkB,MAAM,KAAK,QAAQ,EAAE,IAAI,CAAC;AAAA,QAChF;AAAA,MACF,CAAC;AACD,YAAM,UAAU,gBAAgB;AAAA,QAC9B,aAAa,KAAK;AAAA,QAClB;AAAA,QACA,SAAS,WAAW,KAAK,YAAY;AAAA,QACrC,aAAa,eAAe,KAAK,YAAY;AAAA,MAC/C,CAAC;AAED,YAAM,MAAM,MAAM,MAAM,KAAK,sBAAsB,MAAM,MAAM,CAAC;AAChE,UAAI,KAAK,EAAE,MAAM,SAAS,WAAW,QAAQ,IAAI,SAAS,MAAM,SAAS,OAAO,IAAI,GAAG,CAAC;AACxF,cAAQ,IAAI,4BAA4B,MAAM,OAAO,QAAQ,IAAI,EAAE,YAAY,QAAQ,EAAE,UAAU,KAAK,WAAW,EAAE;AACrH,YAAM,UAAU,KAAK,GAAG;AAAA,IAC1B,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAM,YAAY,eAAe,mBAAmB,QAAS,IAAkC,WAAW,IAAI;AAC9G,cAAQ,MAAM,oCAAoC,GAAG;AACrD,UAAI,KAAK,EAAE,MAAM,SAAS,SAAS,UAAU,CAAC;AAC9C,UAAI,CAAC,IAAI,cAAe,KAAI,IAAI;AAChC,UAAI,MAAO,OAAM,MAAM,OAAO,YAAY,EAAE,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IAC9D;AAEA,QAAI,GAAG,SAAS,MAAM;AACpB,UAAI,CAAC,IAAI,cAAe,KAAI,IAAI;AAAA,IAClC,CAAC;AAAA,EACH,CAAC;AAED,SAAO,KAAK,eAAe,OAAO,KAAc,QAAkB;AAChE,UAAM,EAAE,WAAW,QAAQ,KAAK,IAAK,IAAI,QAAQ,CAAC;AAKlD,QAAI,CAAC,aAAa,CAAC,UAAW,SAAS,UAAU,SAAS,SAAU;AAClE,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,kBAAkB,CAAC;AACjD;AAAA,IACF;AACA,UAAM,UAAUC,YAAW,SAAS;AACpC,QAAI,CAAC,SAAS;AACZ,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,oBAAoB,CAAC;AACnD;AAAA,IACF;AAEA,oBAAgB,GAAG;AAEnB,QAAI;AACF,YAAM,MAAM,MAAM,QAAQ,MAAM,KAAK,sBAAsB,MAAM,MAAM,CAAC;AACxE,UAAI,KAAK,EAAE,MAAM,SAAS,WAAW,QAAQ,IAAI,SAAS,QAAQ,MAAM,SAAS,OAAO,IAAI,GAAG,CAAC;AAChG,cAAQ,IAAI,2BAA2B,QAAQ,MAAM,OAAO,QAAQ,IAAI,EAAE,YAAY,QAAQ,EAAE,EAAE;AAClG,YAAM,UAAU,KAAK,GAAG;AAAA,IAC1B,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,cAAQ,MAAM,mCAAmC,GAAG;AACpD,UAAI,KAAK,EAAE,MAAM,SAAS,QAAQ,CAAC;AACnC,UAAI,CAAC,IAAI,cAAe,KAAI,IAAI;AAAA,IAClC;AAEA,QAAI,GAAG,SAAS,MAAM;AACpB,UAAI,CAAC,IAAI,cAAe,KAAI,IAAI;AAAA,IAClC,CAAC;AAAA,EACH,CAAC;AAED,SAAO,KAAK,kBAAkB,OAAO,KAAc,QAAkB;AACnE,UAAM,EAAE,UAAU,IAAK,IAAI,QAAQ,CAAC;AACpC,QAAI,CAAC,WAAW;AACd,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,kBAAkB,CAAC;AACjD;AAAA,IACF;AACA,UAAM,UAAUA,YAAW,SAAS;AACpC,QAAI,CAAC,SAAS;AACZ,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,oBAAoB,CAAC;AACnD;AAAA,IACF;AAEA,oBAAgB,GAAG;AAEnB,QAAI;AACF,YAAM,MAAM,MAAM,QAAQ,MAAM,KAAK,mBAAmB;AACxD,UAAI,KAAK,EAAE,MAAM,SAAS,WAAW,QAAQ,IAAI,SAAS,QAAQ,MAAM,SAAS,OAAO,IAAI,GAAG,CAAC;AAChG,cAAQ,IAAI,8BAA8B,QAAQ,MAAM,OAAO,QAAQ,IAAI,EAAE,YAAY,QAAQ,EAAE,EAAE;AACrG,YAAM,UAAU,KAAK,GAAG;AAAA,IAC1B,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,cAAQ,MAAM,sCAAsC,GAAG;AACvD,UAAI,KAAK,EAAE,MAAM,SAAS,QAAQ,CAAC;AACnC,UAAI,CAAC,IAAI,cAAe,KAAI,IAAI;AAAA,IAClC;AAEA,QAAI,GAAG,SAAS,MAAM;AACpB,UAAI,CAAC,IAAI,cAAe,KAAI,IAAI;AAAA,IAClC,CAAC;AAAA,EACH,CAAC;AAED,SAAO,KAAK,iBAAiB,OAAO,KAAc,QAAkB;AAClE,UAAM,EAAE,WAAW,MAAM,IAAK,IAAI,QAAQ,CAAC;AAC3C,QAAI,CAAC,aAAa,CAAC,OAAO;AACxB,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,kBAAkB,CAAC;AACjD;AAAA,IACF;AACA,UAAM,UAAUA,YAAW,SAAS;AACpC,QAAI,CAAC,SAAS;AACZ,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,oBAAoB,CAAC;AACnD;AAAA,IACF;AACA,QAAI;AACF,YAAM,MAAM,UAAU,OAAO,EAAE,SAAS,SAAS,SAAS,QAAQ,MAAM,SAAS,QAAQ,KAAK,aAAa,CAAC;AAC5G,UAAI,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,IACvB,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,cAAQ,MAAM,qCAAqC,GAAG;AACtD,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,QAAQ,CAAC;AAAA,IACzC;AAAA,EACF,CAAC;AAED,SAAO,KAAK,kBAAkB,OAAO,KAAc,QAAkB;AACnE,UAAM,EAAE,UAAU,IAAK,IAAI,QAAQ,CAAC;AACpC,QAAI,CAAC,WAAW;AACd,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,kBAAkB,CAAC;AACjD;AAAA,IACF;AACA,UAAM,eAAe,SAAS;AAC9B,QAAI,KAAK,EAAE,IAAI,KAAK,CAAC;AAAA,EACvB,CAAC;AAED,SAAO;AACT;;;AGtQA,SAAS,UAAAC,eAA2C;AACpD,SAAS,eAAe;AAIxB,SAASC,YAAW,OAAuE;AACzF,QAAM,QAAQ,MAAM,MAAM,4CAA4C;AACtE,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,EAAE,OAAO,MAAM,CAAC,GAAI,MAAM,MAAM,CAAC,GAAI,QAAQ,OAAO,MAAM,CAAC,CAAE,EAAE;AACxE;AAEO,SAAS,mBAA2B;AACzC,QAAM,SAASD,QAAO;AAEtB,SAAO,IAAI,cAAc,OAAO,KAAc,QAAkB;AAC9D,UAAM,QAAQ,OAAO,IAAI,MAAM,UAAU,WAAW,IAAI,MAAM,QAAQ;AACtE,QAAI,CAAC,OAAO;AACV,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,gBAAgB,CAAC;AAC/C;AAAA,IACF;AACA,UAAM,SAASC,YAAW,KAAK;AAC/B,QAAI,CAAC,QAAQ;AACX,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,gBAAgB,CAAC;AAC/C;AAAA,IACF;AACA,UAAM,OAAQ,IAA6B;AAC3C,UAAM,UAAU,IAAI,QAAQ,EAAE,MAAM,KAAK,YAAY,CAAC;AACtD,QAAI;AACF,YAAM,KAAK,MAAM,QAAQ,MAAM,IAAI,EAAE,OAAO,OAAO,OAAO,MAAM,OAAO,MAAM,aAAa,OAAO,OAAO,CAAC;AACzG,YAAM,OAAe;AAAA,QACnB,KAAK,GAAG,KAAK;AAAA,QACb,OAAO,OAAO;AAAA,QACd,MAAM,OAAO;AAAA,QACb,QAAQ,OAAO;AAAA,QACf,QAAQ,GAAG,KAAK,MAAM;AAAA,QACtB,OAAO,GAAG,KAAK,SAAS,WAAY,GAAG,KAAK;AAAA,QAC5C,OAAO,GAAG,KAAK;AAAA,QACf,MAAM,GAAG,KAAK,QAAQ;AAAA,MACxB;AACA,UAAI,KAAK,EAAE,IAAI,MAAM,WAAW,GAAG,KAAK,WAAW,iBAAiB,GAAG,KAAK,gBAAgB,CAAC;AAAA,IAC/F,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,QAAQ,CAAC;AAAA,IACzC;AAAA,EACF,CAAC;AAED,SAAO,KAAK,aAAa,OAAO,KAAc,QAAkB;AAC9D,UAAM,OAAQ,IAAI,QAAQ,CAAC;AAC3B,QAAI,CAAC,KAAK,OAAO;AACf,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,gBAAgB,CAAC;AAC/C;AAAA,IACF;AACA,UAAM,SAASA,YAAW,KAAK,KAAK;AACpC,QAAI,CAAC,QAAQ;AACX,UAAI,OAAO,GAAG,EAAE,KAAK,EAAE,OAAO,gBAAgB,CAAC;AAC/C;AAAA,IACF;AACA,UAAM,OAAQ,IAA6B;AAC3C,UAAM,UAAU,IAAI,QAAQ,EAAE,MAAM,KAAK,YAAY,CAAC;AACtD,QAAI;AACF,YAAM,KAAK,MAAM,QAAQ,MAAM,IAAI;AAAA,QACjC,OAAO,OAAO;AAAA,QACd,MAAM,OAAO;AAAA,QACb,aAAa,OAAO;AAAA,MACtB,CAAC;AACD,UAAI,GAAG,KAAK,OAAO;AACjB,cAAM,QAAQ;AAAA,UACZ;AAAA;AAAA;AAAA;AAAA;AAAA,UAKA,EAAE,IAAI,GAAG,KAAK,QAAQ;AAAA,QACxB;AAAA,MACF;AACA,YAAM,QAAQ,MAAM,QAAQ,MAAM,MAAM;AAAA,QACtC,OAAO,OAAO;AAAA,QACd,MAAM,OAAO;AAAA,QACb,aAAa,OAAO;AAAA,QACpB,cAAc,KAAK,eAAe;AAAA,MACpC,CAAC;AACD,UAAI,KAAK,EAAE,KAAK,MAAM,KAAK,KAAK,QAAQ,MAAM,KAAK,OAAO,CAAC;AAAA,IAC7D,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAM,SAAU,IAA4B,UAAU;AACtD,cAAQ,MAAM,iCAAiC,GAAG;AAClD,UAAI,OAAO,UAAU,OAAO,SAAS,MAAM,SAAS,GAAG,EAAE,KAAK,EAAE,OAAO,QAAQ,CAAC;AAAA,IAClF;AAAA,EACF,CAAC;AAED,SAAO;AACT;;;AC3FA,SAAS,UAAAC,eAA2C;AACpD,SAAS,gBAAgB;AACzB,SAAS,SAAS,eAAe;AACjC,SAAS,qBAAqB;AAE9B,IAAM,OAAO,QAAQ,cAAc,YAAY,GAAG,CAAC;AAEnD,IAAM,mBAAmB;AAAA,EACvB,QAAQ,MAAM,YAAY;AAAA,EAC1B,QAAQ,MAAM,MAAM,QAAQ,YAAY;AAAA,EACxC,QAAQ,MAAM,MAAM,YAAY;AAClC;AACA,IAAM,mBAAmB;AAAA,EACvB,QAAQ,MAAM,WAAW;AAAA,EACzB,QAAQ,MAAM,MAAM,QAAQ,WAAW;AAAA,EACvC,QAAQ,MAAM,MAAM,WAAW;AACjC;AAEA,eAAe,UAAU,OAA0D;AACjF,MAAI;AACJ,aAAW,KAAK,OAAO;AACrB,QAAI;AACF,YAAM,OAAO,MAAM,SAAS,CAAC;AAC7B,aAAO,EAAE,MAAM,GAAG,KAAK;AAAA,IACzB,SAAS,KAAK;AACZ,gBAAU;AAAA,IACZ;AAAA,EACF;AACA,QAAM,mBAAmB,QAAQ,UAAU,IAAI,MAAM,iBAAiB;AACxE;AAEO,SAAS,gBAAgB,UAA0B;AACxD,QAAM,SAASA,QAAO;AAEtB,SAAO,IAAI,eAAe,OAAO,MAAM,QAAQ;AAC7C,QAAI;AACF,YAAM,EAAE,KAAK,IAAI,MAAM,UAAU,gBAAgB;AACjD,UAAI,IAAI,gBAAgB,uCAAuC;AAC/D,UAAI,IAAI,iBAAiB,oCAAoC;AAC7D,UAAI,KAAK,IAAI;AAAA,IACf,SAAS,KAAK;AACZ,UAAI,OAAO,GAAG,EAAE,KAAK,0EAAqE;AAAA,IAC5F;AAAA,EACF,CAAC;AAED,SAAO,IAAI,cAAc,OAAO,MAAM,QAAQ;AAC5C,QAAI;AACF,YAAM,EAAE,KAAK,IAAI,MAAM,UAAU,gBAAgB;AACjD,UAAI,IAAI,gBAAgB,yBAAyB;AACjD,UAAI,IAAI,iBAAiB,oCAAoC;AAC7D,UAAI,KAAK,IAAI;AAAA,IACf,QAAQ;AACN,UAAI,OAAO,GAAG,EAAE,KAAK,yBAAyB;AAAA,IAChD;AAAA,EACF,CAAC;AAED,SAAO,IAAI,KAAK,OAAO,MAAe,QAAkB;AACtD,UAAM,OAAO,eAAe,QAAQ;AACpC,QAAI,IAAI,gBAAgB,0BAA0B;AAClD,QAAI,KAAK,IAAI;AAAA,EACf,CAAC;AAED,SAAO;AACT;AAEA,SAAS,eAAe,UAA0B;AAChD,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mCAM0B,QAAQ;AAAA;AAAA;AAAA;AAAA,mBAIxB,QAAQ;AAAA;AAAA;AAAA;AAAA,qBAIN,KAAK,UAAU,QAAQ,CAAC;AAAA;AAAA;AAAA;AAAA;AAK7C;;;ARnEO,SAAS,eAAe,KAAc,SAAwC;AACnF,QAAM,YAAY,QAAQ,YAAY,eAAe,QAAQ,OAAO,EAAE;AACtE,QAAM,SAAS,QAAQ,UAAU,QAAQ,IAAI,aAAa;AAE1D,MAAI,CAAC,QAAQ,aAAc,OAAM,IAAI,MAAM,uCAAuC;AAClF,MAAI,CAAC,QAAQ,mBAAmB,QAAQ,gBAAgB,SAAS,IAAI;AACnE,UAAM,IAAI,MAAM,6DAA6D;AAAA,EAC/E;AACA,MAAI,CAAC,QAAQ,aAAa,YAAY,CAAC,QAAQ,aAAa,cAAc;AACxE,UAAM,IAAI,MAAM,iEAAiE;AAAA,EACnF;AACA,MAAI,CAAC,QAAQ,YAAY,aAAa;AACpC,UAAM,IAAI,MAAM,kDAAkD;AAAA,EACpE;AACA,MAAI,CAAC,QAAQ,YAAY,iBAAiB,QAAQ,YAAY,cAAc,WAAW,GAAG;AACxF,YAAQ,KAAK,+FAA0F;AAAA,EACzG;AAEA,QAAM,SAAS,QAAQ,OAAO;AAC9B,SAAO,IAAI,QAAQ,KAAK,EAAE,OAAO,MAAM,CAAC,CAAC;AAEzC,QAAM,cAAc,gBAAgB,QAAQ;AAC5C,SAAO,IAAI,WAAW;AAEtB,QAAM,aAAa,eAAe;AAAA,IAChC,OAAO,QAAQ;AAAA,IACf,iBAAiB,QAAQ;AAAA,IACzB;AAAA,IACA;AAAA,IACA,gBAAgB,QAAQ,YAAY;AAAA,IACpC,gBAAgB,QAAQ,YAAY;AAAA,EACtC,CAAC;AACD,SAAO,IAAI,UAAU;AAErB,QAAM,cAAc,gBAAgB;AAAA,IAClC,iBAAiB,QAAQ;AAAA,IACzB;AAAA,IACA,eAAe,QAAQ,YAAY;AAAA,EACrC,CAAC;AAED,QAAM,cAAc,gBAAgB;AAAA,IAClC,cAAc,QAAQ;AAAA,IACtB,aAAa,QAAQ;AAAA,IACrB,SAAS,QAAQ;AAAA,IACjB,qBAAqB,QAAQ;AAAA,EAC/B,CAAC;AACD,SAAO,IAAI,aAAa,WAAW;AAEnC,QAAM,eAAe,iBAAiB;AACtC,SAAO,IAAI,aAAa,YAAY;AAEpC,MAAI,IAAI,UAAU,MAAM;AACxB,UAAQ,IAAI,2BAA2B,QAAQ,EAAE;AACjD,SAAO;AACT;","names":["Router","getSession","Router","getSession","Router","parsePrUrl","Router"]}
package/dist/style.css CHANGED
@@ -76,6 +76,22 @@
76
76
  background: var(--ct-accent);
77
77
  color: var(--ct-accent-fg);
78
78
  }
79
+ .coding-tab__repo-locked {
80
+ display: inline-flex;
81
+ align-items: center;
82
+ gap: 6px;
83
+ padding: 5px 9px;
84
+ border: 1px solid var(--ct-border);
85
+ border-radius: 6px;
86
+ background: var(--ct-bg);
87
+ color: var(--ct-fg-dim);
88
+ text-decoration: none;
89
+ font-size: 0.92em;
90
+ flex: 0 1 auto;
91
+ min-width: 0;
92
+ }
93
+ .coding-tab__repo-locked:hover { border-color: var(--ct-accent); color: var(--ct-fg); }
94
+ .coding-tab__repo-locked span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
79
95
  .coding-tab__user {
80
96
  margin-left: auto;
81
97
  font-size: 12px;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tstax/coding-tab",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Drop-in Cursor-style coding agent tab for any Express app. Plan + agent modes, GitHub OAuth, opens PRs against your repo via the Cursor SDK cloud runtime.",
5
5
  "type": "module",
6
6
  "exports": {