@superclawd-ai/pipelines 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{index-BYFgha0A.js → index-B-ujRSjH.js} +196 -180
- package/dist/index.html +1 -1
- package/dist-server/index.mjs +45 -45
- package/package.json +2 -2
package/dist-server/index.mjs
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
import { createRequire as __cr } from 'module'; const require = __cr(import.meta.url);
|
|
2
|
-
var sa=Object.defineProperty;var Vn=(e,t)=>()=>(e&&(t=e(e=0)),t);var oa=(e,t)=>{for(var n in t)sa(e,n,{get:t[n],enumerable:!0})};var Se,B,Ee,Ce,b,h,x,T,I,l,W,ce,O,ne,z,L,K,G,X,j,D,H,re,V=Vn(()=>{"use strict";Se={loggedIn:!0,account:{email:"owner@example.com",org:"superclawd"}},B=[{id:"ws_front",name:"Frontend",tier:"paid"},{id:"ws_core",name:"Core Platform",tier:"paid"},{id:"ws_sandbox",name:"Sandbox",tier:"free"}],Ee=[{id:"team_core",name:"Core"},{id:"team_review",name:"Reviewers"}],Ce=[{id:"agent_orch",name:"Orchestrator"},{id:"agent_reviewer",name:"Code Reviewer"},{id:"agent_fixer",name:"Bug Fixer"}],b=[{id:"conn_acme",provider:"linear",providerAccountName:"Acme",workspaceId:"ws_front",workspaceName:"Frontend",workingDir:"/Users/andy/code/acme",linkStatus:"linked",authStatus:"connected",lastSyncedAt:new Date(Date.now()-2*6e4).toISOString(),ticketCount:5},{id:"conn_web",provider:"linear",providerAccountName:"Acme Web",workspaceId:"ws_front",workspaceName:"Frontend",workingDir:"/Users/andy/code/acme-web",linkStatus:"linked",authStatus:"connected",lastSyncedAt:new Date(Date.now()-5*6e4).toISOString(),ticketCount:3},{id:"conn_beta",provider:"linear",providerAccountName:"Beta Board",workspaceId:null,workspaceName:null,workingDir:null,linkStatus:"unlinked",linkLostReason:"access_lost",authStatus:"connected",lastSyncedAt:new Date(Date.now()-3*36e5).toISOString(),ticketCount:3},{id:"conn_personal",provider:"linear",providerAccountName:"Personal",workspaceId:"ws_core",workspaceName:"Core Platform",workingDir:"/Users/andy/code/core",linkStatus:"linked",authStatus:"expired",lastSyncedAt:new Date(Date.now()-26*36e5).toISOString(),ticketCount:2},{id:"conn_jira_demo",provider:"jira",providerAccountName:"demo.atlassian.net",workspaceId:"ws_core",workspaceName:"Core Platform",workingDir:"/Users/andy/code/core",linkStatus:"linked",authStatus:"connected",lastSyncedAt:new Date(Date.now()-4*6e4).toISOString(),ticketCount:1,cloudId:"cloud-demo"},{id:"conn_local_front",provider:"local",providerAccountName:"Local",workspaceId:"ws_front",workspaceName:"Frontend",workingDir:null,linkStatus:"linked",authStatus:"connected",lastSyncedAt:null,ticketCount:2},{id:"conn_local_core",provider:"local",providerAccountName:"Local",workspaceId:"ws_core",workspaceName:"Core Platform",workingDir:null,linkStatus:"linked",authStatus:"connected",lastSyncedAt:null,ticketCount:0},{id:"conn_local_sandbox",provider:"local",providerAccountName:"Local",workspaceId:"ws_sandbox",workspaceName:"Sandbox",workingDir:null,linkStatus:"linked",authStatus:"connected",lastSyncedAt:null,ticketCount:0}],h=[{id:"jira_9001",connectionId:"conn_jira_demo",provider:"jira",externalIdentifier:"PLAT-1",project:"Platform",title:"Migrate auth service to the new token format",status:"in_progress",statusRaw:"In Progress",assignee:{name:"Andy"},reporter:{name:"Dana K."},dueDate:"2026-07-20",priority:2,labels:[{name:"backend"}],url:"https://demo.atlassian.net/browse/PLAT-1",workspaceId:"ws_core",description:"Swap the legacy HS256 tokens for the rotated RS256 pair."},{id:"t_123",connectionId:"conn_acme",provider:"linear",externalIdentifier:"ENG-123",project:"Auth",title:"Fix login redirect loop",status:"todo",statusRaw:"Todo",assignee:{name:"Andy"},reporter:{name:"Priya"},dueDate:"2026-07-15",priority:1,labels:[{name:"bug",color:"#e5484d"}],url:"https://linear.app/acme/issue/ENG-123",workspaceId:"ws_front",description:"After SSO, users bounce between /login and /dashboard."},{id:"t_140",connectionId:"conn_acme",provider:"linear",externalIdentifier:"ENG-140",project:"Auth",title:"Add SSO to the settings page",status:"in_progress",statusRaw:"In Progress",assignee:{name:"Andy"},priority:2,labels:[{name:"feature"}],url:"https://linear.app/acme/issue/ENG-140",workspaceId:"ws_front",description:"Wire the SAML flow into Settings \u2192 Security."},{id:"t_131",connectionId:"conn_acme",provider:"linear",externalIdentifier:"ENG-131",project:"Platform Config",title:"Migrate config loader to zod",status:"blocked",statusRaw:"Blocked",assignee:{name:"Andy"},priority:2,labels:[{name:"chore"}],url:"https://linear.app/acme/issue/ENG-131",workspaceId:"ws_front",description:"Replace ad-hoc env parsing with a zod schema."},{id:"t_118",connectionId:"conn_acme",provider:"linear",externalIdentifier:"ENG-118",project:"Design System",title:"Refactor the toast provider",status:"in_review",statusRaw:"In Review",assignee:{name:"Andy"},priority:3,labels:[{name:"refactor"}],url:"https://linear.app/acme/issue/ENG-118",workspaceId:"ws_front",description:"Consolidate the two toast systems into one."},{id:"t_090",connectionId:"conn_acme",provider:"linear",externalIdentifier:"ENG-90",project:"Billing",title:"Dark-mode polish on the billing page",status:"done",statusRaw:"Done",priority:4,labels:[{name:"ui"}],url:"https://linear.app/acme/issue/ENG-90",workspaceId:"ws_front"},{id:"t_112",connectionId:"conn_acme",provider:"linear",externalIdentifier:"ENG-112",project:"Dashboard",title:"Empty states for the dashboard widgets",status:"done",statusRaw:"Done",assignee:{name:"Andy"},priority:3,labels:[{name:"ui"}],url:"https://linear.app/acme/issue/ENG-112",workspaceId:"ws_front",description:"Show friendly empty states when a widget has no data yet."},{id:"t_201",connectionId:"conn_personal",provider:"linear",externalIdentifier:"CORE-201",project:"Directives",title:"Cache invalidation for the directives endpoint",status:"todo",statusRaw:"Todo",priority:2,labels:[{name:"perf"}],url:"https://linear.app/personal/issue/CORE-201",workspaceId:"ws_core"},{id:"t_web1",connectionId:"conn_web",provider:"linear",externalIdentifier:"WEB-11",project:"Marketing Site",title:"Rework the pricing page hero",status:"todo",statusRaw:"Todo",priority:3,labels:[{name:"ui"}],url:"https://linear.app/acme/issue/WEB-11",workspaceId:"ws_front",description:"New hero with the updated value prop and a single primary CTA."},{id:"t_web2",connectionId:"conn_web",provider:"linear",externalIdentifier:"WEB-14",project:"Marketing Site",title:"Add OpenGraph images to blog posts",status:"in_progress",statusRaw:"In Progress",priority:2,labels:[{name:"feature"}],url:"https://linear.app/acme/issue/WEB-14",workspaceId:"ws_front",description:"Generate per-post OG images at build time."},{id:"t_web3",connectionId:"conn_web",provider:"linear",externalIdentifier:"WEB-9",project:"Marketing Site",title:"Fix layout shift on the careers page",status:"in_review",statusRaw:"In Review",priority:1,labels:[{name:"bug"}],url:"https://linear.app/acme/issue/WEB-9",workspaceId:"ws_front",description:"Reserve space for the hero image to stop CLS."},{id:"t_local1",connectionId:"conn_local_front",provider:"local",externalIdentifier:"LOCAL-1",project:"Local",title:"Spike: evaluate the new charts library",status:"todo",statusRaw:"Backlog",priority:3,labels:[],url:"",workspaceId:"ws_front",description:"Timebox a day to compare two charting libraries for the dashboard."},{id:"t_local2",connectionId:"conn_local_front",provider:"local",externalIdentifier:"LOCAL-2",project:"Local",title:"Write the onboarding checklist doc",status:"todo",statusRaw:"Backlog",priority:4,labels:[],url:"",workspaceId:"ws_front"}],x={run_web2:{id:"run_web2",ticketId:"t_web2",ticketKey:"WEB-14",title:"Add OpenGraph images to blog posts",provider:"linear",workspaceId:"ws_front",workspaceName:"Frontend",assignmentKind:"agent",agentName:"Orchestrator",autonomy:"autonomous",status:"running",sessionId:"e1f2a3b4-5c6d-4e7f-8a90-1b2c3d4e5f60",branchName:"superclawd/WEB-14-9a8b",plan:[{text:"Add the OG image template",done:!0},{text:"Hook into the build",done:!1}],events:[{id:"e1",ts:"14:02",kind:"tool_use",text:"Read scripts/og.ts"}],createdAt:new Date(Date.now()-8*6e4).toISOString(),updatedAt:new Date().toISOString()},run_web3:{id:"run_web3",ticketId:"t_web3",ticketKey:"WEB-9",title:"Fix layout shift on the careers page",provider:"linear",workspaceId:"ws_front",workspaceName:"Frontend",assignmentKind:"team",teamName:"Core",autonomy:"autonomous",status:"stopped",stopReason:"completed",sessionId:"f6e5d4c3-2b1a-4098-8f7e-6d5c4b3a2190",branchName:"superclawd/WEB-9-3c4d",prUrl:"https://github.com/acme/web/pull/77",plan:[{text:"Reserve hero space",done:!0},{text:"Verify CLS budget",done:!0}],events:[{id:"e1",ts:"13:10",kind:"verification",text:"CLS 0.02 \u2713"}],createdAt:new Date(Date.now()-70*6e4).toISOString(),updatedAt:new Date(Date.now()-25*6e4).toISOString()},run_140:{id:"run_140",ticketId:"t_140",ticketKey:"ENG-140",title:"Add SSO to the settings page",provider:"linear",workspaceId:"ws_front",workspaceName:"Frontend",assignmentKind:"team",teamName:"Core",autonomy:"autonomous",status:"running",sessionId:"3f2a7c9e-1b4d-4a6f-9c2e-8d5b0a1f6e42",branchName:"superclawd/ENG-140-a1b2",plan:[{text:"Read the auth module",done:!0},{text:"Add the SAML client",done:!0},{text:"Wire into Settings \u2192 Security",done:!1},{text:"Add a regression test",done:!1}],events:[{id:"e1",ts:"12:01",kind:"tool_use",text:"Read src/auth.ts"},{id:"e2",ts:"12:02",kind:"commit",text:"wip: SAML client scaffold"},{id:"e3",ts:"12:03",kind:"guardrail",text:"warn: wrote to .env.example (logged)"},{id:"e4",ts:"12:04",kind:"tool_use",text:"Edit src/settings/security.tsx"}],createdAt:new Date(Date.now()-10*6e4).toISOString(),updatedAt:new Date().toISOString()},run_131:{id:"run_131",ticketId:"t_131",ticketKey:"ENG-131",title:"Migrate config loader to zod",provider:"linear",workspaceId:"ws_front",workspaceName:"Frontend",assignmentKind:"anyone",autonomy:"supervised",status:"blocked",sessionId:"a91c04b8-7e2f-4d13-b6a0-2c9f5e731d80",branchName:"superclawd/ENG-131-c3d4",plan:[{text:"Inventory env vars",done:!0},{text:"Draft the zod schema",done:!0},{text:"Swap the loader",done:!1},{text:"Update callers",done:!1}],events:[{id:"e1",ts:"11:40",kind:"tool_use",text:"Grep process.env usages"},{id:"e2",ts:"11:44",kind:"commit",text:"wip: zod schema draft"},{id:"e3",ts:"11:46",kind:"mcp_tool",text:"request_human_input(...)"}],blocker:{id:"b_131",origin:"agent",kind:"agent_question",status:"open",question:"Should the metrics call use the REST or GraphQL client? The repo has both."},createdAt:new Date(Date.now()-30*6e4).toISOString(),updatedAt:new Date(Date.now()-4*6e4).toISOString()},run_118:{id:"run_118",ticketId:"t_118",ticketKey:"ENG-118",title:"Refactor the toast provider",provider:"linear",workspaceId:"ws_front",workspaceName:"Frontend",assignmentKind:"agent",agentName:"Orchestrator",autonomy:"autonomous",status:"stopped",stopReason:"completed",sessionId:"c7d81f30-5a62-4e09-8b17-46f2a9c3e5d1",branchName:"superclawd/ENG-118-e5f6",prUrl:"https://github.com/acme/app/pull/42",plan:[{text:"Map both toast systems",done:!0},{text:"Unify into one provider",done:!0},{text:"Migrate call sites",done:!0},{text:"Verify build + tests",done:!0}],events:[{id:"e1",ts:"10:20",kind:"commit",text:"refactor: unify toast provider"},{id:"e2",ts:"10:35",kind:"verification",text:"build \u2713 \xB7 tests \u2713 \xB7 lint \u2713"},{id:"e3",ts:"10:36",kind:"mcp_tool",text:"mark_complete(...) \u2192 draft PR #42"}],createdAt:new Date(Date.now()-60*6e4).toISOString(),updatedAt:new Date(Date.now()-20*6e4).toISOString()},run_112:{id:"run_112",ticketId:"t_112",ticketKey:"ENG-112",title:"Empty states for the dashboard widgets",provider:"linear",workspaceId:"ws_front",workspaceName:"Frontend",assignmentKind:"team",teamName:"Core",autonomy:"autonomous",status:"stopped",stopReason:"completed",sessionId:"d4e5f6a1-9c02-4b38-a7e1-5f0c2b83d419",branchName:"superclawd/ENG-112-b7c8",prUrl:"https://github.com/acme/app/pull/38",plan:[{text:"Design the empty-state component",done:!0},{text:"Wire it into each widget",done:!0},{text:"Verify build + tests",done:!0}],events:[{id:"e1",ts:"09:10",kind:"commit",text:"feat: dashboard empty states"},{id:"e2",ts:"09:22",kind:"verification",text:"build \u2713 \xB7 tests \u2713"},{id:"e3",ts:"09:23",kind:"state_change",text:"approved by human \u2192 Done"}],createdAt:new Date(Date.now()-90*6e4).toISOString(),updatedAt:new Date(Date.now()-40*6e4).toISOString()},run_090:{id:"run_090",ticketId:"t_090",ticketKey:"ENG-90",title:"Dark-mode polish on the billing page",provider:"linear",workspaceId:"ws_front",workspaceName:"Frontend",assignmentKind:"team",teamName:"Core",autonomy:"supervised",status:"not_started",plan:[],events:[],createdAt:new Date(Date.now()-5*6e4).toISOString(),updatedAt:new Date(Date.now()-5*6e4).toISOString()}},T={t_140:"run_140",t_131:"run_131",t_118:"run_118",t_090:"run_090",t_112:"run_112",t_web2:"run_web2",t_web3:"run_web3"},I={jira_9001:"in_progress",t_123:"backlog",t_090:"backlog",t_140:"in_progress",t_131:"needs_input",t_118:"in_review",t_112:"done",t_201:"backlog",t_web1:"backlog",t_web2:"in_progress",t_web3:"in_review",t_local1:"backlog",t_local2:"backlog"},l={defaultAutonomy:"supervised",maxConcurrentRuns:3,branchPrefix:"feature",defaultModel:"opus",defaultEffort:"high"},W={conv_1:{id:"conv_1",title:"Debounce the workspace search",workspaceId:"ws_front",workspaceName:"Frontend",tier:"Medium",effort:"medium",sessionId:"b8e3d1a4-6f27-4c95-90ab-1e7d32c8f0a5",messages:[{id:"m1",role:"user",text:"How should I debounce the workspace search input?",ts:"09:02"},{id:"m2",role:"assistant",text:`Running under Frontend (Medium \xB7 Sonnet) \u2014 your team, guardrails and memory are in context.
|
|
2
|
+
var oa=Object.defineProperty;var Zn=(e,t)=>()=>(e&&(t=e(e=0)),t);var ia=(e,t)=>{for(var n in t)oa(e,n,{get:t[n],enumerable:!0})};var Ie,$,Se,xe,b,h,x,T,S,l,W,ce,O,se,z,L,K,G,X,j,D,H,Y,Z=Zn(()=>{"use strict";Ie={loggedIn:!0,account:{email:"owner@example.com",org:"superclawd"}},$=[{id:"ws_front",name:"Frontend",tier:"paid"},{id:"ws_core",name:"Core Platform",tier:"paid"},{id:"ws_sandbox",name:"Sandbox",tier:"free"}],Se=[{id:"team_core",name:"Core"},{id:"team_review",name:"Reviewers"}],xe=[{id:"agent_orch",name:"Orchestrator"},{id:"agent_reviewer",name:"Code Reviewer"},{id:"agent_fixer",name:"Bug Fixer"}],b=[{id:"conn_acme",provider:"linear",providerAccountName:"Acme",workspaceId:"ws_front",workspaceName:"Frontend",workingDir:"/Users/andy/code/acme",linkStatus:"linked",authStatus:"connected",lastSyncedAt:new Date(Date.now()-2*6e4).toISOString(),ticketCount:5},{id:"conn_web",provider:"linear",providerAccountName:"Acme Web",workspaceId:"ws_front",workspaceName:"Frontend",workingDir:"/Users/andy/code/acme-web",linkStatus:"linked",authStatus:"connected",lastSyncedAt:new Date(Date.now()-5*6e4).toISOString(),ticketCount:3},{id:"conn_beta",provider:"linear",providerAccountName:"Beta Board",workspaceId:null,workspaceName:null,workingDir:null,linkStatus:"unlinked",linkLostReason:"access_lost",authStatus:"connected",lastSyncedAt:new Date(Date.now()-3*36e5).toISOString(),ticketCount:3},{id:"conn_personal",provider:"linear",providerAccountName:"Personal",workspaceId:"ws_core",workspaceName:"Core Platform",workingDir:"/Users/andy/code/core",linkStatus:"linked",authStatus:"expired",lastSyncedAt:new Date(Date.now()-26*36e5).toISOString(),ticketCount:2},{id:"conn_jira_demo",provider:"jira",providerAccountName:"demo.atlassian.net",workspaceId:"ws_core",workspaceName:"Core Platform",workingDir:"/Users/andy/code/core",linkStatus:"linked",authStatus:"connected",lastSyncedAt:new Date(Date.now()-4*6e4).toISOString(),ticketCount:1,cloudId:"cloud-demo"},{id:"conn_local_front",provider:"local",providerAccountName:"Local",workspaceId:"ws_front",workspaceName:"Frontend",workingDir:null,linkStatus:"linked",authStatus:"connected",lastSyncedAt:null,ticketCount:2},{id:"conn_local_core",provider:"local",providerAccountName:"Local",workspaceId:"ws_core",workspaceName:"Core Platform",workingDir:null,linkStatus:"linked",authStatus:"connected",lastSyncedAt:null,ticketCount:0},{id:"conn_local_sandbox",provider:"local",providerAccountName:"Local",workspaceId:"ws_sandbox",workspaceName:"Sandbox",workingDir:null,linkStatus:"linked",authStatus:"connected",lastSyncedAt:null,ticketCount:0}],h=[{id:"jira_9001",connectionId:"conn_jira_demo",provider:"jira",externalIdentifier:"PLAT-1",project:"Platform",title:"Migrate auth service to the new token format",status:"in_progress",statusRaw:"In Progress",assignee:{name:"Andy"},reporter:{name:"Dana K."},dueDate:"2026-07-20",priority:2,labels:[{name:"backend"}],url:"https://demo.atlassian.net/browse/PLAT-1",workspaceId:"ws_core",description:"Swap the legacy HS256 tokens for the rotated RS256 pair."},{id:"t_123",connectionId:"conn_acme",provider:"linear",externalIdentifier:"ENG-123",project:"Auth",title:"Fix login redirect loop",status:"todo",statusRaw:"Todo",assignee:{name:"Andy"},reporter:{name:"Priya"},dueDate:"2026-07-15",priority:1,labels:[{name:"bug",color:"#e5484d"}],url:"https://linear.app/acme/issue/ENG-123",workspaceId:"ws_front",description:"After SSO, users bounce between /login and /dashboard."},{id:"t_140",connectionId:"conn_acme",provider:"linear",externalIdentifier:"ENG-140",project:"Auth",title:"Add SSO to the settings page",status:"in_progress",statusRaw:"In Progress",assignee:{name:"Andy"},priority:2,labels:[{name:"feature"}],url:"https://linear.app/acme/issue/ENG-140",workspaceId:"ws_front",description:"Wire the SAML flow into Settings \u2192 Security."},{id:"t_131",connectionId:"conn_acme",provider:"linear",externalIdentifier:"ENG-131",project:"Platform Config",title:"Migrate config loader to zod",status:"blocked",statusRaw:"Blocked",assignee:{name:"Andy"},priority:2,labels:[{name:"chore"}],url:"https://linear.app/acme/issue/ENG-131",workspaceId:"ws_front",description:"Replace ad-hoc env parsing with a zod schema."},{id:"t_118",connectionId:"conn_acme",provider:"linear",externalIdentifier:"ENG-118",project:"Design System",title:"Refactor the toast provider",status:"in_review",statusRaw:"In Review",assignee:{name:"Andy"},priority:3,labels:[{name:"refactor"}],url:"https://linear.app/acme/issue/ENG-118",workspaceId:"ws_front",description:"Consolidate the two toast systems into one."},{id:"t_090",connectionId:"conn_acme",provider:"linear",externalIdentifier:"ENG-90",project:"Billing",title:"Dark-mode polish on the billing page",status:"done",statusRaw:"Done",priority:4,labels:[{name:"ui"}],url:"https://linear.app/acme/issue/ENG-90",workspaceId:"ws_front"},{id:"t_112",connectionId:"conn_acme",provider:"linear",externalIdentifier:"ENG-112",project:"Dashboard",title:"Empty states for the dashboard widgets",status:"done",statusRaw:"Done",assignee:{name:"Andy"},priority:3,labels:[{name:"ui"}],url:"https://linear.app/acme/issue/ENG-112",workspaceId:"ws_front",description:"Show friendly empty states when a widget has no data yet."},{id:"t_201",connectionId:"conn_personal",provider:"linear",externalIdentifier:"CORE-201",project:"Directives",title:"Cache invalidation for the directives endpoint",status:"todo",statusRaw:"Todo",priority:2,labels:[{name:"perf"}],url:"https://linear.app/personal/issue/CORE-201",workspaceId:"ws_core"},{id:"t_web1",connectionId:"conn_web",provider:"linear",externalIdentifier:"WEB-11",project:"Marketing Site",title:"Rework the pricing page hero",status:"todo",statusRaw:"Todo",priority:3,labels:[{name:"ui"}],url:"https://linear.app/acme/issue/WEB-11",workspaceId:"ws_front",description:"New hero with the updated value prop and a single primary CTA."},{id:"t_web2",connectionId:"conn_web",provider:"linear",externalIdentifier:"WEB-14",project:"Marketing Site",title:"Add OpenGraph images to blog posts",status:"in_progress",statusRaw:"In Progress",priority:2,labels:[{name:"feature"}],url:"https://linear.app/acme/issue/WEB-14",workspaceId:"ws_front",description:"Generate per-post OG images at build time."},{id:"t_web3",connectionId:"conn_web",provider:"linear",externalIdentifier:"WEB-9",project:"Marketing Site",title:"Fix layout shift on the careers page",status:"in_review",statusRaw:"In Review",priority:1,labels:[{name:"bug"}],url:"https://linear.app/acme/issue/WEB-9",workspaceId:"ws_front",description:"Reserve space for the hero image to stop CLS."},{id:"t_local1",connectionId:"conn_local_front",provider:"local",externalIdentifier:"LOCAL-1",project:"Local",title:"Spike: evaluate the new charts library",status:"todo",statusRaw:"Backlog",priority:3,labels:[],url:"",workspaceId:"ws_front",description:"Timebox a day to compare two charting libraries for the dashboard."},{id:"t_local2",connectionId:"conn_local_front",provider:"local",externalIdentifier:"LOCAL-2",project:"Local",title:"Write the onboarding checklist doc",status:"todo",statusRaw:"Backlog",priority:4,labels:[],url:"",workspaceId:"ws_front"}],x={run_web2:{id:"run_web2",ticketId:"t_web2",ticketKey:"WEB-14",title:"Add OpenGraph images to blog posts",provider:"linear",workspaceId:"ws_front",workspaceName:"Frontend",assignmentKind:"agent",agentName:"Orchestrator",autonomy:"autonomous",status:"running",sessionId:"e1f2a3b4-5c6d-4e7f-8a90-1b2c3d4e5f60",branchName:"superclawd/WEB-14-9a8b",plan:[{text:"Add the OG image template",done:!0},{text:"Hook into the build",done:!1}],events:[{id:"e1",ts:"14:02",kind:"tool_use",text:"Read scripts/og.ts"}],createdAt:new Date(Date.now()-8*6e4).toISOString(),updatedAt:new Date().toISOString()},run_web3:{id:"run_web3",ticketId:"t_web3",ticketKey:"WEB-9",title:"Fix layout shift on the careers page",provider:"linear",workspaceId:"ws_front",workspaceName:"Frontend",assignmentKind:"team",teamName:"Core",autonomy:"autonomous",status:"stopped",stopReason:"completed",sessionId:"f6e5d4c3-2b1a-4098-8f7e-6d5c4b3a2190",branchName:"superclawd/WEB-9-3c4d",prUrl:"https://github.com/acme/web/pull/77",plan:[{text:"Reserve hero space",done:!0},{text:"Verify CLS budget",done:!0}],events:[{id:"e1",ts:"13:10",kind:"verification",text:"CLS 0.02 \u2713"}],createdAt:new Date(Date.now()-70*6e4).toISOString(),updatedAt:new Date(Date.now()-25*6e4).toISOString()},run_140:{id:"run_140",ticketId:"t_140",ticketKey:"ENG-140",title:"Add SSO to the settings page",provider:"linear",workspaceId:"ws_front",workspaceName:"Frontend",assignmentKind:"team",teamName:"Core",autonomy:"autonomous",status:"running",sessionId:"3f2a7c9e-1b4d-4a6f-9c2e-8d5b0a1f6e42",branchName:"superclawd/ENG-140-a1b2",plan:[{text:"Read the auth module",done:!0},{text:"Add the SAML client",done:!0},{text:"Wire into Settings \u2192 Security",done:!1},{text:"Add a regression test",done:!1}],events:[{id:"e1",ts:"12:01",kind:"tool_use",text:"Read src/auth.ts"},{id:"e2",ts:"12:02",kind:"commit",text:"wip: SAML client scaffold"},{id:"e3",ts:"12:03",kind:"guardrail",text:"warn: wrote to .env.example (logged)"},{id:"e4",ts:"12:04",kind:"tool_use",text:"Edit src/settings/security.tsx"}],createdAt:new Date(Date.now()-10*6e4).toISOString(),updatedAt:new Date().toISOString()},run_131:{id:"run_131",ticketId:"t_131",ticketKey:"ENG-131",title:"Migrate config loader to zod",provider:"linear",workspaceId:"ws_front",workspaceName:"Frontend",assignmentKind:"anyone",autonomy:"supervised",status:"blocked",sessionId:"a91c04b8-7e2f-4d13-b6a0-2c9f5e731d80",branchName:"superclawd/ENG-131-c3d4",plan:[{text:"Inventory env vars",done:!0},{text:"Draft the zod schema",done:!0},{text:"Swap the loader",done:!1},{text:"Update callers",done:!1}],events:[{id:"e1",ts:"11:40",kind:"tool_use",text:"Grep process.env usages"},{id:"e2",ts:"11:44",kind:"commit",text:"wip: zod schema draft"},{id:"e3",ts:"11:46",kind:"mcp_tool",text:"request_human_input(...)"}],blocker:{id:"b_131",origin:"agent",kind:"agent_question",status:"open",question:"Should the metrics call use the REST or GraphQL client? The repo has both."},createdAt:new Date(Date.now()-30*6e4).toISOString(),updatedAt:new Date(Date.now()-4*6e4).toISOString()},run_118:{id:"run_118",ticketId:"t_118",ticketKey:"ENG-118",title:"Refactor the toast provider",provider:"linear",workspaceId:"ws_front",workspaceName:"Frontend",assignmentKind:"agent",agentName:"Orchestrator",autonomy:"autonomous",status:"stopped",stopReason:"completed",sessionId:"c7d81f30-5a62-4e09-8b17-46f2a9c3e5d1",branchName:"superclawd/ENG-118-e5f6",prUrl:"https://github.com/acme/app/pull/42",plan:[{text:"Map both toast systems",done:!0},{text:"Unify into one provider",done:!0},{text:"Migrate call sites",done:!0},{text:"Verify build + tests",done:!0}],events:[{id:"e1",ts:"10:20",kind:"commit",text:"refactor: unify toast provider"},{id:"e2",ts:"10:35",kind:"verification",text:"build \u2713 \xB7 tests \u2713 \xB7 lint \u2713"},{id:"e3",ts:"10:36",kind:"mcp_tool",text:"mark_complete(...) \u2192 draft PR #42"}],createdAt:new Date(Date.now()-60*6e4).toISOString(),updatedAt:new Date(Date.now()-20*6e4).toISOString()},run_112:{id:"run_112",ticketId:"t_112",ticketKey:"ENG-112",title:"Empty states for the dashboard widgets",provider:"linear",workspaceId:"ws_front",workspaceName:"Frontend",assignmentKind:"team",teamName:"Core",autonomy:"autonomous",status:"stopped",stopReason:"completed",sessionId:"d4e5f6a1-9c02-4b38-a7e1-5f0c2b83d419",branchName:"superclawd/ENG-112-b7c8",prUrl:"https://github.com/acme/app/pull/38",plan:[{text:"Design the empty-state component",done:!0},{text:"Wire it into each widget",done:!0},{text:"Verify build + tests",done:!0}],events:[{id:"e1",ts:"09:10",kind:"commit",text:"feat: dashboard empty states"},{id:"e2",ts:"09:22",kind:"verification",text:"build \u2713 \xB7 tests \u2713"},{id:"e3",ts:"09:23",kind:"state_change",text:"approved by human \u2192 Done"}],createdAt:new Date(Date.now()-90*6e4).toISOString(),updatedAt:new Date(Date.now()-40*6e4).toISOString()},run_090:{id:"run_090",ticketId:"t_090",ticketKey:"ENG-90",title:"Dark-mode polish on the billing page",provider:"linear",workspaceId:"ws_front",workspaceName:"Frontend",assignmentKind:"team",teamName:"Core",autonomy:"supervised",status:"not_started",plan:[],events:[],createdAt:new Date(Date.now()-5*6e4).toISOString(),updatedAt:new Date(Date.now()-5*6e4).toISOString()}},T={t_140:"run_140",t_131:"run_131",t_118:"run_118",t_090:"run_090",t_112:"run_112",t_web2:"run_web2",t_web3:"run_web3"},S={jira_9001:"in_progress",t_123:"backlog",t_090:"backlog",t_140:"in_progress",t_131:"needs_input",t_118:"in_review",t_112:"done",t_201:"backlog",t_web1:"backlog",t_web2:"in_progress",t_web3:"in_review",t_local1:"backlog",t_local2:"backlog"},l={defaultAutonomy:"supervised",maxConcurrentRuns:3,branchPrefix:"feature",defaultModel:"opus",defaultEffort:"high"},W={conv_1:{id:"conv_1",title:"Debounce the workspace search",workspaceId:"ws_front",workspaceName:"Frontend",tier:"Medium",effort:"medium",sessionId:"b8e3d1a4-6f27-4c95-90ab-1e7d32c8f0a5",messages:[{id:"m1",role:"user",text:"How should I debounce the workspace search input?",ts:"09:02"},{id:"m2",role:"assistant",text:`Running under Frontend (Medium \xB7 Sonnet) \u2014 your team, guardrails and memory are in context.
|
|
3
3
|
|
|
4
4
|
Debounce the input ~300ms with a trailing call, cancel in-flight requests on a new keystroke, and skip the fetch for empty queries. Want me to sketch the hook?`,ts:"09:02"}],starred:!0,createdAt:new Date(Date.now()-3*36e5).toISOString(),updatedAt:new Date(Date.now()-3*36e5).toISOString()},conv_2:{id:"conv_2",title:"Explain the credit reservation flow",workspaceId:"ws_core",workspaceName:"Core Platform",tier:"High",effort:"high",sessionId:"3f9c7e21-8a4b-4d16-b0e2-59c8a1f4d7b3",messages:[{id:"m1",role:"user",text:"Walk me through how credits get reserved and finalized on a metered fetch.",ts:"11:20"},{id:"m2",role:"assistant",text:`Running under Core Platform (High \xB7 Opus) \u2014 your team, guardrails and memory are in context.
|
|
5
5
|
|
|
6
6
|
Reserve holds an estimated amount up front, the fetch runs, then finalize settles the real cost and refunds the difference. Want the FOR-UPDATE details?`,ts:"11:20"}],starred:!1,createdAt:new Date(Date.now()-26*36e5).toISOString(),updatedAt:new Date(Date.now()-26*36e5).toISOString()},conv_3:{id:"conv_3",title:"Draft release notes for v2",workspaceId:"ws_front",workspaceName:"Frontend",tier:"Instant",effort:"low",sessionId:"a17d54c9-2be6-4f80-9c3a-6d0f8b2e1a44",messages:[{id:"m1",role:"user",text:"Draft short release notes for the v2 dashboard changes.",ts:"08:05"},{id:"m2",role:"assistant",text:`Running under Frontend (Instant \xB7 Haiku) \u2014 your team, guardrails and memory are in context.
|
|
7
7
|
|
|
8
|
-
Here\u2019s a first pass grouped by Added / Changed / Fixed. Want me to tighten the copy?`,ts:"08:05"}],starred:!1,createdAt:new Date(Date.now()-2*24*36e5).toISOString(),updatedAt:new Date(Date.now()-2*24*36e5).toISOString()}},ce=["conv_3","conv_2","conv_1"],O={sched_1:{id:"sched_1",name:"Triage new bugs",prompt:"Triage new Linear bugs: reproduce, label severity, and open a follow-up for anything urgent.",workspaceId:"ws_front",workspaceName:"Frontend",assignmentKind:"team",teamId:"team_core",teamName:"Core",cadence:"Every 2 hours",enabled:!0,starred:!0,createdAt:new Date(Date.now()-8*24*36e5).toISOString(),lastRunAt:new Date(Date.now()-40*6e4).toISOString(),nextRunAt:new Date(Date.now()+80*6e4).toISOString()},sched_2:{id:"sched_2",name:"Nightly flaky-test sweep",prompt:"Run the test suite, detect flakes, and file an issue per flaky test with the failing output.",workspaceId:"ws_front",workspaceName:"Frontend",assignmentKind:"anyone",cadence:"Every 1 day",enabled:!1,starred:!1,createdAt:new Date(Date.now()-20*24*36e5).toISOString(),lastRunAt:new Date(Date.now()-14*36e5).toISOString(),nextRunAt:null},sched_3:{id:"sched_3",name:"Reconcile credit ledger",prompt:"Reconcile the credit ledger against Stripe: flag drift over $1 and open a dispute task for anything unexplained.",workspaceId:"ws_core",workspaceName:"Core Platform",assignmentKind:"agent",agentId:"agent_orch",agentName:"Orchestrator",cadence:"Every 6 hours",enabled:!0,starred:!0,createdAt:new Date(Date.now()-5*24*36e5).toISOString(),lastRunAt:new Date(Date.now()-2*36e5).toISOString(),nextRunAt:new Date(Date.now()+4*36e5).toISOString()}},ne=["sched_1","sched_2","sched_3"],z={sched_1:Array.from({length:14},(e,t)=>({id:`srun_1_${14-t}`,scheduleId:"sched_1",status:t===0?"success":t%6===0?"failed":"success",sessionId:`a1b2c3${String(14-t).padStart(2,"0")}-6f27-4c95-90ab-1e7d32c8f0a5`,startedAt:new Date(Date.now()-(40+t*120)*6e4).toISOString(),finishedAt:new Date(Date.now()-(40+t*120)*6e4+95e3).toISOString(),output:t%6===0?"Failed: could not reach the Linear API (429). Will retry next tick.":`Triaged ${2+t%4} new issues \xB7 labeled severity \xB7 opened ${t%3===0?1:0} follow-up. (mocked output)`})),sched_2:Array.from({length:3},(e,t)=>({id:`srun_2_${3-t}`,scheduleId:"sched_2",status:"success",sessionId:`d4e5f6${String(3-t).padStart(2,"0")}-9c02-4b38-a7e1-5f0c2b83d419`,startedAt:new Date(Date.now()-(14+t*24)*36e5).toISOString(),finishedAt:new Date(Date.now()-(14+t*24)*36e5+22e4).toISOString(),output:`Ran the suite \xB7 ${t===0?"no":"1"} flaky test found${t===0?"":" \xB7 filed 1 issue"}. (mocked output)`})),sched_3:Array.from({length:7},(e,t)=>({id:`srun_3_${7-t}`,scheduleId:"sched_3",status:"success",sessionId:`c7d8e9${String(7-t).padStart(2,"0")}-4b38-4c95-90ab-1e7d32c8f0a5`,startedAt:new Date(Date.now()-(2+t*6)*36e5).toISOString(),finishedAt:new Date(Date.now()-(2+t*6)*36e5+18e4).toISOString(),output:`Reconciled ${180+t*12} charges \xB7 ${t%3===0?"no drift":"flagged 1 mismatch \xB7 opened dispute task"}. (mocked output)`}))},L={},K=[],G={runSeq:500,convSeq:3,schedSeq:3,schedRunSeq:100,batchSeq:0,localSeq:2},X=[],j=[],D=[],H=[],re=[{id:"jira_9002",connectionId:"conn_jira_demo",provider:"jira",externalIdentifier:"PLAT-2",project:"Platform",title:"Add rate-limit headers to the public API",status:"todo",statusRaw:"To Do",assignee:{name:"Priya"},priority:3,labels:[{name:"api"}],url:"https://demo.atlassian.net/browse/PLAT-2",workspaceId:"ws_core",description:"Surface X-RateLimit-* so clients can back off."},{id:"s_150",connectionId:"conn_acme",provider:"linear",externalIdentifier:"ENG-150",project:"Frontend App",title:"Debounce the workspace search input",status:"todo",statusRaw:"Todo",assignee:{name:"Priya"},priority:3,labels:[{name:"ui"}],url:"https://linear.app/acme/issue/ENG-150",workspaceId:"ws_front",description:"The search fires a request per keystroke; debounce ~300ms."},{id:"s_151",connectionId:"conn_acme",provider:"linear",externalIdentifier:"ENG-151",project:"CI",title:"Flaky test: auth.session.spec times out on CI",status:"todo",statusRaw:"Todo",assignee:{name:"Sam"},priority:1,labels:[{name:"bug"},{name:"ci"}],url:"https://linear.app/acme/issue/ENG-151",workspaceId:"ws_front",description:"Intermittent 30s timeout; suspect a missing await."},{id:"s_152",connectionId:"conn_acme",provider:"linear",externalIdentifier:"ENG-152",project:"Public API",title:"Add rate-limit headers to the public API",status:"backlog",statusRaw:"Backlog",priority:2,labels:[{name:"api"}],url:"https://linear.app/acme/issue/ENG-152",workspaceId:"ws_front",description:"Expose X-RateLimit-* on responses."},{id:"s_160",connectionId:"conn_acme",provider:"linear",externalIdentifier:"ENG-160",project:"Auth",title:"Login page: remember-me checkbox does nothing",status:"todo",statusRaw:"Todo",assignee:{name:"Andy"},priority:2,labels:[{name:"bug"}],url:"https://linear.app/acme/issue/ENG-160",workspaceId:"ws_front",description:"Session still expires on browser close."},{id:"s_210",connectionId:"conn_personal",provider:"linear",externalIdentifier:"CORE-210",project:"Directives",title:"Batch the memory-read endpoint",status:"backlog",statusRaw:"Backlog",priority:2,labels:[{name:"perf"}],url:"https://linear.app/personal/issue/CORE-210",workspaceId:"ws_core",description:"Coalesce N per-memory fetches into one batch call."},{id:"s_211",connectionId:"conn_personal",provider:"linear",externalIdentifier:"CORE-211",project:"Docs",title:"Document the directives cache TTL",status:"todo",statusRaw:"Todo",priority:4,labels:[{name:"docs"}],url:"https://linear.app/personal/issue/CORE-211",workspaceId:"ws_core"}]});var Zn={};oa(Zn,{seedFromFixtures:()=>ia});async function ia(){let e=E();await Qe.bulkCreate(b.map(n=>({...n,userId:e}))),await Ie.bulkCreate(h.map((n,r)=>({...n,userId:e,column:I[n.id]??"backlog",position:r}))),await Pe.bulkCreate(Object.values(x).map(n=>({...n,userId:e}))),await Je.bulkCreate(ne.map(n=>({...O[n],userId:e})));let t=Object.keys(z).flatMap(n=>{let r=z[n];return r.map((s,o)=>({...s,userId:e,seq:r.length-o}))});await Oe.bulkCreate(t),await De.bulkCreate(ce.map(n=>({...W[n],userId:e}))),await je.bulkCreate(X.map(n=>({...n,userId:e}))),await ze.bulkCreate(j.map(n=>({...n,userId:e}))),await $e.bulkCreate(D.map((n,r)=>({...n,userId:e,position:n.position??r}))),await Me.bulkCreate(H.map((n,r)=>({...n,userId:e,position:n.position??r}))),await ct.create({userId:e,defaultAutonomy:l.defaultAutonomy,maxConcurrentRuns:l.maxConcurrentRuns??3,branchPrefix:l.branchPrefix??"feature",connectedWorkspaces:l.connectedWorkspaces??null,workspaceDirs:l.workspaceDirs??null,defaultModel:l.defaultModel??null,defaultEffort:l.defaultEffort??null})}var er=Vn(()=>{"use strict";V();dt()});import aa from"node:os";import as from"node:path";import ca from"node:fs";import{Sequelize as da,DataTypes as a}from"sequelize";import{Umzug as ua}from"umzug";function E(){return cs}function ln(e){cs=e}async function is(e){try{return await e.count()}catch{return 0}}async function us(){if(ca.mkdirSync(as.dirname(un),{recursive:!0}),await y.authenticate(),await la.up(),await is(Ie)===0&&await is(De)===0&&E()===Wt){let{seedFromFixtures:t}=await Promise.resolve().then(()=>(er(),Zn));await t()}}var un,Wt,cs,y,Qe,Ie,Pe,Je,Oe,Kt,De,ct,ut,je,ze,Me,$e,ds,la,dt=Vn(()=>{"use strict";un=process.env.PIPELINES_DB_PATH??as.join(aa.homedir(),".superclawd","pipelines.db"),Wt=process.env.PIPELINES_DEV_USER_ID??"dev-user",cs=Wt;y=new da({dialect:"sqlite",storage:un,logging:!1}),Qe=y.define("Connection",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,provider:a.STRING,providerAccountName:a.STRING,displayName:a.STRING,workspaceId:a.STRING,workspaceName:a.STRING,workingDir:a.STRING,linkStatus:a.STRING,linkLostReason:a.STRING,authStatus:a.STRING,lastSyncedAt:a.STRING,ticketCount:a.INTEGER,syncFilters:a.JSON,cloudId:a.STRING,providerAccountId:a.STRING},{tableName:"connections",underscored:!0,timestamps:!1}),Ie=y.define("Ticket",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,connectionId:a.STRING,provider:a.STRING,externalIdentifier:a.STRING,title:a.STRING,description:a.TEXT,status:a.STRING,statusRaw:a.STRING,assignee:a.JSON,reporter:a.JSON,dueDate:a.STRING,priority:a.INTEGER,labels:a.JSON,project:a.STRING,parent:a.JSON,model:a.STRING,effort:a.STRING,blockedReason:a.TEXT,comments:a.JSON,additionalContext:a.TEXT,plan:a.TEXT,conclusion:a.TEXT,additionalContextUpdatedAt:a.STRING,planUpdatedAt:a.STRING,conclusionUpdatedAt:a.STRING,createdAt:a.STRING,updatedAt:a.STRING,prLinks:a.JSON,attempt:a.INTEGER,dependsOn:a.JSON,prMonitor:a.BOOLEAN,prMonitorCount:a.INTEGER,prMonitorLastAt:a.STRING,prMonitorNote:a.TEXT,url:a.STRING,workspaceId:a.STRING,workingDir:a.STRING,column:a.STRING,position:a.INTEGER},{tableName:"tickets",underscored:!0,timestamps:!1}),Pe=y.define("Run",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,ticketId:a.STRING,ticketKey:a.STRING,title:a.STRING,provider:a.STRING,workspaceId:a.STRING,workspaceName:a.STRING,assignmentKind:a.STRING,teamName:a.STRING,agentName:a.STRING,autonomy:a.STRING,status:a.STRING,stopReason:a.STRING,sessionId:a.STRING,branchName:a.STRING,prUrl:a.STRING,worktrees:a.JSON,blocker:a.JSON,plan:a.JSON,events:a.JSON,resultText:a.TEXT,pid:a.INTEGER,pgid:a.INTEGER,createdAt:a.STRING,updatedAt:a.STRING},{tableName:"runs",underscored:!0,timestamps:!1}),Je=y.define("Schedule",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,name:a.STRING,prompt:a.TEXT,workspaceId:a.STRING,workspaceName:a.STRING,assignmentKind:a.STRING,teamId:a.STRING,teamName:a.STRING,agentId:a.STRING,agentName:a.STRING,cadence:a.STRING,workingDir:a.STRING,model:a.STRING,effort:a.STRING,enabled:a.BOOLEAN,starred:a.BOOLEAN,createdAt:a.STRING,lastRunAt:a.STRING,nextRunAt:a.STRING},{tableName:"schedules",underscored:!0,timestamps:!1}),Oe=y.define("ScheduleRun",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,scheduleId:a.STRING,status:a.STRING,sessionId:a.STRING,startedAt:a.STRING,finishedAt:a.STRING,output:a.TEXT,model:a.STRING,effort:a.STRING,seq:a.INTEGER},{tableName:"schedule_runs",underscored:!0,timestamps:!1}),Kt=y.define("Batch",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,name:a.STRING,workspaceId:a.STRING,workspaceName:a.STRING,ticketIds:a.JSON,mode:a.STRING,advanceMode:a.STRING,stackDependentWork:a.BOOLEAN,status:a.STRING,budgetCap:a.INTEGER,pausedReason:a.TEXT,runsStarted:a.INTEGER,createdAt:a.STRING,updatedAt:a.STRING},{tableName:"batches",underscored:!0,timestamps:!1}),De=y.define("Conversation",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,title:a.STRING,workspaceId:a.STRING,workspaceName:a.STRING,tier:a.STRING,effort:a.STRING,sessionId:a.STRING,workingDir:a.STRING,messages:a.JSON,starred:a.BOOLEAN,createdAt:a.STRING,updatedAt:a.STRING},{tableName:"conversations",underscored:!0,timestamps:!1}),ct=y.define("Setting",{userId:{type:a.STRING,primaryKey:!0},defaultAutonomy:a.STRING,maxConcurrentRuns:a.INTEGER,branchPrefix:a.STRING,connectedWorkspaces:a.JSON,workspaceDirs:a.JSON,defaultModel:a.STRING,defaultEffort:a.STRING,prMonitorDefault:a.BOOLEAN,prMonitorIntervalMinutes:a.INTEGER,prMonitorMaxEngagements:a.INTEGER,prMonitorReplyToComments:a.BOOLEAN,sandboxWorktreeEdits:a.BOOLEAN,publicApiEnabled:a.BOOLEAN,apiKeyHash:a.STRING,apiKeyLast4:a.STRING,apiKeyCreatedAt:a.STRING,webhookEnabled:a.BOOLEAN,webhookUrl:a.STRING,webhookHeaders:a.JSON,webhookEvents:a.JSON,webhookSecret:a.TEXT},{tableName:"settings",underscored:!0,timestamps:!1}),ut=y.define("OAuthToken",{connectionId:{type:a.STRING,primaryKey:!0},userId:a.STRING,provider:a.STRING,accessToken:a.TEXT,refreshToken:a.TEXT,scope:a.STRING,expiresAt:a.STRING},{tableName:"oauth_tokens",underscored:!0,timestamps:!1}),je=y.define("RunResult",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,ticketId:a.STRING,runId:a.STRING,sessionId:a.STRING,prompt:a.TEXT,response:a.TEXT,status:a.STRING,model:a.STRING,effort:a.STRING,createdAt:a.STRING},{tableName:"run_results",underscored:!0,timestamps:!1}),ze=y.define("Note",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,ticketId:a.STRING,scheduleId:a.STRING,author:a.STRING,body:a.TEXT,createdAt:a.STRING,updatedAt:a.STRING},{tableName:"notes",underscored:!0,timestamps:!1}),Me=y.define("MessagePreset",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,title:a.STRING,prompt:a.TEXT,enabled:a.BOOLEAN,position:a.INTEGER,createdAt:a.STRING,updatedAt:a.STRING},{tableName:"message_presets",underscored:!0,timestamps:!1}),$e=y.define("OpenQuestion",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,ticketId:a.STRING,question:a.TEXT,answer:a.TEXT,resolved:a.BOOLEAN,position:a.INTEGER,createdAt:a.STRING,updatedAt:a.STRING},{tableName:"open_questions",underscored:!0,timestamps:!1}),ds={Connection:Qe,Ticket:Ie,Run:Pe,Schedule:Je,ScheduleRun:Oe,Batch:Kt,Conversation:De,Setting:ct,OAuthToken:ut,RunResult:je,Note:ze,MessagePreset:Me,OpenQuestion:$e},la=new ua({context:y.getQueryInterface(),storage:{async logMigration({name:e}){await y.query("INSERT INTO pipelines_migrations (name) VALUES (:name)",{replacements:{name:e}})},async unlogMigration({name:e}){await y.query("DELETE FROM pipelines_migrations WHERE name = :name",{replacements:{name:e}})},async executed(){await y.query("CREATE TABLE IF NOT EXISTS pipelines_migrations (name TEXT PRIMARY KEY)");let[e]=await y.query("SELECT name FROM pipelines_migrations ORDER BY name");return e.map(t=>t.name)}},logger:void 0,migrations:[{name:"0001-init",async up(){await y.sync()}},{name:"0002-oauth-tokens",async up(){await ut.sync()}},{name:"0003-settings-branch-prefix",async up(){let e=y.getQueryInterface();(await e.describeTable("settings")).branch_prefix||await e.addColumn("settings","branch_prefix",{type:a.STRING,allowNull:!0})}},{name:"0004-settings-connected-workspaces",async up(){let e=y.getQueryInterface();(await e.describeTable("settings")).connected_workspaces||await e.addColumn("settings","connected_workspaces",{type:a.JSON,allowNull:!0})}},{name:"0005-runs-result-text",async up(){let e=y.getQueryInterface();(await e.describeTable("runs")).result_text||await e.addColumn("runs","result_text",{type:a.TEXT,allowNull:!0})}},{name:"0006-run-results",async up(){let e=y.getQueryInterface();(await e.showAllTables()).map(String).includes("run_results")||await e.createTable("run_results",{id:{type:a.STRING,primaryKey:!0},user_id:a.STRING,ticket_id:a.STRING,run_id:a.STRING,session_id:a.STRING,prompt:a.TEXT,response:a.TEXT,status:a.STRING,created_at:a.STRING})}},{name:"0007-comments",async up(){let e=y.getQueryInterface();(await e.showAllTables()).map(String).includes("comments")||await e.createTable("comments",{id:{type:a.STRING,primaryKey:!0},user_id:a.STRING,ticket_id:a.STRING,author:a.STRING,body:a.TEXT,created_at:a.STRING})}},{name:"0008-comments-updated-at",async up(){let e=y.getQueryInterface();(await e.describeTable("comments")).updated_at||await e.addColumn("comments","updated_at",{type:a.STRING,allowNull:!0})}},{name:"0009-conversations-working-dir",async up(){let e=y.getQueryInterface();(await e.describeTable("conversations")).working_dir||await e.addColumn("conversations","working_dir",{type:a.STRING,allowNull:!0})}},{name:"0010-settings-workspace-dirs",async up(){let e=y.getQueryInterface();(await e.describeTable("settings")).workspace_dirs||await e.addColumn("settings","workspace_dirs",{type:a.JSON,allowNull:!0})}},{name:"0011-tickets-working-dir",async up(){let e=y.getQueryInterface();(await e.describeTable("tickets")).working_dir||await e.addColumn("tickets","working_dir",{type:a.STRING,allowNull:!0})}},{name:"0012-schedules-working-dir",async up(){let e=y.getQueryInterface();(await e.describeTable("schedules")).working_dir||await e.addColumn("schedules","working_dir",{type:a.STRING,allowNull:!0})}},{name:"0013-rename-comments-to-notes",async up(){let e=y.getQueryInterface(),t=(await e.showAllTables()).map(String);t.includes("comments")&&!t.includes("notes")&&await e.renameTable("comments","notes")}},{name:"0014-notes-schedule-id",async up(){let e=y.getQueryInterface();(await e.describeTable("notes")).schedule_id||await e.addColumn("notes","schedule_id",{type:a.STRING,allowNull:!0})}},{name:"0015-connections-sync-filters",async up(){let e=y.getQueryInterface();(await e.describeTable("connections")).sync_filters||await e.addColumn("connections","sync_filters",{type:a.JSON,allowNull:!0})}},{name:"0016-tickets-parent",async up(){let e=y.getQueryInterface();(await e.describeTable("tickets")).parent||await e.addColumn("tickets","parent",{type:a.JSON,allowNull:!0})}},{name:"0017-conversations-effort",async up(){let e=y.getQueryInterface();(await e.describeTable("conversations")).effort||await e.addColumn("conversations","effort",{type:a.STRING,allowNull:!0})}},{name:"0018-model-effort",async up(){let e=y.getQueryInterface(),t={type:a.STRING,allowNull:!0},n=async(r,s)=>{(await e.describeTable(r))[s]||await e.addColumn(r,s,t)};await n("settings","default_model"),await n("settings","default_effort");for(let r of["tickets","schedules","run_results","schedule_runs"])await n(r,"model"),await n(r,"effort")}},{name:"0019-tickets-blocked-reason",async up(){let e=y.getQueryInterface();(await e.describeTable("tickets")).blocked_reason||await e.addColumn("tickets","blocked_reason",{type:a.TEXT,allowNull:!0})}},{name:"0020-message-presets",async up(){let e=y.getQueryInterface();(await e.showAllTables()).map(String).includes("message_presets")||await e.createTable("message_presets",{id:{type:a.STRING,primaryKey:!0},user_id:a.STRING,title:a.STRING,prompt:a.TEXT,enabled:a.BOOLEAN,position:a.INTEGER,created_at:a.STRING,updated_at:a.STRING})}},{name:"0021-ticket-context-fields",async up(){let e=y.getQueryInterface(),t=await e.describeTable("tickets"),n={type:a.TEXT,allowNull:!0};t.additional_context||await e.addColumn("tickets","additional_context",n),!t.tech_plan&&!t.plan&&await e.addColumn("tickets","tech_plan",n),t.conclusion||await e.addColumn("tickets","conclusion",n)}},{name:"0022-open-questions",async up(){let e=y.getQueryInterface();(await e.showAllTables()).map(String).includes("open_questions")||await e.createTable("open_questions",{id:{type:a.STRING,primaryKey:!0},user_id:a.STRING,ticket_id:a.STRING,question:a.TEXT,answer:a.TEXT,resolved:a.BOOLEAN,position:a.INTEGER,created_at:a.STRING,updated_at:a.STRING})}},{name:"0023-rename-tech-plan-to-plan",async up(){let e=y.getQueryInterface(),t=await e.describeTable("tickets");t.tech_plan&&!t.plan&&await e.renameColumn("tickets","tech_plan","plan")}},{name:"0024-ticket-context-timestamps",async up(){let e=y.getQueryInterface(),t=await e.describeTable("tickets"),n={type:a.STRING,allowNull:!0};t.additional_context_updated_at||await e.addColumn("tickets","additional_context_updated_at",n),t.plan_updated_at||await e.addColumn("tickets","plan_updated_at",n),t.conclusion_updated_at||await e.addColumn("tickets","conclusion_updated_at",n)}},{name:"0025-ticket-created-updated",async up(){let e=y.getQueryInterface(),t=await e.describeTable("tickets"),n={type:a.STRING,allowNull:!0};t.created_at||await e.addColumn("tickets","created_at",n),t.updated_at||await e.addColumn("tickets","updated_at",n)}},{name:"0026-ticket-pr-links",async up(){let e=y.getQueryInterface();(await e.describeTable("tickets")).pr_links||await e.addColumn("tickets","pr_links",{type:a.JSON,allowNull:!0})}},{name:"0027-run-worktrees",async up(){let e=y.getQueryInterface();(await e.describeTable("runs")).worktrees||await e.addColumn("runs","worktrees",{type:a.JSON,allowNull:!0})}},{name:"0028-ticket-attempt",async up(){let e=y.getQueryInterface();(await e.describeTable("tickets")).attempt||await e.addColumn("tickets","attempt",{type:a.INTEGER,allowNull:!0})}},{name:"0029-pr-monitor",async up(){let e=y.getQueryInterface(),t={type:a.INTEGER,allowNull:!0},n={type:a.BOOLEAN,allowNull:!0},r={type:a.STRING,allowNull:!0},s={type:a.TEXT,allowNull:!0},o=await e.describeTable("settings");o.pr_monitor_default||await e.addColumn("settings","pr_monitor_default",n),o.pr_monitor_interval_minutes||await e.addColumn("settings","pr_monitor_interval_minutes",t),o.pr_monitor_max_engagements||await e.addColumn("settings","pr_monitor_max_engagements",t),o.pr_monitor_reply_to_comments||await e.addColumn("settings","pr_monitor_reply_to_comments",n);let i=await e.describeTable("tickets");i.pr_monitor||await e.addColumn("tickets","pr_monitor",n),i.pr_monitor_count||await e.addColumn("tickets","pr_monitor_count",t),i.pr_monitor_last_at||await e.addColumn("tickets","pr_monitor_last_at",r),i.pr_monitor_note||await e.addColumn("tickets","pr_monitor_note",s)}},{name:"0030-ticket-depends-on",async up(){let e=y.getQueryInterface();(await e.describeTable("tickets")).depends_on||await e.addColumn("tickets","depends_on",{type:a.JSON,allowNull:!0})}},{name:"0031-settings-sandbox",async up(){let e=y.getQueryInterface();(await e.describeTable("settings")).sandbox_worktree_edits||await e.addColumn("settings","sandbox_worktree_edits",{type:a.BOOLEAN,allowNull:!0})}},{name:"0032-connection-cloud-id",async up(){let e=y.getQueryInterface();(await e.describeTable("connections")).cloud_id||await e.addColumn("connections","cloud_id",{type:a.STRING,allowNull:!0})}},{name:"0033-ticket-reporter-due-date",async up(){let e=y.getQueryInterface(),t=await e.describeTable("tickets");t.reporter||await e.addColumn("tickets","reporter",{type:a.JSON,allowNull:!0}),t.due_date||await e.addColumn("tickets","due_date",{type:a.STRING,allowNull:!0})}},{name:"0034-connection-provider-account-id",async up(){let e=y.getQueryInterface();(await e.describeTable("connections")).provider_account_id||await e.addColumn("connections","provider_account_id",{type:a.STRING,allowNull:!0})}},{name:"0035-settings-public-api",async up(){let e=y.getQueryInterface(),t=await e.describeTable("settings"),n=async(r,s)=>{t[r]||await e.addColumn("settings",r,{type:s,allowNull:!0})};await n("public_api_enabled",a.BOOLEAN),await n("api_key_hash",a.STRING),await n("api_key_last4",a.STRING),await n("api_key_created_at",a.STRING),await n("webhook_enabled",a.BOOLEAN),await n("webhook_url",a.STRING),await n("webhook_headers",a.JSON),await n("webhook_events",a.JSON),await n("webhook_secret",a.TEXT)}},{name:"0036-ticket-comments",async up(){let e=y.getQueryInterface();(await e.describeTable("tickets")).comments||await e.addColumn("tickets","comments",{type:a.JSON,allowNull:!0})}},{name:"0037-batches",async up(){let e=y.getQueryInterface();(await e.showAllTables()).map(String).includes("batches")||await e.createTable("batches",{id:{type:a.STRING,primaryKey:!0},user_id:a.STRING,name:a.STRING,workspace_id:a.STRING,workspace_name:a.STRING,ticket_ids:a.JSON,mode:a.STRING,advance_mode:a.STRING,stack_dependent_work:a.BOOLEAN,status:a.STRING,budget_cap:a.INTEGER,paused_reason:a.TEXT,runs_started:a.INTEGER,created_at:a.STRING,updated_at:a.STRING})}}]})});V();dt();import zn from"express";import Bd from"cors";import{randomUUID as ot}from"node:crypto";import te from"node:path";import Ne from"node:fs";import Jn from"node:os";import{fileURLToPath as Ld}from"node:url";V();import Be from"node:crypto";import tr from"node:fs";import pa from"node:os";import sr from"node:path";var rr="enc:v1:",fa=process.env.SUPERCLAWD_HOME||sr.join(pa.homedir(),".superclawd"),nr=process.env.PIPELINES_KEY_FILE||sr.join(fa,"pipelines.key"),Ht=null;function ls(){if(Ht)return Ht;tr.mkdirSync(sr.dirname(nr),{recursive:!0});try{let t=tr.readFileSync(nr);if(t.length===32)return Ht=t,Ht}catch{}let e=Be.randomBytes(32);return tr.writeFileSync(nr,e,{mode:384}),Ht=e,e}function lt(e){let t=Be.randomBytes(12),n=Be.createCipheriv("aes-256-gcm",ls(),t),r=Buffer.concat([n.update(e,"utf8"),n.final()]),s=n.getAuthTag();return`${rr}${t.toString("base64")}:${s.toString("base64")}:${r.toString("base64")}`}function pt(e){if(!e.startsWith(rr))return e;let[t,n,r]=e.slice(rr.length).split(":"),s=Be.createDecipheriv("aes-256-gcm",ls(),Buffer.from(t,"base64"));return s.setAuthTag(Buffer.from(n,"base64")),Buffer.concat([s.update(Buffer.from(r,"base64")),s.final()]).toString("utf8")}function ft(e){return Be.createHash("sha256").update(e,"utf8").digest("hex")}function mt(e="",t=32){return e+Be.randomBytes(t).toString("base64url")}function Qt(e,t){let n=Buffer.from(e),r=Buffer.from(t);return n.length!==r.length?!1:Be.timingSafeEqual(n,r)}function ps(e,t){return Be.createHmac("sha256",e).update(t,"utf8").digest("hex")}dt();function ma(e){let t={};for(let[n,r]of Object.entries(e))r!=null&&(t[n]=r);return t}var Z=e=>e.get({plain:!0}),ie=(e,t)=>{let n={...e};for(let r of t)delete n[r];return ma(n)};function fs(e){let t={};for(let[n,r]of Object.entries(e))t[n]=r===void 0?null:r;return t}function gt(e,t){let n=0;for(let r of e){let s=new RegExp(`^${t}_?(\\d+)$`).exec(r);s&&(n=Math.max(n,Number(s[1])))}return n}async function ms(){let e={where:{userId:E()}},t=await Qe.findAll(e);b.length=0,b.push(...t.map(f=>ie(Z(f),["userId"])));let n=await Ie.findAll({...e,order:[["position","ASC"]]});h.length=0;for(let f of Object.keys(I))delete I[f];for(let f of n){let w=Z(f);I[w.id]=w.column??"backlog",h.push(ie(w,["userId","column","position"]))}let r=await Pe.findAll(e);for(let f of Object.keys(x))delete x[f];for(let f of Object.keys(T))delete T[f];for(let f of r){let w=ie(Z(f),["userId","pid","pgid"]);x[w.id]=w,T[w.ticketId]=w.id}let s=await Je.findAll({...e,order:[["createdAt","ASC"]]});for(let f of Object.keys(O))delete O[f];ne.length=0;for(let f of s){let w=ie(Z(f),["userId"]);O[w.id]=w,ne.push(w.id)}let o=await Oe.findAll({...e,order:[["seq","DESC"]]});for(let f of Object.keys(z))delete z[f];for(let f of ne)z[f]=[];for(let f of o){let w=Z(f),Y=ie(w,["userId","seq"]);(z[w.scheduleId]??=[]).push(Y)}let i=await Kt.findAll({...e,order:[["createdAt","ASC"]]});for(let f of Object.keys(L))delete L[f];K.length=0;for(let f of i){let w=ie(Z(f),["userId"]);L[w.id]=w,K.push(w.id)}let c=await De.findAll({...e,order:[["updatedAt","DESC"]]});for(let f of Object.keys(W))delete W[f];ce.length=0;for(let f of c){let w=ie(Z(f),["userId"]);w.messages||(w.messages=[]),W[w.id]=w,ce.push(w.id)}let d=await je.findAll({...e,order:[["createdAt","DESC"]]});X.length=0;for(let f of d)X.push(ie(Z(f),["userId"]));let u=await ze.findAll({...e,order:[["createdAt","ASC"]]});j.length=0;for(let f of u)j.push(ie(Z(f),["userId"]));let p=await Me.findAll({...e,order:[["position","ASC"]]});H.length=0;for(let f of p)H.push(ie(Z(f),["userId"]));let g=await $e.findAll({...e,order:[["position","ASC"]]});D.length=0;for(let f of g)D.push(ie(Z(f),["userId"]));let S=await ct.findByPk(E());if(S){let f=Z(S);l.defaultAutonomy=f.defaultAutonomy??l.defaultAutonomy,typeof f.maxConcurrentRuns=="number"&&(l.maxConcurrentRuns=f.maxConcurrentRuns),typeof f.branchPrefix=="string"&&(l.branchPrefix=f.branchPrefix),Array.isArray(f.connectedWorkspaces)&&(l.connectedWorkspaces=f.connectedWorkspaces),f.workspaceDirs&&typeof f.workspaceDirs=="object"&&(l.workspaceDirs=f.workspaceDirs),typeof f.defaultModel=="string"&&(l.defaultModel=f.defaultModel),typeof f.defaultEffort=="string"&&(l.defaultEffort=f.defaultEffort),typeof f.prMonitorDefault=="boolean"&&(l.prMonitorDefault=f.prMonitorDefault),typeof f.prMonitorIntervalMinutes=="number"&&(l.prMonitorIntervalMinutes=f.prMonitorIntervalMinutes),typeof f.prMonitorMaxEngagements=="number"&&(l.prMonitorMaxEngagements=f.prMonitorMaxEngagements),typeof f.prMonitorReplyToComments=="boolean"&&(l.prMonitorReplyToComments=f.prMonitorReplyToComments),typeof f.sandboxWorktreeEdits=="boolean"&&(l.sandboxWorktreeEdits=f.sandboxWorktreeEdits),typeof f.publicApiEnabled=="boolean"&&(l.publicApiEnabled=f.publicApiEnabled),typeof f.apiKeyHash=="string"&&(l.apiKeyHash=f.apiKeyHash),typeof f.apiKeyLast4=="string"&&(l.apiKeyLast4=f.apiKeyLast4),typeof f.apiKeyCreatedAt=="string"&&(l.apiKeyCreatedAt=f.apiKeyCreatedAt),typeof f.webhookEnabled=="boolean"&&(l.webhookEnabled=f.webhookEnabled),typeof f.webhookUrl=="string"&&(l.webhookUrl=f.webhookUrl),Array.isArray(f.webhookHeaders)&&(l.webhookHeaders=f.webhookHeaders.map(w=>({key:w.key,value:pt(String(w.value??""))}))),Array.isArray(f.webhookEvents)&&(l.webhookEvents=f.webhookEvents),typeof f.webhookSecret=="string"&&(l.webhookSecret=pt(f.webhookSecret))}G.convSeq=gt(Object.keys(W),"conv"),G.schedSeq=gt(Object.keys(O),"sched"),G.batchSeq=gt(Object.keys(L),"batch"),G.localSeq=Math.max(G.localSeq,gt(h.map(f=>f.id),"t_local")),G.schedRunSeq=gt(o.map(f=>Z(f).id),"srun"),G.runSeq=gt(Object.keys(x),"run")}async function A(e){await Qe.upsert({...e,userId:E()})}async function gs(e){await Qe.destroy({where:{id:e}})}async function pn(){for(let e of B){let t=`conn_local_${e.id}`;if(b.some(r=>r.provider==="local"&&r.workspaceId===e.id))continue;let n={id:t,provider:"local",providerAccountName:"Local",workspaceId:e.id,workspaceName:e.name,workingDir:null,linkStatus:"linked",authStatus:"connected",lastSyncedAt:null,ticketCount:0};b.push(n),await A(n)}}async function N(e){let t=h.find(n=>n.id===e);t&&await Ie.upsert(fs({...t,userId:E(),column:I[e]??"backlog",position:h.indexOf(t)}))}async function hs(){await Promise.all(h.map((e,t)=>Ie.update({position:t,column:I[e.id]??"backlog"},{where:{id:e.id}})))}async function fn(e){await Ie.destroy({where:{id:e}}),await Pe.destroy({where:{ticketId:e}})}async function U(e){let t=x[e];t&&await Pe.upsert({...t,userId:E()})}async function ks(e){await Pe.destroy({where:{id:e}})}async function Jt(e){let t=O[e];t&&await Je.upsert({...t,userId:E()})}async function ys(e){await Je.destroy({where:{id:e}}),await Oe.destroy({where:{scheduleId:e}})}async function ue(e){let t=L[e];t&&await Kt.upsert({...t,userId:E()})}async function zt(e){await Kt.destroy({where:{id:e}})}async function mn(e,t){await Oe.upsert({...e,userId:E(),seq:t})}async function Xe(e){let t=W[e];t&&await De.upsert({...t,userId:E()})}async function ws(e){await De.destroy({where:{id:e}})}async function gn(e){await je.upsert({...e,userId:E()})}async function bs(e){await je.destroy({where:{ticketId:e,userId:E()}})}async function Ss(e,t,n){let{count:r,rows:s}=await je.findAndCountAll({where:{ticketId:e,userId:E()},order:[["createdAt","DESC"]],limit:t,offset:n});return{entries:s.map(o=>ie(Z(o),["userId"])),total:r}}async function Is(e,t,n){let{count:r,rows:s}=await Oe.findAndCountAll({where:{scheduleId:e,userId:E()},order:[["seq","DESC"]],limit:t,offset:n});return{runs:s.map(o=>ie(Z(o),["userId","seq"])),total:r}}async function ht(e){await ze.upsert({...e,userId:E()})}async function hn(e){await ze.destroy({where:{id:e}})}async function kn(e){let t=H.find(n=>n.id===e);t&&await Me.upsert({...t,userId:E()})}async function xs(e){await Me.destroy({where:{id:e}})}async function vs(){await Promise.all(H.map((e,t)=>Me.update({position:t},{where:{id:e.id}})))}async function or(e){let t=D.find(n=>n.id===e);t&&await $e.upsert(fs({...t,userId:E()}))}async function yn(e){await $e.destroy({where:{id:e}})}async function Rs(){await Promise.all(D.map((e,t)=>$e.update({position:t},{where:{id:e.id}})))}async function se(){await ct.upsert({userId:E(),defaultAutonomy:l.defaultAutonomy,maxConcurrentRuns:l.maxConcurrentRuns??3,branchPrefix:l.branchPrefix??"feature",connectedWorkspaces:l.connectedWorkspaces??null,workspaceDirs:l.workspaceDirs??null,defaultModel:l.defaultModel??null,defaultEffort:l.defaultEffort??null,prMonitorDefault:l.prMonitorDefault??null,prMonitorIntervalMinutes:l.prMonitorIntervalMinutes??null,prMonitorMaxEngagements:l.prMonitorMaxEngagements??null,prMonitorReplyToComments:l.prMonitorReplyToComments??null,sandboxWorktreeEdits:l.sandboxWorktreeEdits??null,publicApiEnabled:l.publicApiEnabled??null,apiKeyHash:l.apiKeyHash??null,apiKeyLast4:l.apiKeyLast4??null,apiKeyCreatedAt:l.apiKeyCreatedAt??null,webhookEnabled:l.webhookEnabled??null,webhookUrl:l.webhookUrl??null,webhookHeaders:l.webhookHeaders?l.webhookHeaders.map(e=>({key:e.key,value:lt(e.value)})):null,webhookEvents:l.webhookEvents??null,webhookSecret:l.webhookSecret?lt(l.webhookSecret):null})}async function Ts(){let e={where:{userId:E()}};await Promise.all(Object.values(ds).map(t=>t.destroy(e)))}async function _s(){await Ts();let{seedFromFixtures:e}=await Promise.resolve().then(()=>(er(),Zn));await e()}async function As(){let e=new Set(B.map(o=>o.id)),t=[],n=new Set;for(let o of b)o.provider!=="local"||!o.workspaceId||!e.has(o.workspaceId)||n.has(o.workspaceId)||(n.add(o.workspaceId),t.push(o));let r=o=>{o.length=0},s=o=>{for(let i of Object.keys(o))delete o[i]};r(b),b.push(...t),r(h),r(re),r(X),r(j),r(D),r(ne),r(K),r(ce),s(x),s(T),s(I),s(O),s(z),s(L),s(W),await Ts();for(let o of t)await A(o);await pn(),await se();for(let o of H)await kn(o.id)}import wn from"node:fs";import ga from"node:os";import ir from"node:path";function ha(){let e=process.env.CLAUDE_CONFIG_DIR||ir.join(ga.homedir(),".claude");return ir.join(e,"projects")}function Ns(e){let t=ha(),n;try{n=wn.readdirSync(t)}catch{return null}let r=`${e}.jsonl`;for(let s of n){let o=ir.join(t,s,r);if(wn.existsSync(o))return o}return null}function ka(e){return Array.isArray(e)?e.filter(t=>!!t&&typeof t=="object"&&t.type==="text"&&typeof t.text=="string").map(t=>t.text).join("").trim():typeof e=="string"?e.trim():""}function bn(e){return ar(e).response}function ar(e){let t=Ns(e);if(!t)return{prompt:null,response:null};let n;try{n=wn.readFileSync(t,"utf8")}catch{return{prompt:null,response:null}}let r=null,s=null;for(let o of n.split(`
|
|
9
|
-
`)){let i=o.trim();if(!i)continue;let c;try{c=JSON.parse(i)}catch{continue}let d=c,u=
|
|
10
|
-
\u2026 (truncated)`:n}function
|
|
11
|
-
`)){let o=s.trim();if(!o)continue;let i;try{i=JSON.parse(o)}catch{continue}let c=i,d=c.type,u=c.message?.content;if(typeof u=="string"){let p=u.trim();if(!p)continue;d==="user"?r.push({type:"prompt",text:p}):d==="assistant"&&r.push({type:"text",text:p});continue}if(Array.isArray(u))for(let p of u){if(!p||typeof p!="object")continue;let g=p;if(g.type==="text"&&typeof g.text=="string"&&g.text.trim()){let
|
|
12
|
-
`)}function Hs(e){let{cmd:t,args:n,repoPaths:r,enabled:s}=e;return!s||!r.length||!
|
|
13
|
-
`)}catch{}}let c=$a(i.cmd,i.args,{cwd:o,detached:!0,stdio:["ignore","pipe","pipe"],env:{...process.env,...s}}),d="",u="";c.stdout?.on("data",
|
|
14
|
-
${String(f)}`})),c.on("close",f=>
|
|
8
|
+
Here\u2019s a first pass grouped by Added / Changed / Fixed. Want me to tighten the copy?`,ts:"08:05"}],starred:!1,createdAt:new Date(Date.now()-2*24*36e5).toISOString(),updatedAt:new Date(Date.now()-2*24*36e5).toISOString()}},ce=["conv_3","conv_2","conv_1"],O={sched_1:{id:"sched_1",name:"Triage new bugs",prompt:"Triage new Linear bugs: reproduce, label severity, and open a follow-up for anything urgent.",workspaceId:"ws_front",workspaceName:"Frontend",assignmentKind:"team",teamId:"team_core",teamName:"Core",cadence:"Every 2 hours",enabled:!0,starred:!0,createdAt:new Date(Date.now()-8*24*36e5).toISOString(),lastRunAt:new Date(Date.now()-40*6e4).toISOString(),nextRunAt:new Date(Date.now()+80*6e4).toISOString()},sched_2:{id:"sched_2",name:"Nightly flaky-test sweep",prompt:"Run the test suite, detect flakes, and file an issue per flaky test with the failing output.",workspaceId:"ws_front",workspaceName:"Frontend",assignmentKind:"anyone",cadence:"Every 1 day",enabled:!1,starred:!1,createdAt:new Date(Date.now()-20*24*36e5).toISOString(),lastRunAt:new Date(Date.now()-14*36e5).toISOString(),nextRunAt:null},sched_3:{id:"sched_3",name:"Reconcile credit ledger",prompt:"Reconcile the credit ledger against Stripe: flag drift over $1 and open a dispute task for anything unexplained.",workspaceId:"ws_core",workspaceName:"Core Platform",assignmentKind:"agent",agentId:"agent_orch",agentName:"Orchestrator",cadence:"Every 6 hours",enabled:!0,starred:!0,createdAt:new Date(Date.now()-5*24*36e5).toISOString(),lastRunAt:new Date(Date.now()-2*36e5).toISOString(),nextRunAt:new Date(Date.now()+4*36e5).toISOString()}},se=["sched_1","sched_2","sched_3"],z={sched_1:Array.from({length:14},(e,t)=>({id:`srun_1_${14-t}`,scheduleId:"sched_1",status:t===0?"success":t%6===0?"failed":"success",sessionId:`a1b2c3${String(14-t).padStart(2,"0")}-6f27-4c95-90ab-1e7d32c8f0a5`,startedAt:new Date(Date.now()-(40+t*120)*6e4).toISOString(),finishedAt:new Date(Date.now()-(40+t*120)*6e4+95e3).toISOString(),output:t%6===0?"Failed: could not reach the Linear API (429). Will retry next tick.":`Triaged ${2+t%4} new issues \xB7 labeled severity \xB7 opened ${t%3===0?1:0} follow-up. (mocked output)`})),sched_2:Array.from({length:3},(e,t)=>({id:`srun_2_${3-t}`,scheduleId:"sched_2",status:"success",sessionId:`d4e5f6${String(3-t).padStart(2,"0")}-9c02-4b38-a7e1-5f0c2b83d419`,startedAt:new Date(Date.now()-(14+t*24)*36e5).toISOString(),finishedAt:new Date(Date.now()-(14+t*24)*36e5+22e4).toISOString(),output:`Ran the suite \xB7 ${t===0?"no":"1"} flaky test found${t===0?"":" \xB7 filed 1 issue"}. (mocked output)`})),sched_3:Array.from({length:7},(e,t)=>({id:`srun_3_${7-t}`,scheduleId:"sched_3",status:"success",sessionId:`c7d8e9${String(7-t).padStart(2,"0")}-4b38-4c95-90ab-1e7d32c8f0a5`,startedAt:new Date(Date.now()-(2+t*6)*36e5).toISOString(),finishedAt:new Date(Date.now()-(2+t*6)*36e5+18e4).toISOString(),output:`Reconciled ${180+t*12} charges \xB7 ${t%3===0?"no drift":"flagged 1 mismatch \xB7 opened dispute task"}. (mocked output)`}))},L={},K=[],G={runSeq:500,convSeq:3,schedSeq:3,schedRunSeq:100,batchSeq:0,localSeq:2},X=[],j=[],D=[],H=[],Y=[{id:"jira_9002",connectionId:"conn_jira_demo",provider:"jira",externalIdentifier:"PLAT-2",project:"Platform",title:"Add rate-limit headers to the public API",status:"todo",statusRaw:"To Do",assignee:{name:"Priya"},priority:3,labels:[{name:"api"}],url:"https://demo.atlassian.net/browse/PLAT-2",workspaceId:"ws_core",description:"Surface X-RateLimit-* so clients can back off."},{id:"s_150",connectionId:"conn_acme",provider:"linear",externalIdentifier:"ENG-150",project:"Frontend App",title:"Debounce the workspace search input",status:"todo",statusRaw:"Todo",assignee:{name:"Priya"},priority:3,labels:[{name:"ui"}],url:"https://linear.app/acme/issue/ENG-150",workspaceId:"ws_front",description:"The search fires a request per keystroke; debounce ~300ms."},{id:"s_151",connectionId:"conn_acme",provider:"linear",externalIdentifier:"ENG-151",project:"CI",title:"Flaky test: auth.session.spec times out on CI",status:"todo",statusRaw:"Todo",assignee:{name:"Sam"},priority:1,labels:[{name:"bug"},{name:"ci"}],url:"https://linear.app/acme/issue/ENG-151",workspaceId:"ws_front",description:"Intermittent 30s timeout; suspect a missing await."},{id:"s_152",connectionId:"conn_acme",provider:"linear",externalIdentifier:"ENG-152",project:"Public API",title:"Add rate-limit headers to the public API",status:"backlog",statusRaw:"Backlog",priority:2,labels:[{name:"api"}],url:"https://linear.app/acme/issue/ENG-152",workspaceId:"ws_front",description:"Expose X-RateLimit-* on responses."},{id:"s_160",connectionId:"conn_acme",provider:"linear",externalIdentifier:"ENG-160",project:"Auth",title:"Login page: remember-me checkbox does nothing",status:"todo",statusRaw:"Todo",assignee:{name:"Andy"},priority:2,labels:[{name:"bug"}],url:"https://linear.app/acme/issue/ENG-160",workspaceId:"ws_front",description:"Session still expires on browser close."},{id:"s_210",connectionId:"conn_personal",provider:"linear",externalIdentifier:"CORE-210",project:"Directives",title:"Batch the memory-read endpoint",status:"backlog",statusRaw:"Backlog",priority:2,labels:[{name:"perf"}],url:"https://linear.app/personal/issue/CORE-210",workspaceId:"ws_core",description:"Coalesce N per-memory fetches into one batch call."},{id:"s_211",connectionId:"conn_personal",provider:"linear",externalIdentifier:"CORE-211",project:"Docs",title:"Document the directives cache TTL",status:"todo",statusRaw:"Todo",priority:4,labels:[{name:"docs"}],url:"https://linear.app/personal/issue/CORE-211",workspaceId:"ws_core"}]});var er={};ia(er,{seedFromFixtures:()=>aa});async function aa(){let e=C();await Qe.bulkCreate(b.map(n=>({...n,userId:e}))),await ve.bulkCreate(h.map((n,r)=>({...n,userId:e,column:S[n.id]??"backlog",position:r}))),await Pe.bulkCreate(Object.values(x).map(n=>({...n,userId:e}))),await Je.bulkCreate(se.map(n=>({...O[n],userId:e})));let t=Object.keys(z).flatMap(n=>{let r=z[n];return r.map((s,o)=>({...s,userId:e,seq:r.length-o}))});await Oe.bulkCreate(t),await De.bulkCreate(ce.map(n=>({...W[n],userId:e}))),await je.bulkCreate(X.map(n=>({...n,userId:e}))),await ze.bulkCreate(j.map(n=>({...n,userId:e}))),await Be.bulkCreate(D.map((n,r)=>({...n,userId:e,position:n.position??r}))),await Me.bulkCreate(H.map((n,r)=>({...n,userId:e,position:n.position??r}))),await ct.create({userId:e,defaultAutonomy:l.defaultAutonomy,maxConcurrentRuns:l.maxConcurrentRuns??3,branchPrefix:l.branchPrefix??"feature",connectedWorkspaces:l.connectedWorkspaces??null,workspaceDirs:l.workspaceDirs??null,defaultModel:l.defaultModel??null,defaultEffort:l.defaultEffort??null,autoAssignImports:l.autoAssignImports??null,importDelegate:l.importDelegate??null})}var tr=Zn(()=>{"use strict";Z();dt()});import ca from"node:os";import as from"node:path";import da from"node:fs";import{Sequelize as ua,DataTypes as a}from"sequelize";import{Umzug as la}from"umzug";function C(){return cs}function pn(e){cs=e}async function is(e){try{return await e.count()}catch{return 0}}async function us(){if(da.mkdirSync(as.dirname(ln),{recursive:!0}),await k.authenticate(),await pa.up(),await is(ve)===0&&await is(De)===0&&C()===Kt){let{seedFromFixtures:t}=await Promise.resolve().then(()=>(tr(),er));await t()}}var ln,Kt,cs,k,Qe,ve,Pe,Je,Oe,Ht,De,ct,ut,je,ze,Me,Be,ds,pa,dt=Zn(()=>{"use strict";ln=process.env.PIPELINES_DB_PATH??as.join(ca.homedir(),".superclawd","pipelines.db"),Kt=process.env.PIPELINES_DEV_USER_ID??"dev-user",cs=Kt;k=new ua({dialect:"sqlite",storage:ln,logging:!1}),Qe=k.define("Connection",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,provider:a.STRING,providerAccountName:a.STRING,displayName:a.STRING,workspaceId:a.STRING,workspaceName:a.STRING,workingDir:a.STRING,linkStatus:a.STRING,linkLostReason:a.STRING,authStatus:a.STRING,lastSyncedAt:a.STRING,ticketCount:a.INTEGER,syncFilters:a.JSON,autoIngestTickets:a.BOOLEAN,cloudId:a.STRING,providerAccountId:a.STRING},{tableName:"connections",underscored:!0,timestamps:!1}),ve=k.define("Ticket",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,connectionId:a.STRING,provider:a.STRING,externalIdentifier:a.STRING,title:a.STRING,description:a.TEXT,status:a.STRING,statusRaw:a.STRING,assignee:a.JSON,reporter:a.JSON,dueDate:a.STRING,priority:a.INTEGER,labels:a.JSON,project:a.STRING,parent:a.JSON,model:a.STRING,effort:a.STRING,blockedReason:a.TEXT,comments:a.JSON,additionalContext:a.TEXT,plan:a.TEXT,conclusion:a.TEXT,additionalContextUpdatedAt:a.STRING,planUpdatedAt:a.STRING,conclusionUpdatedAt:a.STRING,createdAt:a.STRING,updatedAt:a.STRING,prLinks:a.JSON,attempt:a.INTEGER,dependsOn:a.JSON,prMonitor:a.BOOLEAN,prMonitorCount:a.INTEGER,prMonitorLastAt:a.STRING,prMonitorNote:a.TEXT,url:a.STRING,workspaceId:a.STRING,workingDir:a.STRING,column:a.STRING,position:a.INTEGER,dismissedAt:a.STRING},{tableName:"tickets",underscored:!0,timestamps:!1}),Pe=k.define("Run",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,ticketId:a.STRING,ticketKey:a.STRING,title:a.STRING,provider:a.STRING,workspaceId:a.STRING,workspaceName:a.STRING,assignmentKind:a.STRING,teamName:a.STRING,agentName:a.STRING,autonomy:a.STRING,status:a.STRING,stopReason:a.STRING,sessionId:a.STRING,branchName:a.STRING,prUrl:a.STRING,worktrees:a.JSON,blocker:a.JSON,plan:a.JSON,events:a.JSON,resultText:a.TEXT,pid:a.INTEGER,pgid:a.INTEGER,createdAt:a.STRING,updatedAt:a.STRING},{tableName:"runs",underscored:!0,timestamps:!1}),Je=k.define("Schedule",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,name:a.STRING,prompt:a.TEXT,workspaceId:a.STRING,workspaceName:a.STRING,assignmentKind:a.STRING,teamId:a.STRING,teamName:a.STRING,agentId:a.STRING,agentName:a.STRING,cadence:a.STRING,workingDir:a.STRING,model:a.STRING,effort:a.STRING,enabled:a.BOOLEAN,starred:a.BOOLEAN,createdAt:a.STRING,lastRunAt:a.STRING,nextRunAt:a.STRING},{tableName:"schedules",underscored:!0,timestamps:!1}),Oe=k.define("ScheduleRun",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,scheduleId:a.STRING,status:a.STRING,sessionId:a.STRING,startedAt:a.STRING,finishedAt:a.STRING,output:a.TEXT,model:a.STRING,effort:a.STRING,seq:a.INTEGER},{tableName:"schedule_runs",underscored:!0,timestamps:!1}),Ht=k.define("Batch",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,name:a.STRING,workspaceId:a.STRING,workspaceName:a.STRING,ticketIds:a.JSON,mode:a.STRING,advanceMode:a.STRING,stackDependentWork:a.BOOLEAN,status:a.STRING,budgetCap:a.INTEGER,pausedReason:a.TEXT,runsStarted:a.INTEGER,createdAt:a.STRING,updatedAt:a.STRING},{tableName:"batches",underscored:!0,timestamps:!1}),De=k.define("Conversation",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,title:a.STRING,workspaceId:a.STRING,workspaceName:a.STRING,tier:a.STRING,effort:a.STRING,sessionId:a.STRING,workingDir:a.STRING,messages:a.JSON,starred:a.BOOLEAN,createdAt:a.STRING,updatedAt:a.STRING},{tableName:"conversations",underscored:!0,timestamps:!1}),ct=k.define("Setting",{userId:{type:a.STRING,primaryKey:!0},defaultAutonomy:a.STRING,maxConcurrentRuns:a.INTEGER,branchPrefix:a.STRING,connectedWorkspaces:a.JSON,workspaceDirs:a.JSON,defaultModel:a.STRING,defaultEffort:a.STRING,prMonitorDefault:a.BOOLEAN,prMonitorIntervalMinutes:a.INTEGER,prMonitorMaxEngagements:a.INTEGER,prMonitorReplyToComments:a.BOOLEAN,sandboxWorktreeEdits:a.BOOLEAN,autoAssignImports:a.BOOLEAN,importDelegate:a.JSON,publicApiEnabled:a.BOOLEAN,apiKeyHash:a.STRING,apiKeyLast4:a.STRING,apiKeyCreatedAt:a.STRING,webhookEnabled:a.BOOLEAN,webhookUrl:a.STRING,webhookHeaders:a.JSON,webhookEvents:a.JSON,webhookSecret:a.TEXT},{tableName:"settings",underscored:!0,timestamps:!1}),ut=k.define("OAuthToken",{connectionId:{type:a.STRING,primaryKey:!0},userId:a.STRING,provider:a.STRING,accessToken:a.TEXT,refreshToken:a.TEXT,scope:a.STRING,expiresAt:a.STRING},{tableName:"oauth_tokens",underscored:!0,timestamps:!1}),je=k.define("RunResult",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,ticketId:a.STRING,runId:a.STRING,sessionId:a.STRING,prompt:a.TEXT,response:a.TEXT,status:a.STRING,model:a.STRING,effort:a.STRING,createdAt:a.STRING},{tableName:"run_results",underscored:!0,timestamps:!1}),ze=k.define("Note",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,ticketId:a.STRING,scheduleId:a.STRING,author:a.STRING,body:a.TEXT,createdAt:a.STRING,updatedAt:a.STRING},{tableName:"notes",underscored:!0,timestamps:!1}),Me=k.define("MessagePreset",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,title:a.STRING,prompt:a.TEXT,enabled:a.BOOLEAN,position:a.INTEGER,createdAt:a.STRING,updatedAt:a.STRING},{tableName:"message_presets",underscored:!0,timestamps:!1}),Be=k.define("OpenQuestion",{id:{type:a.STRING,primaryKey:!0},userId:a.STRING,ticketId:a.STRING,question:a.TEXT,answer:a.TEXT,resolved:a.BOOLEAN,position:a.INTEGER,createdAt:a.STRING,updatedAt:a.STRING},{tableName:"open_questions",underscored:!0,timestamps:!1}),ds={Connection:Qe,Ticket:ve,Run:Pe,Schedule:Je,ScheduleRun:Oe,Batch:Ht,Conversation:De,Setting:ct,OAuthToken:ut,RunResult:je,Note:ze,MessagePreset:Me,OpenQuestion:Be},pa=new la({context:k.getQueryInterface(),storage:{async logMigration({name:e}){await k.query("INSERT INTO pipelines_migrations (name) VALUES (:name)",{replacements:{name:e}})},async unlogMigration({name:e}){await k.query("DELETE FROM pipelines_migrations WHERE name = :name",{replacements:{name:e}})},async executed(){await k.query("CREATE TABLE IF NOT EXISTS pipelines_migrations (name TEXT PRIMARY KEY)");let[e]=await k.query("SELECT name FROM pipelines_migrations ORDER BY name");return e.map(t=>t.name)}},logger:void 0,migrations:[{name:"0001-init",async up(){await k.sync()}},{name:"0002-oauth-tokens",async up(){await ut.sync()}},{name:"0003-settings-branch-prefix",async up(){let e=k.getQueryInterface();(await e.describeTable("settings")).branch_prefix||await e.addColumn("settings","branch_prefix",{type:a.STRING,allowNull:!0})}},{name:"0004-settings-connected-workspaces",async up(){let e=k.getQueryInterface();(await e.describeTable("settings")).connected_workspaces||await e.addColumn("settings","connected_workspaces",{type:a.JSON,allowNull:!0})}},{name:"0005-runs-result-text",async up(){let e=k.getQueryInterface();(await e.describeTable("runs")).result_text||await e.addColumn("runs","result_text",{type:a.TEXT,allowNull:!0})}},{name:"0006-run-results",async up(){let e=k.getQueryInterface();(await e.showAllTables()).map(String).includes("run_results")||await e.createTable("run_results",{id:{type:a.STRING,primaryKey:!0},user_id:a.STRING,ticket_id:a.STRING,run_id:a.STRING,session_id:a.STRING,prompt:a.TEXT,response:a.TEXT,status:a.STRING,created_at:a.STRING})}},{name:"0007-comments",async up(){let e=k.getQueryInterface();(await e.showAllTables()).map(String).includes("comments")||await e.createTable("comments",{id:{type:a.STRING,primaryKey:!0},user_id:a.STRING,ticket_id:a.STRING,author:a.STRING,body:a.TEXT,created_at:a.STRING})}},{name:"0008-comments-updated-at",async up(){let e=k.getQueryInterface();(await e.describeTable("comments")).updated_at||await e.addColumn("comments","updated_at",{type:a.STRING,allowNull:!0})}},{name:"0009-conversations-working-dir",async up(){let e=k.getQueryInterface();(await e.describeTable("conversations")).working_dir||await e.addColumn("conversations","working_dir",{type:a.STRING,allowNull:!0})}},{name:"0010-settings-workspace-dirs",async up(){let e=k.getQueryInterface();(await e.describeTable("settings")).workspace_dirs||await e.addColumn("settings","workspace_dirs",{type:a.JSON,allowNull:!0})}},{name:"0011-tickets-working-dir",async up(){let e=k.getQueryInterface();(await e.describeTable("tickets")).working_dir||await e.addColumn("tickets","working_dir",{type:a.STRING,allowNull:!0})}},{name:"0012-schedules-working-dir",async up(){let e=k.getQueryInterface();(await e.describeTable("schedules")).working_dir||await e.addColumn("schedules","working_dir",{type:a.STRING,allowNull:!0})}},{name:"0013-rename-comments-to-notes",async up(){let e=k.getQueryInterface(),t=(await e.showAllTables()).map(String);t.includes("comments")&&!t.includes("notes")&&await e.renameTable("comments","notes")}},{name:"0014-notes-schedule-id",async up(){let e=k.getQueryInterface();(await e.describeTable("notes")).schedule_id||await e.addColumn("notes","schedule_id",{type:a.STRING,allowNull:!0})}},{name:"0015-connections-sync-filters",async up(){let e=k.getQueryInterface();(await e.describeTable("connections")).sync_filters||await e.addColumn("connections","sync_filters",{type:a.JSON,allowNull:!0})}},{name:"0016-tickets-parent",async up(){let e=k.getQueryInterface();(await e.describeTable("tickets")).parent||await e.addColumn("tickets","parent",{type:a.JSON,allowNull:!0})}},{name:"0017-conversations-effort",async up(){let e=k.getQueryInterface();(await e.describeTable("conversations")).effort||await e.addColumn("conversations","effort",{type:a.STRING,allowNull:!0})}},{name:"0018-model-effort",async up(){let e=k.getQueryInterface(),t={type:a.STRING,allowNull:!0},n=async(r,s)=>{(await e.describeTable(r))[s]||await e.addColumn(r,s,t)};await n("settings","default_model"),await n("settings","default_effort");for(let r of["tickets","schedules","run_results","schedule_runs"])await n(r,"model"),await n(r,"effort")}},{name:"0019-tickets-blocked-reason",async up(){let e=k.getQueryInterface();(await e.describeTable("tickets")).blocked_reason||await e.addColumn("tickets","blocked_reason",{type:a.TEXT,allowNull:!0})}},{name:"0020-message-presets",async up(){let e=k.getQueryInterface();(await e.showAllTables()).map(String).includes("message_presets")||await e.createTable("message_presets",{id:{type:a.STRING,primaryKey:!0},user_id:a.STRING,title:a.STRING,prompt:a.TEXT,enabled:a.BOOLEAN,position:a.INTEGER,created_at:a.STRING,updated_at:a.STRING})}},{name:"0021-ticket-context-fields",async up(){let e=k.getQueryInterface(),t=await e.describeTable("tickets"),n={type:a.TEXT,allowNull:!0};t.additional_context||await e.addColumn("tickets","additional_context",n),!t.tech_plan&&!t.plan&&await e.addColumn("tickets","tech_plan",n),t.conclusion||await e.addColumn("tickets","conclusion",n)}},{name:"0022-open-questions",async up(){let e=k.getQueryInterface();(await e.showAllTables()).map(String).includes("open_questions")||await e.createTable("open_questions",{id:{type:a.STRING,primaryKey:!0},user_id:a.STRING,ticket_id:a.STRING,question:a.TEXT,answer:a.TEXT,resolved:a.BOOLEAN,position:a.INTEGER,created_at:a.STRING,updated_at:a.STRING})}},{name:"0023-rename-tech-plan-to-plan",async up(){let e=k.getQueryInterface(),t=await e.describeTable("tickets");t.tech_plan&&!t.plan&&await e.renameColumn("tickets","tech_plan","plan")}},{name:"0024-ticket-context-timestamps",async up(){let e=k.getQueryInterface(),t=await e.describeTable("tickets"),n={type:a.STRING,allowNull:!0};t.additional_context_updated_at||await e.addColumn("tickets","additional_context_updated_at",n),t.plan_updated_at||await e.addColumn("tickets","plan_updated_at",n),t.conclusion_updated_at||await e.addColumn("tickets","conclusion_updated_at",n)}},{name:"0025-ticket-created-updated",async up(){let e=k.getQueryInterface(),t=await e.describeTable("tickets"),n={type:a.STRING,allowNull:!0};t.created_at||await e.addColumn("tickets","created_at",n),t.updated_at||await e.addColumn("tickets","updated_at",n)}},{name:"0026-ticket-pr-links",async up(){let e=k.getQueryInterface();(await e.describeTable("tickets")).pr_links||await e.addColumn("tickets","pr_links",{type:a.JSON,allowNull:!0})}},{name:"0027-run-worktrees",async up(){let e=k.getQueryInterface();(await e.describeTable("runs")).worktrees||await e.addColumn("runs","worktrees",{type:a.JSON,allowNull:!0})}},{name:"0028-ticket-attempt",async up(){let e=k.getQueryInterface();(await e.describeTable("tickets")).attempt||await e.addColumn("tickets","attempt",{type:a.INTEGER,allowNull:!0})}},{name:"0029-pr-monitor",async up(){let e=k.getQueryInterface(),t={type:a.INTEGER,allowNull:!0},n={type:a.BOOLEAN,allowNull:!0},r={type:a.STRING,allowNull:!0},s={type:a.TEXT,allowNull:!0},o=await e.describeTable("settings");o.pr_monitor_default||await e.addColumn("settings","pr_monitor_default",n),o.pr_monitor_interval_minutes||await e.addColumn("settings","pr_monitor_interval_minutes",t),o.pr_monitor_max_engagements||await e.addColumn("settings","pr_monitor_max_engagements",t),o.pr_monitor_reply_to_comments||await e.addColumn("settings","pr_monitor_reply_to_comments",n);let i=await e.describeTable("tickets");i.pr_monitor||await e.addColumn("tickets","pr_monitor",n),i.pr_monitor_count||await e.addColumn("tickets","pr_monitor_count",t),i.pr_monitor_last_at||await e.addColumn("tickets","pr_monitor_last_at",r),i.pr_monitor_note||await e.addColumn("tickets","pr_monitor_note",s)}},{name:"0030-ticket-depends-on",async up(){let e=k.getQueryInterface();(await e.describeTable("tickets")).depends_on||await e.addColumn("tickets","depends_on",{type:a.JSON,allowNull:!0})}},{name:"0031-settings-sandbox",async up(){let e=k.getQueryInterface();(await e.describeTable("settings")).sandbox_worktree_edits||await e.addColumn("settings","sandbox_worktree_edits",{type:a.BOOLEAN,allowNull:!0})}},{name:"0032-connection-cloud-id",async up(){let e=k.getQueryInterface();(await e.describeTable("connections")).cloud_id||await e.addColumn("connections","cloud_id",{type:a.STRING,allowNull:!0})}},{name:"0033-ticket-reporter-due-date",async up(){let e=k.getQueryInterface(),t=await e.describeTable("tickets");t.reporter||await e.addColumn("tickets","reporter",{type:a.JSON,allowNull:!0}),t.due_date||await e.addColumn("tickets","due_date",{type:a.STRING,allowNull:!0})}},{name:"0034-connection-provider-account-id",async up(){let e=k.getQueryInterface();(await e.describeTable("connections")).provider_account_id||await e.addColumn("connections","provider_account_id",{type:a.STRING,allowNull:!0})}},{name:"0035-settings-public-api",async up(){let e=k.getQueryInterface(),t=await e.describeTable("settings"),n=async(r,s)=>{t[r]||await e.addColumn("settings",r,{type:s,allowNull:!0})};await n("public_api_enabled",a.BOOLEAN),await n("api_key_hash",a.STRING),await n("api_key_last4",a.STRING),await n("api_key_created_at",a.STRING),await n("webhook_enabled",a.BOOLEAN),await n("webhook_url",a.STRING),await n("webhook_headers",a.JSON),await n("webhook_events",a.JSON),await n("webhook_secret",a.TEXT)}},{name:"0036-ticket-comments",async up(){let e=k.getQueryInterface();(await e.describeTable("tickets")).comments||await e.addColumn("tickets","comments",{type:a.JSON,allowNull:!0})}},{name:"0037-batches",async up(){let e=k.getQueryInterface();(await e.showAllTables()).map(String).includes("batches")||await e.createTable("batches",{id:{type:a.STRING,primaryKey:!0},user_id:a.STRING,name:a.STRING,workspace_id:a.STRING,workspace_name:a.STRING,ticket_ids:a.JSON,mode:a.STRING,advance_mode:a.STRING,stack_dependent_work:a.BOOLEAN,status:a.STRING,budget_cap:a.INTEGER,paused_reason:a.TEXT,runs_started:a.INTEGER,created_at:a.STRING,updated_at:a.STRING})}},{name:"0038-auto-ingest-and-assign",async up(){let e=k.getQueryInterface();(await e.describeTable("connections")).auto_ingest_tickets||await e.addColumn("connections","auto_ingest_tickets",{type:a.BOOLEAN,allowNull:!0});let n=await e.describeTable("settings");n.auto_assign_imports||await e.addColumn("settings","auto_assign_imports",{type:a.BOOLEAN,allowNull:!0}),n.import_delegate||await e.addColumn("settings","import_delegate",{type:a.JSON,allowNull:!0})}},{name:"0039-ticket-dismissed-at",async up(){let e=k.getQueryInterface();(await e.describeTable("tickets")).dismissed_at||await e.addColumn("tickets","dismissed_at",{type:a.STRING,allowNull:!0})}}]})});Z();dt();import Xn from"express";import Ld from"cors";import{randomUUID as ot}from"node:crypto";import re from"node:path";import Ce from"node:fs";import zn from"node:os";import{fileURLToPath as Gd}from"node:url";Z();import $e from"node:crypto";import nr from"node:fs";import fa from"node:os";import or from"node:path";var sr="enc:v1:",ma=process.env.SUPERCLAWD_HOME||or.join(fa.homedir(),".superclawd"),rr=process.env.PIPELINES_KEY_FILE||or.join(ma,"pipelines.key"),Qt=null;function ls(){if(Qt)return Qt;nr.mkdirSync(or.dirname(rr),{recursive:!0});try{let t=nr.readFileSync(rr);if(t.length===32)return Qt=t,Qt}catch{}let e=$e.randomBytes(32);return nr.writeFileSync(rr,e,{mode:384}),Qt=e,e}function lt(e){let t=$e.randomBytes(12),n=$e.createCipheriv("aes-256-gcm",ls(),t),r=Buffer.concat([n.update(e,"utf8"),n.final()]),s=n.getAuthTag();return`${sr}${t.toString("base64")}:${s.toString("base64")}:${r.toString("base64")}`}function pt(e){if(!e.startsWith(sr))return e;let[t,n,r]=e.slice(sr.length).split(":"),s=$e.createDecipheriv("aes-256-gcm",ls(),Buffer.from(t,"base64"));return s.setAuthTag(Buffer.from(n,"base64")),Buffer.concat([s.update(Buffer.from(r,"base64")),s.final()]).toString("utf8")}function ft(e){return $e.createHash("sha256").update(e,"utf8").digest("hex")}function mt(e="",t=32){return e+$e.randomBytes(t).toString("base64url")}function Jt(e,t){let n=Buffer.from(e),r=Buffer.from(t);return n.length!==r.length?!1:$e.timingSafeEqual(n,r)}function ps(e,t){return $e.createHmac("sha256",e).update(t,"utf8").digest("hex")}dt();function ga(e){let t={};for(let[n,r]of Object.entries(e))r!=null&&(t[n]=r);return t}var ee=e=>e.get({plain:!0}),ie=(e,t)=>{let n={...e};for(let r of t)delete n[r];return ga(n)};function fs(e){let t={};for(let[n,r]of Object.entries(e))t[n]=r===void 0?null:r;return t}function gt(e,t){let n=0;for(let r of e){let s=new RegExp(`^${t}_?(\\d+)$`).exec(r);s&&(n=Math.max(n,Number(s[1])))}return n}async function ms(){let e={where:{userId:C()}},t=await Qe.findAll(e);b.length=0,b.push(...t.map(f=>ie(ee(f),["userId"])));let n=await ve.findAll({...e,order:[["position","ASC"]]});h.length=0;for(let f of Object.keys(S))delete S[f];for(let f of n){let w=ee(f);S[w.id]=w.column??"backlog",h.push(ie(w,["userId","column","position"]))}let r=await Pe.findAll(e);for(let f of Object.keys(x))delete x[f];for(let f of Object.keys(T))delete T[f];for(let f of r){let w=ie(ee(f),["userId","pid","pgid"]);x[w.id]=w,T[w.ticketId]=w.id}let s=await Je.findAll({...e,order:[["createdAt","ASC"]]});for(let f of Object.keys(O))delete O[f];se.length=0;for(let f of s){let w=ie(ee(f),["userId"]);O[w.id]=w,se.push(w.id)}let o=await Oe.findAll({...e,order:[["seq","DESC"]]});for(let f of Object.keys(z))delete z[f];for(let f of se)z[f]=[];for(let f of o){let w=ee(f),V=ie(w,["userId","seq"]);(z[w.scheduleId]??=[]).push(V)}let i=await Ht.findAll({...e,order:[["createdAt","ASC"]]});for(let f of Object.keys(L))delete L[f];K.length=0;for(let f of i){let w=ie(ee(f),["userId"]);L[w.id]=w,K.push(w.id)}let c=await De.findAll({...e,order:[["updatedAt","DESC"]]});for(let f of Object.keys(W))delete W[f];ce.length=0;for(let f of c){let w=ie(ee(f),["userId"]);w.messages||(w.messages=[]),W[w.id]=w,ce.push(w.id)}let d=await je.findAll({...e,order:[["createdAt","DESC"]]});X.length=0;for(let f of d)X.push(ie(ee(f),["userId"]));let u=await ze.findAll({...e,order:[["createdAt","ASC"]]});j.length=0;for(let f of u)j.push(ie(ee(f),["userId"]));let p=await Me.findAll({...e,order:[["position","ASC"]]});H.length=0;for(let f of p)H.push(ie(ee(f),["userId"]));let g=await Be.findAll({...e,order:[["position","ASC"]]});D.length=0;for(let f of g)D.push(ie(ee(f),["userId"]));let I=await ct.findByPk(C());if(I){let f=ee(I);l.defaultAutonomy=f.defaultAutonomy??l.defaultAutonomy,typeof f.maxConcurrentRuns=="number"&&(l.maxConcurrentRuns=f.maxConcurrentRuns),typeof f.branchPrefix=="string"&&(l.branchPrefix=f.branchPrefix),Array.isArray(f.connectedWorkspaces)&&(l.connectedWorkspaces=f.connectedWorkspaces),f.workspaceDirs&&typeof f.workspaceDirs=="object"&&(l.workspaceDirs=f.workspaceDirs),typeof f.defaultModel=="string"&&(l.defaultModel=f.defaultModel),typeof f.defaultEffort=="string"&&(l.defaultEffort=f.defaultEffort),typeof f.prMonitorDefault=="boolean"&&(l.prMonitorDefault=f.prMonitorDefault),typeof f.prMonitorIntervalMinutes=="number"&&(l.prMonitorIntervalMinutes=f.prMonitorIntervalMinutes),typeof f.prMonitorMaxEngagements=="number"&&(l.prMonitorMaxEngagements=f.prMonitorMaxEngagements),typeof f.prMonitorReplyToComments=="boolean"&&(l.prMonitorReplyToComments=f.prMonitorReplyToComments),typeof f.sandboxWorktreeEdits=="boolean"&&(l.sandboxWorktreeEdits=f.sandboxWorktreeEdits),typeof f.autoAssignImports=="boolean"&&(l.autoAssignImports=f.autoAssignImports),f.importDelegate&&typeof f.importDelegate=="object"&&(l.importDelegate=f.importDelegate),typeof f.publicApiEnabled=="boolean"&&(l.publicApiEnabled=f.publicApiEnabled),typeof f.apiKeyHash=="string"&&(l.apiKeyHash=f.apiKeyHash),typeof f.apiKeyLast4=="string"&&(l.apiKeyLast4=f.apiKeyLast4),typeof f.apiKeyCreatedAt=="string"&&(l.apiKeyCreatedAt=f.apiKeyCreatedAt),typeof f.webhookEnabled=="boolean"&&(l.webhookEnabled=f.webhookEnabled),typeof f.webhookUrl=="string"&&(l.webhookUrl=f.webhookUrl),Array.isArray(f.webhookHeaders)&&(l.webhookHeaders=f.webhookHeaders.map(w=>({key:w.key,value:pt(String(w.value??""))}))),Array.isArray(f.webhookEvents)&&(l.webhookEvents=f.webhookEvents),typeof f.webhookSecret=="string"&&(l.webhookSecret=pt(f.webhookSecret))}G.convSeq=gt(Object.keys(W),"conv"),G.schedSeq=gt(Object.keys(O),"sched"),G.batchSeq=gt(Object.keys(L),"batch"),G.localSeq=Math.max(G.localSeq,gt(h.map(f=>f.id),"t_local")),G.schedRunSeq=gt(o.map(f=>ee(f).id),"srun"),G.runSeq=gt(Object.keys(x),"run")}async function N(e){await Qe.upsert({...e,userId:C()})}async function gs(e){await Qe.destroy({where:{id:e}})}async function fn(){for(let e of $){let t=`conn_local_${e.id}`;if(b.some(r=>r.provider==="local"&&r.workspaceId===e.id))continue;let n={id:t,provider:"local",providerAccountName:"Local",workspaceId:e.id,workspaceName:e.name,workingDir:null,linkStatus:"linked",authStatus:"connected",lastSyncedAt:null,ticketCount:0};b.push(n),await N(n)}}async function A(e){let t=h.find(n=>n.id===e);t&&await ve.upsert(fs({...t,userId:C(),column:S[e]??"backlog",position:h.indexOf(t)}))}async function hs(){await Promise.all(h.map((e,t)=>ve.update({position:t,column:S[e.id]??"backlog"},{where:{id:e.id}})))}async function mn(e){await ve.destroy({where:{id:e}}),await Pe.destroy({where:{ticketId:e}})}async function U(e){let t=x[e];t&&await Pe.upsert({...t,userId:C()})}async function ks(e){await Pe.destroy({where:{id:e}})}async function zt(e){let t=O[e];t&&await Je.upsert({...t,userId:C()})}async function ys(e){await Je.destroy({where:{id:e}}),await Oe.destroy({where:{scheduleId:e}})}async function ue(e){let t=L[e];t&&await Ht.upsert({...t,userId:C()})}async function Xt(e){await Ht.destroy({where:{id:e}})}async function gn(e,t){await Oe.upsert({...e,userId:C(),seq:t})}async function Xe(e){let t=W[e];t&&await De.upsert({...t,userId:C()})}async function ws(e){await De.destroy({where:{id:e}})}async function hn(e){await je.upsert({...e,userId:C()})}async function bs(e){await je.destroy({where:{ticketId:e,userId:C()}})}async function Is(e,t,n){let{count:r,rows:s}=await je.findAndCountAll({where:{ticketId:e,userId:C()},order:[["createdAt","DESC"]],limit:t,offset:n});return{entries:s.map(o=>ie(ee(o),["userId"])),total:r}}async function Ss(e,t,n){let{count:r,rows:s}=await Oe.findAndCountAll({where:{scheduleId:e,userId:C()},order:[["seq","DESC"]],limit:t,offset:n});return{runs:s.map(o=>ie(ee(o),["userId","seq"])),total:r}}async function ht(e){await ze.upsert({...e,userId:C()})}async function kn(e){await ze.destroy({where:{id:e}})}async function yn(e){let t=H.find(n=>n.id===e);t&&await Me.upsert({...t,userId:C()})}async function xs(e){await Me.destroy({where:{id:e}})}async function vs(){await Promise.all(H.map((e,t)=>Me.update({position:t},{where:{id:e.id}})))}async function ir(e){let t=D.find(n=>n.id===e);t&&await Be.upsert(fs({...t,userId:C()}))}async function wn(e){await Be.destroy({where:{id:e}})}async function Rs(){await Promise.all(D.map((e,t)=>Be.update({position:t},{where:{id:e.id}})))}async function te(){await ct.upsert({userId:C(),defaultAutonomy:l.defaultAutonomy,maxConcurrentRuns:l.maxConcurrentRuns??3,branchPrefix:l.branchPrefix??"feature",connectedWorkspaces:l.connectedWorkspaces??null,workspaceDirs:l.workspaceDirs??null,defaultModel:l.defaultModel??null,defaultEffort:l.defaultEffort??null,prMonitorDefault:l.prMonitorDefault??null,prMonitorIntervalMinutes:l.prMonitorIntervalMinutes??null,prMonitorMaxEngagements:l.prMonitorMaxEngagements??null,prMonitorReplyToComments:l.prMonitorReplyToComments??null,sandboxWorktreeEdits:l.sandboxWorktreeEdits??null,autoAssignImports:l.autoAssignImports??null,importDelegate:l.importDelegate??null,publicApiEnabled:l.publicApiEnabled??null,apiKeyHash:l.apiKeyHash??null,apiKeyLast4:l.apiKeyLast4??null,apiKeyCreatedAt:l.apiKeyCreatedAt??null,webhookEnabled:l.webhookEnabled??null,webhookUrl:l.webhookUrl??null,webhookHeaders:l.webhookHeaders?l.webhookHeaders.map(e=>({key:e.key,value:lt(e.value)})):null,webhookEvents:l.webhookEvents??null,webhookSecret:l.webhookSecret?lt(l.webhookSecret):null})}async function Ts(){let e={where:{userId:C()}};await Promise.all(Object.values(ds).map(t=>t.destroy(e)))}async function _s(){await Ts();let{seedFromFixtures:e}=await Promise.resolve().then(()=>(tr(),er));await e()}async function As(){let e=new Set($.map(o=>o.id)),t=[],n=new Set;for(let o of b)o.provider!=="local"||!o.workspaceId||!e.has(o.workspaceId)||n.has(o.workspaceId)||(n.add(o.workspaceId),t.push(o));let r=o=>{o.length=0},s=o=>{for(let i of Object.keys(o))delete o[i]};r(b),b.push(...t),r(h),r(Y),r(X),r(j),r(D),r(se),r(K),r(ce),s(x),s(T),s(S),s(O),s(z),s(L),s(W),await Ts();for(let o of t)await N(o);await fn(),await te();for(let o of H)await yn(o.id)}import bn from"node:fs";import ha from"node:os";import ar from"node:path";function ka(){let e=process.env.CLAUDE_CONFIG_DIR||ar.join(ha.homedir(),".claude");return ar.join(e,"projects")}function Ns(e){let t=ka(),n;try{n=bn.readdirSync(t)}catch{return null}let r=`${e}.jsonl`;for(let s of n){let o=ar.join(t,s,r);if(bn.existsSync(o))return o}return null}function ya(e){return Array.isArray(e)?e.filter(t=>!!t&&typeof t=="object"&&t.type==="text"&&typeof t.text=="string").map(t=>t.text).join("").trim():typeof e=="string"?e.trim():""}function In(e){return cr(e).response}function cr(e){let t=Ns(e);if(!t)return{prompt:null,response:null};let n;try{n=bn.readFileSync(t,"utf8")}catch{return{prompt:null,response:null}}let r=null,s=null;for(let o of n.split(`
|
|
9
|
+
`)){let i=o.trim();if(!i)continue;let c;try{c=JSON.parse(i)}catch{continue}let d=c,u=ya(d.message?.content);u&&(d.type==="user"&&r===null&&(r=u),d.type==="assistant"&&(s=u))}return{prompt:r,response:s}}function wa(e){let t="";typeof e=="string"?t=e:Array.isArray(e)&&(t=e.map(s=>s&&typeof s=="object"&&typeof s.text=="string"?s.text:"").join(""));let n=t.trim(),r=600;return n.length>r?`${n.slice(0,r)}
|
|
10
|
+
\u2026 (truncated)`:n}function ba(e,t){let n=typeof e=="string"?e:"tool",r=n.includes("__")?n.slice(n.lastIndexOf("__")+2):n,s=t??{},o=(i,c=80)=>{let u=(typeof i=="string"?i:i==null?"":JSON.stringify(i)).replace(/\s+/g," ").trim();return u.length>c?`${u.slice(0,c)}\u2026`:u};switch(r){case"Bash":return`$ ${o(s.command,120)}`;case"Read":return`Read ${o(s.file_path)}`;case"Edit":case"Write":case"NotebookEdit":return`${r} ${o(s.file_path)}`;case"Grep":return`Grep ${o(s.pattern)}`;case"Glob":return`Glob ${o(s.pattern)}`;case"Task":return`Task \u2192 ${o(s.subagent_type,40)}: ${o(s.description,80)}`;case"WebFetch":return`WebFetch ${o(s.url)}`;default:{let i=s.file_path??s.pattern??s.command??s.repo??s.query;return i!==void 0?`${r} ${o(i)}`:Object.keys(s).length?`${r}(${o(s,80)})`:r}}}function dr(e){let t=Ns(e);if(!t)return[];let n;try{n=bn.readFileSync(t,"utf8")}catch{return[]}let r=[];for(let s of n.split(`
|
|
11
|
+
`)){let o=s.trim();if(!o)continue;let i;try{i=JSON.parse(o)}catch{continue}let c=i,d=c.type,u=c.message?.content;if(typeof u=="string"){let p=u.trim();if(!p)continue;d==="user"?r.push({type:"prompt",text:p}):d==="assistant"&&r.push({type:"text",text:p});continue}if(Array.isArray(u))for(let p of u){if(!p||typeof p!="object")continue;let g=p;if(g.type==="text"&&typeof g.text=="string"&&g.text.trim()){let I=g.text.trim();d==="user"?r.push({type:"prompt",text:I}):d==="assistant"&&r.push({type:"text",text:I})}else if(g.type==="tool_use")r.push({type:"tool",text:ba(g.name,g.input)});else if(g.type==="tool_result"){let I=wa(g.content);I&&r.push({type:"tool_result",text:I,isError:g.is_error===!0})}}}return r}dt();Z();import Ia from"node:fs";import Sa from"node:os";import Ps from"node:path";import{randomUUID as Os}from"node:crypto";var xa=process.env.SUPERCLAWD_HOME||Ps.join(Sa.homedir(),".superclawd"),va=Ps.join(xa,"config.json"),Ds=process.env.SUPERCLAWD_MCP_API_URL||"https://mcp-api.superclawd.com",js=process.env.PIPELINES_BACKEND==="mock"||process.env.SUPERCLAWD_BACKEND==="mock",vn=3,ur="__logged_out__";function E(){return js}var kt={loggedIn:!1,userId:ur,email:null};function he(){return lr()}function lr(){let e=process.env.SUPERCLAWD_MCP_KEY,t=process.env.SUPERCLAWD_MCP_SECRET;if(e&&t)return{keyId:e,secret:t};try{let n=JSON.parse(Ia.readFileSync(va,"utf8")),r=n?.credentials??n;if(typeof r?.keyId=="string"&&typeof r?.secret=="string")return{keyId:r.keyId,secret:r.secret}}catch{}return null}var xn=[];function Yt(){return xn}var Es=new Map,Ra=5*6e4;async function yt(e){let t=Es.get(e);if(t&&Date.now()-t.at<Ra)return{agents:t.agents,teams:t.teams};let n=lr();if(!n)return null;try{let r=await fetch(`${Ds}/api/prompt/agents-teams`,{headers:{"content-type":"application/json","x-mcp-key-id":n.keyId,"x-mcp-secret":n.secret,"x-workspace-slug":e,"x-request-id":Os()}});if(!r.ok)return null;let s=await r.json().catch(()=>null),o=s?.agents??[],i=s?.teams??[];return Es.set(e,{agents:o,teams:i,at:Date.now()}),{agents:o,teams:i}}catch{return null}}function Ta(e){return e.map(t=>({id:t.slug,name:t.name,tier:t.tier==="paid"?"paid":"free"}))}function Cs(e){return e===401?"Your credentials are invalid or expired. Run `superclawd login`.":e===403?"This account isn't permitted to use Pipelines.":e===0||e>=500?"The SuperClawd backend is unreachable. Try again in a moment.":`Authentication failed (HTTP ${e}).`}function Sn(e){return pn(ur),Ie.loggedIn=!1,delete Ie.account,$.length=0,xn=[],kt={loggedIn:!1,userId:ur,email:null,error:e},kt}async function Ms(){if(js)return pn(Kt),Ie.loggedIn=!0,xn=[...$],kt={loggedIn:!0,userId:Kt,email:Ie.account?.email??null},kt;let e=lr();if(!e)return Sn("Not authenticated. Run `superclawd login` to get started.");let t=d=>new Promise(u=>setTimeout(u,d)),n=8,r=2e3,s=0,o=null,i=!1;for(let d=1;d<=n;d++){try{let u=await fetch(`${Ds}/api/prompt/workspaces`,{headers:{"content-type":"application/json","x-mcp-key-id":e.keyId,"x-mcp-secret":e.secret,"x-request-id":Os()}});if(s=u.status,o=await u.json().catch(()=>null),u.ok){i=!0;break}if(s<500)return Sn(Cs(s))}catch{s=0}d===1&&console.log("[pipelines] waiting for the SuperClawd backend to come up\u2026"),d<n&&await t(r)}if(!i)return Sn(Cs(s));let c=o?.userId;return c?(pn(c),Ie.loggedIn=!0,Ie.account={email:o?.email??"",org:"SuperClawd"},xn=Ta(o?.workspaces??[]),$.length=0,kt={loggedIn:!0,userId:c,email:o?.email??null},kt):Sn("The backend didn't return an account id. Update the CLI/backend.")}Z();import{execFile as _a}from"node:child_process";import{existsSync as Tn}from"node:fs";import{rm as Aa}from"node:fs/promises";import{homedir as Na}from"node:os";import{dirname as Ea,join as Rn,resolve as Bs}from"node:path";var pr=Rn(Na(),".superclawd","pipelines-worktrees");function oe(e,t){return new Promise(n=>{_a("git",t,{cwd:e,maxBuffer:10*1024*1024},(r,s,o)=>{n({code:r&&typeof r.code=="number"?r.code:r?1:0,stdout:String(s??"").trim(),stderr:String(o??"").trim()})})})}async function $s(e){if(!e||!Tn(e))return!1;let t=await oe(e,["rev-parse","--is-inside-work-tree"]);return t.code===0&&t.stdout==="true"}async function Ge(e){if(!e||!Tn(e))return[];if(await $s(e))return[{name:".",path:Bs(e)}];let{readdir:t}=await import("node:fs/promises"),n=await t(e,{withFileTypes:!0}).catch(()=>[]),r=[];for(let s of n){if(!s.isDirectory()||s.name.startsWith("."))continue;let o=Rn(e,s.name);await $s(o)&&r.push({name:s.name,path:Bs(o)})}return r}async function Ls(e,t){let n=await Ge(e);if(!n.length)return null;let r=(t??"").trim();return!r||r==="."?n.length===1?n[0]:null:n.find(s=>s.name===r)??null}async function Gs(e){let t=await oe(e,["symbolic-ref","--short","refs/remotes/origin/HEAD"]);if(t.code===0&&t.stdout)return t.stdout.replace(/^origin\//,"");let n=await oe(e,["rev-parse","--abbrev-ref","HEAD"]);return n.code===0&&n.stdout&&n.stdout!=="HEAD"?n.stdout:"main"}async function fr(e,t){await oe(e,["fetch","origin",t]);let n=`origin/${t}`;return(await oe(e,["rev-parse","--verify","--quiet",`refs/remotes/${n}`])).code===0?n:t}async function Us(e,t,n){return(await oe(e,["merge-base","--is-ancestor",t,n])).code===0}async function qs(e){if(Tn(e.worktreePath)){let s=await oe(e.worktreePath,["rev-parse","--is-inside-work-tree"]);if(s.code===0&&s.stdout==="true")return{ok:!0,worktreePath:e.worktreePath}}let n=(await oe(e.repoPath,["rev-parse","--verify","--quiet",`refs/heads/${e.branch}`])).code===0?["worktree","add",e.worktreePath,e.branch]:["worktree","add","-b",e.branch,e.worktreePath,e.startPoint??e.base],r=await oe(e.repoPath,n);return r.code!==0?{ok:!1,error:r.stderr||r.stdout||"git worktree add failed"}:{ok:!0,worktreePath:e.worktreePath}}async function Fs(e,t,n){let r=await fr(e,n),s=await oe(e,["rev-list","--left-right","--count",`${r}...${t}`]);if(s.code!==0)return{behind:0,ahead:0};let[o,i]=s.stdout.split(/\s+/).map(c=>Number(c)||0);return{behind:o??0,ahead:i??0}}async function Ws(e,t){await oe(e,["branch","-D",t])}async function _n(e){let t=Rn(pr,e);if(!Tn(t))return;let{readdir:n}=await import("node:fs/promises"),r=await n(t,{withFileTypes:!0}).catch(()=>[]);for(let s of r){if(!s.isDirectory())continue;let o=Rn(t,s.name),i=await oe(o,["rev-parse","--path-format=absolute","--git-common-dir"]);if(i.code===0&&i.stdout){let c=Ea(i.stdout);await oe(c,["worktree","remove","--force",o])}}await Aa(t,{recursive:!0,force:!0}).catch(()=>{})}function Ca(e){let t=e.trim().replace(/\.git$/,""),n=/^git@([^:]+):(.+)$/.exec(t);return n?`https://${n[1]}/${n[2]}`:(n=/^ssh:\/\/git@([^/]+)\/(.+)$/.exec(t),n?`https://${n[1]}/${n[2]}`:/^https?:\/\//.test(t)?t:null)}async function Ks(e,t,n){let r=await oe(e,["remote","get-url","origin"]);if(r.code!==0||!r.stdout)return null;let s=Ca(r.stdout);return s?`${s}/compare/${t}...${n}?expand=1`:null}import{spawn as $a,spawnSync as La}from"node:child_process";import mr from"node:fs";import gr from"node:os";import wt from"node:path";import{fileURLToPath as Ga}from"node:url";import{execFileSync as Pa}from"node:child_process";import{realpathSync as Oa}from"node:fs";function Da(){if(process.platform!=="darwin")return!1;try{return Pa("which",["sandbox-exec"],{stdio:"ignore"}),!0}catch{return!1}}function ja(e){try{return Oa(e)}catch{return e}}function Ma(e){return e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}function Ba(e){let t=["(version 1)","(allow default)"];for(let n of e)t.push(`(deny file-write* (subpath "${Ma(ja(n))}"))`);return t.push('(allow file-write* (regex #"/\\.git/"))'),t.push('(allow file-write* (regex #"/\\.claude/"))'),t.join(`
|
|
12
|
+
`)}function Hs(e){let{cmd:t,args:n,repoPaths:r,enabled:s}=e;return!s||!r.length||!Da()?{cmd:t,args:n,sandboxed:!1}:{cmd:"sandbox-exec",args:["-p",Ba(r),t,...n],sandboxed:!0}}function Vt(e){if(!e||!e.trim())return process.cwd();let t=e.trim();return t==="~"?t=gr.homedir():(t.startsWith("~/")||t.startsWith("~\\"))&&(t=wt.join(gr.homedir(),t.slice(2))),wt.resolve(t)}function Ua(){return`http://127.0.0.1:${Number(process.env.PIPELINES_PORT||process.env.PIPELINES_MOCK_PORT||63210)}`}function qa(){let e=process.env.SUPERCLAWD_BIN;if(e)return{cmd:e,baseArgs:[]};let t=wt.dirname(Ga(import.meta.url));for(let n=0;n<8;n++){let r=wt.join(t,"apps","cli","dist","index.js");if(mr.existsSync(r))return{cmd:process.execPath,baseArgs:[r]};let s=wt.dirname(t);if(s===t)break;t=s}return{cmd:"superclawd",baseArgs:[]}}function hr(e){let{cmd:t,baseArgs:n}=qa(),r=[...n,"-p",e.prompt,"--pipelines"];e.pipelinesTools&&r.push(`--pipelines-tools=${e.pipelinesToolsMode??"board"}`),e.workspace&&r.push("--workspace",e.workspace),e.model&&r.push("--model",e.model),e.effort&&r.push("--effort",e.effort),e.noCapture&&r.push("--no-capture"),e.resume?r.push("--resume",e.resume):e.sessionId&&r.push("--session-id",e.sessionId);let s={};e.pipelinesTools&&(s.SUPERCLAWD_PIPELINES_URL=Ua(),e.ticketId&&(s.SUPERCLAWD_PIPELINES_TICKET=e.ticketId));let o=Vt(e.workingDir);if(!mr.existsSync(o))return{pid:0,pgid:0,done:Promise.resolve({code:1,stdout:"",stderr:`working directory does not exist: ${o}${e.workingDir&&e.workingDir!==o?` (from "${e.workingDir}")`:""}`})};let i=Hs({cmd:t,args:r,repoPaths:e.sandboxRepos??[],enabled:!!e.sandbox});if(process.env.PIPELINES_DEBUG){let I=`[pipelines] spawn (cwd=${o}${i.sandboxed?", sandboxed":""}): ${i.cmd} ${i.args.join(" ")}`;console.log(I);try{mr.appendFileSync(wt.join(gr.homedir(),".superclawd","pipelines-debug.log"),`${new Date().toISOString()} ${I}
|
|
13
|
+
`)}catch{}}let c=$a(i.cmd,i.args,{cwd:o,detached:!0,stdio:["ignore","pipe","pipe"],env:{...process.env,...s}}),d="",u="";c.stdout?.on("data",I=>d+=String(I)),c.stderr?.on("data",I=>u+=String(I));let p=new Promise(I=>{c.on("error",f=>I({code:127,stdout:d,stderr:`${u}
|
|
14
|
+
${String(f)}`})),c.on("close",f=>I({code:f??137,stdout:d,stderr:u}))}),g=c.pid??0;return{pid:g,pgid:g,done:p}}function Qs(e,t="SIGTERM"){if(e){if(process.platform==="win32"){try{La("taskkill",["/pid",String(e),"/T","/F"],{stdio:"ignore"})}catch{}return}try{process.kill(-e,t)}catch{try{process.kill(e,t)}catch{}}}}function Ye(e){return hr(e).done}var Fa=["haiku","sonnet","opus","fable"],Wa=["low","medium","high","xhigh","max"],ke="opus",ye="high",Re=(e,t=ke)=>{let n=String(e??"").trim();return Fa.includes(n)?n:t},Te=(e,t=ye)=>{let n=String(e??"").trim();return Wa.includes(n)?n:t};function Ka(){let e=new Date;return`${String(e.getHours()).padStart(2,"0")}:${String(e.getMinutes()).padStart(2,"0")}`}function Ue(e,t,n){let r={id:`e${e.events.length+1}`,ts:Ka(),kind:t,text:n};return e.events.push(r),r}Z();import{randomUUID as zs}from"node:crypto";var Ha=1e4,Js=3,Qa=1e3,Ja=new Set(["host","content-length","content-type","user-agent","x-superclawd-event","x-superclawd-signature","transfer-encoding","connection"]),za=e=>new Promise(t=>setTimeout(t,e));function Xs(e,t){let n={"content-type":"application/json","user-agent":"SuperClawd-Pipelines-Webhook/1","x-superclawd-event":e};l.webhookSecret&&(n["x-superclawd-signature"]=`sha256=${ps(l.webhookSecret,t)}`);for(let r of l.webhookHeaders??[])r.key&&!Ja.has(r.key.toLowerCase())&&(n[r.key]=r.value);return n}async function Ys(e,t,n){let r=new AbortController,s=setTimeout(()=>r.abort(),Ha);try{return(await fetch(e,{method:"POST",headers:n,body:t,signal:r.signal})).status}catch{return 0}finally{clearTimeout(s)}}function le(e,t,n={}){let r=l;if(!r.webhookEnabled||!r.webhookUrl||!r.webhookEvents||!r.webhookEvents.includes(e))return;let s=r.webhookUrl,o={id:`evt_${zs()}`,event:e,occurredAt:new Date().toISOString(),workspaceId:n.workspaceId,data:t},i=JSON.stringify(o),c=Xs(e,i);(async()=>{for(let d=1;d<=Js;d++){let u=await Ys(s,i,c);if(u>=200&&u<300)return;d<Js&&await za(Qa*d)}})().catch(()=>{})}async function Vs(){let e=l.webhookUrl;if(!e)return{ok:!1,error:"no webhook URL configured"};let t={id:`evt_${zs()}`,event:"webhook.test",occurredAt:new Date().toISOString(),data:{message:"SuperClawd Pipelines test event"}},n=JSON.stringify(t),r=Xs("webhook.test",n),s=await Ys(e,n,r);return s>=200&&s<300?{ok:!0,status:s}:{ok:!1,...s?{status:s}:{error:"could not reach the webhook URL"}}}function fe(e,t,n={}){return e.status=t,n.stopReason!==void 0&&(e.stopReason=n.stopReason===null?void 0:n.stopReason),n.clearBlocker&&(e.blocker=void 0),n.event&&Ue(e,n.event.kind,n.event.text),e.updatedAt=new Date().toISOString(),n.webhookEvent&&le(n.webhookEvent,{runId:e.id,ticketId:e.ticketId,ticketKey:e.ticketKey,title:e.title,status:e.status,stopReason:e.stopReason},{workspaceId:e.workspaceId}),e}import{randomUUID as Xa}from"node:crypto";var Ya=3,Va=1,Za=10,bt=new Map,It=[];function ec(){let e=l.maxConcurrentRuns??Ya;return Math.max(Va,Math.min(Za,e))}function _e(e,t=!1,n){bt.has(e)||It.some(r=>r.runId===e)||(It.push({runId:e,resume:t,prompt:n}),Zs())}function Zs(){for(;bt.size<ec()&&It.length>0;){let e=It.shift();rc(e.runId,e.resume,e.prompt)}}function tc(e,t,n){let r=e?`You are working on SuperClawd Pipelines ticket ${e.externalIdentifier} \u2014 "${e.title}" (ticket id: ${e.id}).`:`You are working on SuperClawd Pipelines run "${t.title}".`,o=!!e&&e.provider!=="local"?"- Start by calling read_ticket to read the full ticket (description, vendor comments, context, plan, open questions, conclusion, notes). On this run you can call it with no arguments \u2014 it defaults to this ticket. Read the vendor comments too \u2014 the real acceptance criteria and decisions often live in the discussion, not the description.":"- Start by calling read_ticket to read the full ticket (description, context, plan, open questions, conclusion, notes). On this run you can call it with no arguments \u2014 it defaults to this ticket.",i=[r,"","Use the SuperClawd Pipelines tools for this task:",o,"- Write your plan with set_plan (use the tool; do not create a plan file unless the ticket explicitly asks for one).",`- If this task changes code: call list_repos to see the git repositories in your working directory. It returns either several repos (the working dir is an umbrella folder) OR a single entry named "." (the working dir is itself one git repo) \u2014 in BOTH cases you must call start_repo_work({repo}) to get an isolated worktree before touching any code (for the single "." repo you can call start_repo_work with no repo). Make ALL changes inside the returned worktreePath and commit there. NEVER edit files in the working directory checkout directly \u2014 not even when it's a single repo and not even if it looks unmanaged; other runs may be working on it, and in-place edits break isolation. Claim each repo you touch. When done, commit and push the branch, then open the pull request following THIS repo's own PR conventions \u2014 if a create-pull-request skill or a PR rule in CLAUDE.md is available, use it to author the PR rather than hand-crafting one. Then record the resulting PR link with add_pr_link (once per repo). If you can't open a PR (e.g. no gh), push the branch and pass the compareUrl to add_pr_link so a human can open it.`,"- If you need something that isn't in the ticket, record it with add_open_question rather than guessing \u2014 and if it blocks progress, also call block_ticket with a reason.","- Keep durable findings or decisions on the ticket with add_note.","- When you've fully completed what the ticket asked, write a short wrap-up of what you did (and any results) with set_conclusion."];if(t.autonomy==="autonomous"&&i.push("","This run is AUTONOMOUS \u2014 no one is available to answer questions while it runs. Do NOT guess on any decision that materially changes the outcome. When you hit a genuine unknown, record it with add_open_question AND call block_ticket with a short reason, then stop \u2014 a human will resolve it and the ticket will be re-run. Only make assumptions for trivial, low-risk gaps, and state them clearly."),t.assignmentKind==="agent"&&t.agentName)i.push("",`DELEGATION \u2014 this ticket is assigned to the "${t.agentName}" agent. Use the Task tool to hand the ENTIRE ticket to the subagent for "${t.agentName}" (available in this session), let it complete the work, then report its result. Do NOT do the work yourself. If you cannot find that subagent, proceed yourself and add_note that the assigned agent was unavailable.`);else if(t.assignmentKind==="team"&&t.teamName){let u=t.teamLeadName;i.push("",u?`DELEGATION \u2014 this ticket is assigned to the "${t.teamName}" team. Use the Task tool to hand the ENTIRE ticket to the team's LEAD subagent, "${u}" (available in this session); it will coordinate its members. Do NOT do the work yourself. If you cannot find it, proceed yourself and add_note that the assigned team was unavailable.`:`DELEGATION \u2014 this ticket is assigned to the "${t.teamName}" team. Use the Task tool to hand the ENTIRE ticket to that team's lead-agent subagent, which coordinates its members. Do NOT do the work yourself. If you cannot find it, proceed yourself and add_note that the assigned team was unavailable.`)}i.push("","Then do the work the ticket asks for.");let c=i.join(`
|
|
15
15
|
`),d=(n??"").trim();return d?`${c}
|
|
16
16
|
|
|
17
17
|
Additional instructions from the human:
|
|
18
|
-
${d}`:c}async function
|
|
19
|
-
`)}async function
|
|
18
|
+
${d}`:c}async function nc(e){let t=e.worktrees??[];if(!t.length)return"";let n=[],r=!1;for(let o of t){let{behind:i}=await Fs(o.worktreePath,o.branch,o.base);i>0&&(r=!0),n.push(`- repo "${o.repo}": worktree at ${o.worktreePath} (branch ${o.branch}`+(i>0?`, ${i} commit(s) behind ${o.base})`:")"))}let s=r?"One or more branches are behind their base branch. BEFORE making changes, update each stale branch: in its worktree run `git rebase origin/<base>`, resolve any conflicts, and then push with `--force-with-lease` to update the PR. If a rebase conflict is beyond what you can safely resolve, add_note explaining it and stop.":"";return["You are continuing earlier work on this ticket. You already created isolated git worktree(s) for it:",...n,"Make ALL changes inside the worktree path(s) above and commit/push there on the SAME branch \u2014 do NOT create a new worktree or branch, and do NOT edit the repo in the working directory. Update the existing PR rather than opening a new one. Only call start_repo_work if you need a repo not listed above.",...s?[s]:[],"",""].join(`
|
|
19
|
+
`)}async function rc(e,t,n){let r=x[e];if(!r)return;let s=h.find(f=>f.id===r.ticketId),o=n??(s?`${s.title}
|
|
20
20
|
|
|
21
|
-
${s.description??""}`.trim():r.title),i=t?await
|
|
21
|
+
${s.description??""}`.trim():r.title),i=t?await nc(r)+o:tc(s,r,n),c=s?.workingDir??l.workspaceDirs?.[r.workspaceId]??"~",d=s?.model||l.defaultModel||ke,u=s?.effort||l.defaultEffort||ye,p=l.sandboxWorktreeEdits?(await Ge(Vt(c))).map(f=>f.path):void 0,g=hr({prompt:i,workspace:r.workspaceId,workingDir:c,model:d,effort:u,sessionId:t?null:r.sessionId,resume:t?r.sessionId??null:null,pipelinesTools:!0,ticketId:r.ticketId,sandbox:l.sandboxWorktreeEdits,sandboxRepos:p});bt.set(e,g),Ue(r,"state_change",t?"re-engaged (resume)":"agent started"),r.updatedAt=new Date().toISOString(),await U(e);let I=await g.done;bt.delete(e),await sc(e,I.code,I.stdout,I.stderr,i),Zs()}var kr=null;function eo(e){kr=e}async function sc(e,t,n,r,s){let o=x[e];if(!o||o.status!=="running")return;let i;if(t===0)i=((o.sessionId?In(o.sessionId):null)??n).trim();else{let d=(r||n).trim().slice(0,5e4);i=`\u26A0\uFE0F The run failed (exit code ${t}).${d?`
|
|
22
22
|
|
|
23
|
-
${d}`:""}`}if(i&&(o.resultText=i.slice(0,5e4)),s||i){let d=h.find(p=>p.id===o.ticketId),u={id:`rr_${
|
|
23
|
+
${d}`:""}`}if(i&&(o.resultText=i.slice(0,5e4)),s||i){let d=h.find(p=>p.id===o.ticketId),u={id:`rr_${Xa()}`,ticketId:o.ticketId,runId:e,sessionId:o.sessionId,prompt:(s??"").slice(0,5e4),response:i.slice(0,5e4),status:t===0?"completed":"error",model:d?.model||l.defaultModel||ke,effort:d?.effort||l.defaultEffort||ye,createdAt:new Date().toISOString()};X.unshift(u),await hn(u)}let c=!!h.find(d=>d.id===o.ticketId)?.blockedReason;if(t===0){let d=n.trim().slice(0,4e3);if(d&&Ue(o,"state_change",d),Ue(o,"mcp_tool","agent finished its turn"),fe(o,"stopped",{stopReason:"completed",clearBlocker:!0,webhookEvent:"run.completed"}),!c){S[o.ticketId]="in_review";let u=h.find(p=>p.id===o.ticketId);u&&l.prMonitorDefault&&u.prLinks?.length&&!u.prMonitor&&(u.prMonitor=!0,u.prMonitorCount=0,u.prMonitorLastAt=void 0,u.prMonitorNote=void 0)}}else{let d=(r||n).trim().slice(0,500);Ue(o,"error",`agent exited with code ${t}${d?`: ${d}`:""}`),fe(o,"stopped",{stopReason:"error",webhookEvent:"run.failed"})}if(o.updatedAt=new Date().toISOString(),await U(e),await A(o.ticketId),kr){let d=t===0?c?"blocked":"in_review":"error";try{kr(o.ticketId,d)}catch(u){console.error("batch finalize hook failed",u)}}}async function St(e){let t=bt.get(e);t&&(Qs(t.pgid),bt.delete(e));let n=It.findIndex(r=>r.runId===e);n!==-1&&It.splice(n,1)}async function to(){let e=Object.values(x).filter(t=>t.status==="running");for(let t of e)Ue(t,"error","interrupted by daemon restart"),fe(t,"stopped",{stopReason:"error"}),await U(t.id);e.length&&console.log(`[pipelines] reconciled ${e.length} interrupted run(s)`)}Z();function xt(e,t,n){let r=[];for(let s of e.dependsOn??[]){let o=t(s);if(!o){r.push({id:s,reason:"missing"});continue}n(s)!=="done"&&r.push({id:s,identifier:o.externalIdentifier,title:o.title,reason:"not_done"})}return r}function no(e,t,n){let r=new Set,s=[...t];for(;s.length;){let o=s.pop();if(o===e)return!0;if(r.has(o))continue;r.add(o);let i=n(o);i?.dependsOn?.length&&s.push(...i.dependsOn)}return!1}function ro(e,t,n,r){if(!Array.isArray(n))return[];let s=[];for(let o of n){let i=String(o??"").trim();if(!i||i===e||s.includes(i))continue;let c=r(i);c&&c.connectionId===t&&s.push(i)}return s}import{randomUUID as oc}from"node:crypto";var pe=class extends Error{constructor(n,r,s,o){super(s);this.status=n;this.code=r;this.extra=o;this.name="RunError"}},ic=e=>S[e]??"backlog";function Nn(e){let t=x[e];if(!t)throw new pe(404,void 0,"run not found");return t}function J(e){let t=T[e];return t?x[t]:void 0}async function En(e){let t=h.find(d=>d.id===e.ticketId);if(!t)throw new pe(404,void 0,"ticket not found");let n=b.find(d=>d.id===t.connectionId);if(!n||n.linkStatus!=="linked"||!n.workspaceId)throw new pe(409,void 0,"ticket connection is not linked to a workspace");let r=`run_${G.runSeq++}`,s=E()?{teams:Se,agents:xe}:await yt(n.workspaceId)??{teams:[],agents:[]},o=s.teams.find(d=>d.id===e.teamId),i=s.agents.find(d=>d.id===e.agentId),c={id:r,ticketId:t.id,ticketKey:t.externalIdentifier,title:t.title,provider:t.provider,workspaceId:n.workspaceId,workspaceName:n.workspaceName,assignmentKind:e.assignmentKind,teamId:e.teamId,teamName:o?.name,teamLeadName:o?.leadAgentName??void 0,agentId:e.agentId,agentName:i?.name,autonomy:e.autonomy,status:"not_started",plan:[],events:[],createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()};return x[r]=c,T[t.id]=r,await U(r),c}async function vt(e,t={}){let n=J(e);return n||En({ticketId:e,assignmentKind:"anyone",autonomy:t.autonomy??l.defaultAutonomy})}async function qe(e,t={}){let n=Nn(e);if(n.status==="not_started"&&!t.skipDepGate){let i=h.find(d=>d.id===n.ticketId),c=i?xt(i,d=>h.find(u=>u.id===d),ic):[];if(c.length)throw new pe(409,"unmet_dependencies","blocked by dependencies",{unmet:c})}let r=(t.details??"").trim(),s=t.attachments??[],o=r+(s.length?`
|
|
24
24
|
|
|
25
25
|
Attached files (read these):
|
|
26
26
|
${s.map(i=>`- ${i}`).join(`
|
|
27
|
-
`)}`:"");return n.sessionId||(n.sessionId=sc()),fe(n,"running",{stopReason:null,event:{kind:"state_change",text:r?`started with details: ${r.slice(0,80)}`:"started"},webhookEvent:"run.started"}),I[n.ticketId]="in_progress",await U(n.id),await N(n.ticketId),C()||Re(n.id,!1,o||void 0),n}async function vt(e){let t=An(e);return fe(t,"paused",{event:{kind:"state_change",text:"paused by user"},webhookEvent:"run.paused"}),await St(t.id),await U(t.id),t}async function Rt(e){let t=An(e);return fe(t,"running",{event:{kind:"state_change",text:"resumed (continue)"},webhookEvent:"run.resumed"}),I[t.ticketId]="in_progress",await U(t.id),await N(t.ticketId),C()||Re(t.id,!0),t}async function Fe(e){let t=An(e);return fe(t,"stopped",{stopReason:"user",clearBlocker:!0,event:{kind:"state_change",text:"stopped by user"},webhookEvent:"run.stopped"}),await St(t.id),await U(t.id),t}V();import{randomUUID as so}from"node:crypto";var P=class extends Error{constructor(n,r,s){super(r);this.status=n;this.extra=s;this.name="TicketError"}},ic=["backlog","in_progress","needs_input","in_review","done"],en=e=>I[e]??"backlog",Sr=e=>b.find(t=>t.id===e.connectionId)?.workspaceId??void 0;function Tt(e){let t=h.find(n=>n.id===e);if(!t)throw new P(404,"ticket not found");return t}function Nn(e){return h.find(t=>t.id===e)}function En(e){let t=(e.dependsOn??[]).map(n=>{let r=h.find(s=>s.id===n);return{id:n,identifier:r?.externalIdentifier??null,title:r?.title??null,column:r?en(n):null,done:!!r&&en(n)==="done",missing:!r}});return{dependencies:t,blockedByDeps:t.some(n=>!n.done)}}async function Cn(e,t){let n=Tt(e),r=o=>h.find(i=>i.id===o),s=ro(n.id,n.connectionId,t,r);if(no(n.id,s,r))throw new P(409,"that would create a dependency cycle",{code:"dependency_cycle"});return n.dependsOn=s.length?s:void 0,await N(n.id),{dependsOn:s,unmet:It(n,r,en)}}function _t(e){return j.filter(t=>t.ticketId===e).slice().reverse()}function At(e){return D.filter(t=>t.ticketId===e)}async function Nt(e,t){let n=Tt(e);if(!ic.includes(t))throw new P(400,"invalid column");if(n.blockedReason&&t!==en(n.id))throw new P(409,"a blocked ticket can't change lanes \u2014 unblock it first");if(t==="done"){let s=T[n.id],o=s?x[s]:void 0;if(!o||o.status!=="stopped")throw new P(409,"a task can only enter Done once its session is stopped")}let r=en(n.id);return I[n.id]=t,await N(n.id),t!==r&&le("ticket.moved",{ticketId:n.id,ticketKey:n.externalIdentifier,from:r,to:t},{workspaceId:Sr(n)}),{ticketId:n.id,column:t}}async function Te(e,t){let n=Tt(e),r=[],s=null;if(typeof t.workingDir=="string"&&(n.workingDir=t.workingDir.trim()||void 0),t.model!==void 0&&(n.model=xe(t.model,n.model||ke)),t.effort!==void 0&&(n.effort=ve(t.effort,n.effort||ye)),"blockedReason"in t){let i=String(t.blockedReason??"").trim().slice(0,2e3),c=!!n.blockedReason,d=!!i;if(c&&!d){let u=D.filter(p=>p.ticketId===n.id&&!p.resolved).length;if(u>0)throw new P(409,"resolve open questions before unblocking",{unresolved:u})}n.blockedReason=i||void 0,d&&!c?(I[n.id]="needs_input",s={event:"ticket.blocked"}):!d&&c&&(I[n.id]="backlog",s={event:"ticket.unblocked"})}for(let i of["additionalContext","plan","conclusion"])if(i in t){let d=String(t[i]??"").trim().slice(0,2e4)||void 0;d!==n[i]&&(n[i]=d,n[`${i}UpdatedAt`]=d?new Date().toISOString():void 0,r.push(i))}if(n.provider==="local"){if(typeof t.title=="string"&&t.title.trim()&&t.title.trim()!==n.title&&(n.title=t.title.trim(),r.push("title")),typeof t.description=="string"){let c=t.description.trim()||void 0;c!==n.description&&(n.description=c,r.push("description"))}let i=Number(t.priority);[0,1,2,3,4].includes(i)&&i!==n.priority&&(n.priority=i,r.push("priority")),n.updatedAt=new Date().toISOString()}await N(n.id);let o=Sr(n);return s&&le(s.event,{ticketId:n.id,ticketKey:n.externalIdentifier,...n.blockedReason?{reason:n.blockedReason}:{}},{workspaceId:o}),r.length&&le("ticket.updated",{ticketId:n.id,ticketKey:n.externalIdentifier,fields:r},{workspaceId:o}),n}async function Et(e,t={}){let n=Tt(e);if(!n.blockedReason)throw new P(409,"ticket is not blocked");let r=D.filter(d=>d.ticketId===n.id&&!d.resolved).length;if(r>0)throw new P(409,"resolve open questions before unblocking",{unresolved:r});let s=T[n.id],o=s?x[s]:void 0,i=t.resume===!0&&!!o?.sessionId;n.blockedReason=void 0;let c=Sr(n);return le("ticket.unblocked",{ticketId:n.id,ticketKey:n.externalIdentifier},{workspaceId:c}),i&&o?(fe(o,"running",{stopReason:null,clearBlocker:!0,event:{kind:"state_change",text:"unblocked & resumed"},webhookEvent:"run.resumed"}),I[n.id]="in_progress",await U(o.id),await N(n.id),C()||Re(o.id,!0,"The block on this ticket has been resolved. Re-read the ticket with read_ticket (its open questions are now answered) and continue the work."),{ticket:n,run:o,resumed:!0}):(I[n.id]="backlog",await N(n.id),{ticket:n,resumed:!1})}async function Ct(e,t){let n=b.find(u=>u.id===e);if(!n)throw new P(404,"board not found");if(n.provider!=="local")throw new P(400,"tickets can only be created on the Local board");let r=String(t.title??"").trim();if(!r)throw new P(400,"title is required");let s=Number(t.priority),o=[0,1,2,3,4].includes(s)?s:0,i=++G.localSeq,c=new Date().toISOString(),d={id:`t_local${i}`,connectionId:n.id,provider:"local",externalIdentifier:`LOCAL-${i}`,project:"Local",title:r,status:"todo",statusRaw:"Backlog",priority:o,labels:[],url:"",workspaceId:n.workspaceId,workingDir:String(t.workingDir??"").trim()||(n.workspaceId?l.workspaceDirs?.[n.workspaceId]??"~":"~"),model:xe(t.model??l.defaultModel),effort:ve(t.effort??l.defaultEffort),description:String(t.description??"").trim()||void 0,createdAt:c,updatedAt:c};return h.push(d),I[d.id]="backlog",n.ticketCount+=1,await N(d.id),await A(n),d}async function Pt(e,t){Tt(e);let n=String(t.body??"").trim();if(!n)throw new P(400,"empty note");let r=t.author==="assistant"?"assistant":"user",s=new Date().toISOString(),o={id:`note_${so()}`,ticketId:e,author:r,body:n.slice(0,5e3),createdAt:s,updatedAt:s};return j.push(o),await ht(o),o}async function Ot(e,t){let n=j.find(s=>s.id===e);if(!n)throw new P(404,"note not found");let r=String(t??"").trim();if(!r)throw new P(400,"empty note");return n.body=r.slice(0,5e3),n.updatedAt=new Date().toISOString(),await ht(n),n}async function Dt(e){let t=j.findIndex(n=>n.id===e);if(t===-1)throw new P(404,"note not found");j.splice(t,1),await hn(e)}async function jt(e,t){Tt(e);let n=String(t??"").trim();if(!n)throw new P(400,"empty question");let r=new Date().toISOString(),s=D.filter(i=>i.ticketId===e).length,o={id:`oq_${so()}`,ticketId:e,question:n.slice(0,5e3),answer:void 0,resolved:!1,position:s,createdAt:r,updatedAt:r};return D.push(o),await or(o.id),o}async function Mt(e,t){let n=D.find(r=>r.id===e);if(n===void 0)throw new P(404,"question not found");return typeof t.question=="string"&&t.question.trim()&&(n.question=t.question.trim().slice(0,5e3)),"answer"in t&&(n.answer=String(t.answer??"").trim().slice(0,2e4)||void 0),typeof t.resolved=="boolean"&&(n.resolved=t.resolved),n.updatedAt=new Date().toISOString(),await or(n.id),n}async function $t(e){let t=D.findIndex(n=>n.id===e);if(t===-1)throw new P(404,"question not found");D.splice(t,1),await yn(e),await Rs()}V();var q=class extends Error{constructor(n,r,s,o){super(s);this.status=n;this.code=r;this.extra=o;this.name="BatchError"}};function Bt(){return K.map(e=>L[e]).filter(Boolean)}function Ve(e){let t=L[e];if(!t)throw new q(404,"not_found","batch not found");return t}async function Ze(e){let t=(e.name??"").trim();if(!t)throw new q(400,"invalid","batch name is required");let n=[...new Set(e.ticketIds??[])];if(n.length===0)throw new q(400,"invalid","a batch needs at least one ticket");let r=new Set(["queued","running","paused"]);for(let S of K){let f=L[S];if(!f||!r.has(f.status))continue;let w=n.find(Y=>f.ticketIds.includes(Y));if(w){let Y=h.find(He=>He.id===w);throw new q(409,"in_batch",`ticket ${Y?.externalIdentifier??w} is already in the active batch "${f.name}"`)}}let s=new Set(n),o,i,c=!1;for(let S of n){let f=h.find(Y=>Y.id===S);if(!f)throw new q(404,"not_found",`ticket ${S} not found`);if(!J(S))throw new q(409,"not_assigned",`ticket ${f.externalIdentifier} isn't assigned \u2014 assign it before adding it to a batch`);let w=b.find(Y=>Y.id===f.connectionId);if(!w||w.linkStatus!=="linked"||!w.workspaceId)throw new q(409,"not_linked",`ticket ${f.externalIdentifier}'s connection isn't linked to a workspace`);if(o&&w.workspaceId!==o)throw new q(409,"mixed_workspaces","all tickets in a batch must belong to the same workspace");o=w.workspaceId,i=w.workspaceName??o,(f.dependsOn??[]).some(Y=>s.has(Y))&&(c=!0)}let d=c?"ordered":"unordered",u=new Date().toISOString(),p=`batch_${++G.batchSeq}`,g={id:p,name:t,workspaceId:o,workspaceName:i,ticketIds:n,mode:d,advanceMode:e.advanceMode??"on_success",stackDependentWork:e.stackDependentWork??d==="ordered",status:"idle",budgetCap:e.budgetCap??null,pausedReason:null,runsStarted:0,createdAt:u,updatedAt:u};return L[p]=g,K.unshift(p),await ue(p),g}async function oo(e,t){let n=Ve(e);if(n.status!=="idle"&&n.status!=="done")throw new q(409,"active","stop the batch before editing it");if(t.name!==void 0){let r=t.name.trim();if(!r)throw new q(400,"invalid","batch name is required");n.name=r}if(t.ticketIds!==void 0){let r=await Ze({name:n.name,ticketIds:t.ticketIds,advanceMode:t.advanceMode??n.advanceMode,stackDependentWork:t.stackDependentWork??n.stackDependentWork,budgetCap:t.budgetCap??n.budgetCap});n.ticketIds=r.ticketIds,n.mode=r.mode,n.advanceMode=r.advanceMode,n.stackDependentWork=r.stackDependentWork,n.budgetCap=r.budgetCap,delete L[r.id],K.splice(K.indexOf(r.id),1),await zt(r.id)}else t.advanceMode!==void 0&&(n.advanceMode=t.advanceMode),t.stackDependentWork!==void 0&&(n.stackDependentWork=t.stackDependentWork),t.budgetCap!==void 0&&(n.budgetCap=t.budgetCap);return n.updatedAt=new Date().toISOString(),await ue(e),n}async function Pn(e){if(Ve(e).status==="running")throw new q(409,"running","stop the batch before deleting it");delete L[e];let n=K.indexOf(e);n!==-1&&K.splice(n,1),await zt(e)}V();var vr=e=>I[e];function _e(e,t,n={}){le(e,{batch:{id:t.id,name:t.name,status:t.status,mode:t.mode,ticketCount:t.ticketIds.length},...n},{workspaceId:t.workspaceId})}function We(e,t,n){e.status=t,e.pausedReason=n,e.updatedAt=new Date().toISOString(),ue(e.id)}function Rr(e,t){let n=vr(e);return n==="done"?!0:t.ticketIds.includes(e)&&t.advanceMode==="on_success"&&n==="in_review"}function ac(e){return e.ticketIds.filter(t=>{let n=J(t);if(!n||n.status!=="not_started")return!1;let r=h.find(s=>s.id===t);return r?(r.dependsOn??[]).every(s=>Rr(s,e)):!1})}function io(e){return e.ticketIds.filter(t=>J(t)?.status==="running")}function cc(e){(async()=>{try{let t=await xt(e,{autonomy:"autonomous"});if(t.status!=="not_started")return;t.autonomy="autonomous",await qe(t.id,{skipDepGate:!0})}catch(t){console.error(`batch: failed to start ticket ${e}`,t)}})()}function ao(e,t){for(let n of K){let r=L[n];if(r?.ticketIds.includes(e)&&(!t||t(r)))return r}}function dc(e){return ao(e,t=>t.status==="running")}function co(){return K.map(e=>L[e]).find(e=>e?.status==="running")}function Ke(){if(co())return;let t=K.map(n=>L[n]).filter(n=>!!n&&n.status==="queued").sort((n,r)=>n.updatedAt.localeCompare(r.updatedAt))[0];t&&(t.runsStarted=0,We(t,"running",null),_e("batch.started",t),On(t))}async function uo(e,t,n,r){let s=ao(e,d=>d.stackDependentWork&&d.mode==="ordered");if(!s)return{};let o=h.find(d=>d.id===e);if(!o)return{};let i=[];for(let d of o.dependsOn??[]){if(!s.ticketIds.includes(d))continue;let u=J(d)?.worktrees?.find(p=>p.repo===t);u&&vr(d)!=="done"&&(h.find(p=>p.id===d)?.prMerged||await Us(n,u.branch,r)||i.push(u.branch))}let c=[...new Set(i)];return c.length>1?{ambiguous:c}:c.length===1?{startPoint:c[0]}:{}}function On(e){if(e.status!=="running")return;let t=ac(e),n=io(e);if(t.length===0&&n.length===0){let r=e.ticketIds.filter(s=>J(s)?.status==="not_started");if(r.length>0){let s=u=>h.find(p=>p.id===u)?.externalIdentifier??u,o=r[0],i=(h.find(u=>u.id===o)?.dependsOn??[]).filter(u=>!Rr(u,e)).map(s),c=r.length>1?` (+${r.length-1} more)`:"",d=i.length?`${s(o)} can't start \u2014 waiting on ${i.join(", ")} (not satisfied in this batch)${c}. Resolve (approve / add to the batch) and Resume.`:`${s(o)} can't start${c}.`;We(e,"paused",d),_e("batch.paused",e,{reason:d,stuck:r}),Ke();return}We(e,"done",null),_e("batch.completed",e),Ke();return}for(let r of t){if(e.budgetCap!=null&&e.runsStarted>=e.budgetCap){We(e,"paused",`hit the budget cap of ${e.budgetCap} runs`),_e("batch.paused",e,{reason:e.pausedReason}),Ke();return}e.runsStarted++,cc(r)}ue(e.id)}function uc(e,t){let n=dc(e);if(!n)return;let s=h.find(o=>o.id===e)?.externalIdentifier??e;t==="blocked"?(We(n,"paused",`${s} blocked \u2014 needs input`),_e("batch.ticket_blocked",n,{ticket:{id:e,key:s}}),_e("batch.paused",n,{reason:n.pausedReason}),Ke()):t==="error"?(We(n,"paused",`${s} failed`),_e("batch.paused",n,{reason:n.pausedReason}),Ke()):On(n)}function Lt(e){let t=Ve(e),r=t.ticketIds.filter(s=>h.some(o=>o.id===s)).map(s=>{let o=h.find(p=>p.id===s),i=J(s),c=vr(s)??"backlog",d=(o?.dependsOn??[]).filter(p=>!Rr(p,t)),u;return c==="done"?u="done":c==="in_review"?u="in_review":i?.status==="running"?u="running":o?.blockedReason?u="blocked":i?.status==="stopped"&&i.stopReason==="error"?u="failed":d.length?u="waiting":u="queued",{ticketId:s,externalIdentifier:o?.externalIdentifier??s,title:o?.title??s,state:u,column:c,unmetDeps:d}});return{batch:t,tickets:r}}async function Gt(e){let t=Ve(e);if(t.status==="running")throw new q(409,"running","batch is already running");let n=co();return n&&n.id!==e?(We(t,"queued",`waiting for "${n.name}" to finish`),t):(t.status!=="paused"&&(t.runsStarted=0),t.status="running",t.pausedReason=null,t.updatedAt=new Date().toISOString(),await ue(e),_e("batch.started",t),On(t),t)}async function Ut(e){let t=Ve(e);if(t.status!=="running")throw new q(409,"not_running","batch isn't running");return t.status="paused",t.pausedReason="paused by you",t.updatedAt=new Date().toISOString(),await ue(e),_e("batch.paused",t,{reason:t.pausedReason}),Ke(),t}async function qt(e){let t=Ve(e),n=t.status==="running";for(let r of io(t)){let s=J(r);s&&await Fe(s.id).catch(()=>{})}return t.status="idle",t.pausedReason=null,t.updatedAt=new Date().toISOString(),await ue(e),n&&Ke(),t}function Tr(e){let t=new Set(e);return e.some(r=>(h.find(s=>s.id===r)?.dependsOn??[]).some(s=>t.has(s)))?"ordered":"unordered"}async function lo(e){delete L[e];let t=K.indexOf(e);t!==-1&&K.splice(t,1),await zt(e)}async function Dn(e){for(let t of[...K]){let n=L[t];if(!(!n||!n.ticketIds.includes(e))){if(n.ticketIds=n.ticketIds.filter(r=>r!==e),n.ticketIds.length===0){await lo(t);continue}n.mode=Tr(n.ticketIds),n.updatedAt=new Date().toISOString(),await ue(t),n.status==="running"&&On(n)}}}async function jn(e){for(let t of K){let n=L[t];if(!n||!n.ticketIds.includes(e))continue;let r=Tr(n.ticketIds);n.mode!==r&&(n.mode=r,n.updatedAt=new Date().toISOString(),await ue(t))}}function po(){for(let e of[...K]){let t=L[e];if(!t)continue;let n=t.ticketIds.filter(r=>h.some(s=>s.id===r));if(n.length===0){lo(e);continue}n.length!==t.ticketIds.length&&(t.ticketIds=n,t.mode=Tr(n),ue(e)),t.status==="running"&&We(t,"paused","interrupted by a daemon restart \u2014 resume to continue")}Ke()}eo(uc);V();V();import mo from"express";import pc from"cors";import{createRequire as fc}from"node:module";import{dirname as go,join as Ar}from"node:path";import{existsSync as mc}from"node:fs";var Mn=["ticket.moved","ticket.blocked","ticket.unblocked","ticket.updated","run.started","run.paused","run.resumed","run.completed","run.failed","run.stopped","schedule.run.started","schedule.run.completed","schedule.run.failed","batch.started","batch.completed","batch.paused","batch.ticket_blocked"];var Q={name:"id",in:"path",required:!0,schema:{type:"string"}},M=[{bearerAuth:[]}],me=(e,t=!0)=>({required:t,content:{"application/json":{schema:e}}}),F=(e,t="Success")=>({description:t,content:{"application/json":{schema:e}}}),v=e=>({$ref:`#/components/schemas/${e}`}),R=e=>({description:e,content:{"application/json":{schema:v("Error")}}});function lc(){let e={};for(let t of Mn)e[t]={post:{summary:`\`${t}\` event`,description:"Delivered to your configured webhook URL. Fixed headers (Content-Type, User-Agent, X-SuperClawd-Event) plus your custom headers; signed with HMAC-SHA256 in X-SuperClawd-Signature (sha256=<hex>) when a signing secret is set.",requestBody:me(v("WebhookEnvelope")),responses:{"2xx":{description:"Acknowledge with any 2xx; non-2xx is retried."}}}};return e}var fo={openapi:"3.1.0",info:{title:"SuperClawd Pipelines \u2014 Public API",version:"1.0.0",description:"Key-authenticated HTTP API for board, ticket, and run actions, plus outbound webhooks for ticket and run lifecycle events. Authenticate every request with `Authorization: Bearer <key>` (generate a key in Settings \u2192 API)."},servers:[{url:"/",description:"This Pipelines daemon (public API port)"}],security:M,tags:[{name:"Boards"},{name:"Tickets"},{name:"Notes"},{name:"Questions"},{name:"Runs"},{name:"Batches"}],paths:{"/v1/boards":{get:{tags:["Boards"],summary:"List boards (connections)",security:M,responses:{200:F({type:"array",items:v("Board")},"Boards"),401:R("Missing or invalid API key")}}},"/v1/boards/{id}/tickets":{get:{tags:["Boards"],summary:"List a board's tickets",security:M,parameters:[Q],responses:{200:F({type:"array",items:v("Ticket")},"Tickets"),401:R("Missing or invalid API key")}},post:{tags:["Boards"],summary:"Create a ticket on a Local board",description:"Local-board only (vendor boards import tickets, they don't author them). New tickets land in Backlog.",security:M,parameters:[Q],requestBody:me({type:"object",required:["title"],properties:{title:{type:"string"},description:{type:"string"},priority:{type:"integer",enum:[0,1,2,3,4],description:"0 none \xB7 1 urgent \xB7 2 high \xB7 3 medium \xB7 4 low"},workingDir:{type:"string"},model:{type:"string"},effort:{type:"string"}}}),responses:{201:F(v("Ticket"),"Created ticket"),400:R("Not a local board, or title missing"),404:R("Board not found")}}},"/v1/tickets/{id}":{get:{tags:["Tickets"],summary:"Get a ticket",security:M,parameters:[Q],responses:{200:F(v("Ticket"),"Ticket"),404:R("Ticket not found")}},patch:{tags:["Tickets"],summary:"Update ticket fields",description:"plan/conclusion/additionalContext/blockedReason/workingDir/model/effort apply to any ticket; title/description/priority to local-board tickets only. Setting blockedReason blocks (moves to the Blocked lane); clearing it unblocks (refused while open questions are unresolved).",security:M,parameters:[Q],requestBody:me(v("TicketUpdate")),responses:{200:F(v("Ticket"),"Updated ticket"),404:R("Ticket not found"),409:R("Unresolved open questions block unblocking")}}},"/v1/tickets/{id}/move":{post:{tags:["Tickets"],summary:"Move a ticket to a lane",description:"A blocked ticket can't change lanes until unblocked; Done is only allowed once the ticket's run is stopped.",security:M,parameters:[Q],requestBody:me(v("MoveRequest")),responses:{200:F(v("MoveResult"),"Moved"),400:R("Invalid column"),404:R("Ticket not found"),409:R("Blocked-lane freeze or Done gate")}}},"/v1/tickets/{id}/unblock":{post:{tags:["Tickets"],summary:"Unblock a ticket",description:"Refused while open questions are unresolved. With `resume: true` (and an existing run session) the run re-enters and the card goes to In Progress; otherwise it returns to Backlog.",security:M,parameters:[Q],requestBody:me(v("UnblockRequest"),!1),responses:{200:F(v("UnblockResult"),"Unblocked"),409:R("Not blocked, or unresolved open questions")}}},"/v1/tickets/{id}/notes":{get:{tags:["Notes"],summary:"List a ticket's notes (newest first)",security:M,parameters:[Q],responses:{200:F({type:"array",items:v("Note")},"Notes")}},post:{tags:["Notes"],summary:"Add a note",security:M,parameters:[Q],requestBody:me(v("NoteCreate")),responses:{201:F(v("Note"),"Created"),400:R("Empty note"),404:R("Ticket not found")}}},"/v1/notes/{id}":{patch:{tags:["Notes"],summary:"Edit a note",security:M,parameters:[Q],requestBody:me(v("NoteCreate")),responses:{200:F(v("Note"),"Updated"),404:R("Note not found")}},delete:{tags:["Notes"],summary:"Delete a note",security:M,parameters:[Q],responses:{200:F(v("Ok"),"Deleted"),404:R("Note not found")}}},"/v1/tickets/{id}/questions":{get:{tags:["Questions"],summary:"List a ticket's open questions",security:M,parameters:[Q],responses:{200:F({type:"array",items:v("Question")},"Questions")}},post:{tags:["Questions"],summary:"Add an open question",security:M,parameters:[Q],requestBody:me(v("QuestionCreate")),responses:{201:F(v("Question"),"Created"),400:R("Empty question"),404:R("Ticket not found")}}},"/v1/questions/{id}":{patch:{tags:["Questions"],summary:"Edit a question (text / answer / resolved)",security:M,parameters:[Q],requestBody:me(v("QuestionUpdate")),responses:{200:F(v("Question"),"Updated"),404:R("Question not found")}},delete:{tags:["Questions"],summary:"Delete a question",security:M,parameters:[Q],responses:{200:F(v("Ok"),"Deleted"),404:R("Question not found")}}},"/v1/tickets/{id}/run":{get:{tags:["Runs"],summary:"Get the ticket's run",security:M,parameters:[Q],responses:{200:F(v("Run"),"Run"),404:R("No run for this ticket")}}},"/v1/tickets/{id}/run/{action}":{post:{tags:["Runs"],summary:"Control the run (start | pause | resume | stop)",description:"start creates a run if none exists, then enqueues it. pause kills the process but keeps the session resumable; resume re-enters it; stop marks it stopped and kills it.",security:M,parameters:[Q,{name:"action",in:"path",required:!0,schema:{type:"string",enum:["start","pause","resume","stop"]}}],requestBody:me(v("RunStartRequest"),!1),responses:{200:F(v("Run"),"Run"),400:R("Unknown action"),404:R("No run for this ticket"),409:R("Unmet dependencies (on first start)")}}},"/v1/batches":{get:{tags:["Batches"],summary:"List batches",security:M,responses:{200:F({type:"array",items:v("Batch")},"Batches"),401:R("Missing or invalid API key")}},post:{tags:["Batches"],summary:"Create a batch",description:"A batch is a set of already-assigned tickets you press play on to run unattended. ordered/unordered is derived from the tickets' dependencies. Every ticket must be assigned and share one linked workspace, and can't already be in another active batch.",security:M,requestBody:me({type:"object",required:["name","ticketIds"],properties:{name:{type:"string"},ticketIds:{type:"array",items:{type:"string"}},advanceMode:{type:"string",enum:["on_success","approval"],description:"on_success: a member reaching In Review unblocks the next. approval: dependents wait for Done."},stackDependentWork:{type:"boolean",description:"Ordered batches only: a dependent member branches off its blocker's branch."},budgetCap:{type:["integer","null"],description:"Pause the batch after this many runs (null = no cap)."}}}),responses:{201:F(v("Batch"),"Created batch"),400:R("Invalid (name/tickets missing)"),409:R("A ticket isn't assigned, mixes workspaces, or is already in an active batch")}}},"/v1/batches/{id}":{get:{tags:["Batches"],summary:"Get a batch with its members' live state",security:M,parameters:[Q],responses:{200:F(v("BatchDetail"),"Batch detail"),404:R("Batch not found")}},delete:{tags:["Batches"],summary:"Delete a batch",description:"Removes the batch grouping only \u2014 the tickets and their runs are untouched. A running batch can't be deleted (stop it first).",security:M,parameters:[Q],responses:{200:F(v("Ok"),"Deleted"),404:R("Batch not found"),409:R("Batch is running")}}},"/v1/batches/{id}/{action}":{post:{tags:["Batches"],summary:"Control the batch (start | pause | stop)",description:"start plays or resumes it (queues behind any already-running batch). pause stops starting new members (in-flight runs finish). stop halts it and kills its running members.",security:M,parameters:[Q,{name:"action",in:"path",required:!0,schema:{type:"string",enum:["start","pause","stop"]}}],responses:{200:F(v("Batch"),"Batch"),400:R("Unknown action"),404:R("Batch not found"),409:R("Illegal transition (e.g. already running)")}}}},webhooks:lc(),components:{securitySchemes:{bearerAuth:{type:"http",scheme:"bearer",description:"The API key generated in Settings \u2192 API."}},schemas:{Error:{type:"object",properties:{error:{type:"string"},code:{type:"string"}},required:["error"]},Ok:{type:"object",properties:{ok:{type:"boolean"}}},Board:{type:"object",properties:{id:{type:"string"},provider:{type:"string"},workspaceId:{type:"string"},workspaceName:{type:"string"},linkStatus:{type:"string"},ticketCount:{type:"integer"}}},Ticket:{type:"object",description:"A board ticket (fields vary by provider).",properties:{id:{type:"string"},connectionId:{type:"string"},provider:{type:"string"},externalIdentifier:{type:"string"},title:{type:"string"},description:{type:"string"},column:{type:"string",enum:["backlog","in_progress","needs_input","in_review","done"]},blockedReason:{type:"string",nullable:!0},plan:{type:"string",nullable:!0},conclusion:{type:"string",nullable:!0},additionalContext:{type:"string",nullable:!0},comments:{type:"array",description:"Vendor comments/discussion synced read-only from the provider (oldest\u2192newest).",items:v("TicketComment")}}},TicketComment:{type:"object",description:"A ticket comment synced read-only from the vendor.",properties:{id:{type:"string"},author:{type:"string",nullable:!0},body:{type:"string"},createdAt:{type:"string",format:"date-time",nullable:!0},updatedAt:{type:"string",format:"date-time",nullable:!0}}},TicketUpdate:{type:"object",properties:{title:{type:"string"},description:{type:"string"},priority:{type:"integer",minimum:0,maximum:4},plan:{type:"string"},conclusion:{type:"string"},additionalContext:{type:"string"},blockedReason:{type:"string",nullable:!0,description:"Non-empty blocks; null/empty unblocks."},workingDir:{type:"string"},model:{type:"string"},effort:{type:"string"}}},MoveRequest:{type:"object",required:["column"],properties:{column:{type:"string",enum:["backlog","in_progress","needs_input","in_review","done"]}}},MoveResult:{type:"object",properties:{ticketId:{type:"string"},column:{type:"string"}}},UnblockRequest:{type:"object",properties:{resume:{type:"boolean",description:"Re-enter the existing run session on unblock."}}},UnblockResult:{type:"object",properties:{ticket:v("Ticket"),run:v("Run"),resumed:{type:"boolean"}}},Note:{type:"object",properties:{id:{type:"string"},ticketId:{type:"string"},author:{type:"string",enum:["user","assistant"]},body:{type:"string"},createdAt:{type:"string",format:"date-time"},updatedAt:{type:"string",format:"date-time"}}},NoteCreate:{type:"object",required:["body"],properties:{body:{type:"string",maxLength:5e3},author:{type:"string",enum:["user","assistant"]}}},Question:{type:"object",properties:{id:{type:"string"},ticketId:{type:"string"},question:{type:"string"},answer:{type:"string",nullable:!0},resolved:{type:"boolean"},position:{type:"integer"}}},QuestionCreate:{type:"object",required:["question"],properties:{question:{type:"string",maxLength:5e3}}},QuestionUpdate:{type:"object",properties:{question:{type:"string"},answer:{type:"string",nullable:!0},resolved:{type:"boolean"}}},RunStartRequest:{type:"object",description:"Only used by the `start` action.",properties:{details:{type:"string",description:"Calibration prompt appended to the run's guiding prompt."},attachments:{type:"array",items:{type:"string"},description:"File paths the agent reads from disk."},autonomy:{type:"string",enum:["supervised","autonomous"],description:"Applied only when the run doesn't exist yet; defaults to the workspace setting."}}},Run:{type:"object",properties:{id:{type:"string"},ticketId:{type:"string"},ticketKey:{type:"string"},title:{type:"string"},status:{type:"string",enum:["not_started","running","paused","blocked","stopped"]},stopReason:{type:"string",enum:["completed","user","error"],nullable:!0},workspaceId:{type:"string"}}},WebhookEnvelope:{type:"object",properties:{id:{type:"string",description:"Unique event id (evt_\u2026)."},event:{type:"string"},occurredAt:{type:"string",format:"date-time"},workspaceId:{type:"string"},data:{type:"object",additionalProperties:!0,description:"Event-specific payload (ticketId/runId, changed fields, \u2026)."}},required:["id","event","occurredAt","data"]},Batch:{type:"object",properties:{id:{type:"string"},name:{type:"string"},workspaceId:{type:"string"},workspaceName:{type:"string"},ticketIds:{type:"array",items:{type:"string"}},mode:{type:"string",enum:["ordered","unordered"]},advanceMode:{type:"string",enum:["on_success","approval"]},stackDependentWork:{type:"boolean"},status:{type:"string",enum:["idle","queued","running","paused","done"]},budgetCap:{type:["integer","null"]},pausedReason:{type:["string","null"]},runsStarted:{type:"integer"},createdAt:{type:"string",format:"date-time"},updatedAt:{type:"string",format:"date-time"}}},BatchDetail:{type:"object",properties:{batch:v("Batch"),tickets:{type:"array",items:{type:"object",properties:{ticketId:{type:"string"},externalIdentifier:{type:"string"},title:{type:"string"},state:{type:"string",enum:["done","running","blocked","failed","waiting","queued"]},column:{type:"string"},unmetDeps:{type:"array",items:{type:"string"}}}}}}}}}};function gc(){let e="dist/browser/standalone.js",t=go(fc(import.meta.url).resolve("@scalar/api-reference"));for(let n=0;n<6;n++){if(mc(Ar(t,e)))return Ar(t,e);t=go(t)}return Ar(t,e)}var hc=gc(),kc=`<!doctype html>
|
|
27
|
+
`)}`:"");return n.sessionId||(n.sessionId=oc()),fe(n,"running",{stopReason:null,event:{kind:"state_change",text:r?`started with details: ${r.slice(0,80)}`:"started"},webhookEvent:"run.started"}),S[n.ticketId]="in_progress",await U(n.id),await A(n.ticketId),E()||_e(n.id,!1,o||void 0),n}async function Rt(e){let t=Nn(e);return fe(t,"paused",{event:{kind:"state_change",text:"paused by user"},webhookEvent:"run.paused"}),await St(t.id),await U(t.id),t}async function Tt(e){let t=Nn(e);return fe(t,"running",{event:{kind:"state_change",text:"resumed (continue)"},webhookEvent:"run.resumed"}),S[t.ticketId]="in_progress",await U(t.id),await A(t.ticketId),E()||_e(t.id,!0),t}async function Fe(e){let t=Nn(e);return fe(t,"stopped",{stopReason:"user",clearBlocker:!0,event:{kind:"state_change",text:"stopped by user"},webhookEvent:"run.stopped"}),await St(t.id),await U(t.id),t}Z();import{randomUUID as so}from"node:crypto";var P=class extends Error{constructor(n,r,s){super(r);this.status=n;this.extra=s;this.name="TicketError"}},ac=["backlog","in_progress","needs_input","in_review","done"],en=e=>S[e]??"backlog",Ir=e=>b.find(t=>t.id===e.connectionId)?.workspaceId??void 0;function _t(e){let t=h.find(n=>n.id===e);if(!t)throw new P(404,"ticket not found");return t}function Cn(e){return h.find(t=>t.id===e)}function Pn(e){let t=(e.dependsOn??[]).map(n=>{let r=h.find(s=>s.id===n);return{id:n,identifier:r?.externalIdentifier??null,title:r?.title??null,column:r?en(n):null,done:!!r&&en(n)==="done",missing:!r}});return{dependencies:t,blockedByDeps:t.some(n=>!n.done)}}async function On(e,t){let n=_t(e),r=o=>h.find(i=>i.id===o),s=ro(n.id,n.connectionId,t,r);if(no(n.id,s,r))throw new P(409,"that would create a dependency cycle",{code:"dependency_cycle"});return n.dependsOn=s.length?s:void 0,await A(n.id),{dependsOn:s,unmet:xt(n,r,en)}}function At(e){return j.filter(t=>t.ticketId===e).slice().reverse()}function Nt(e){return D.filter(t=>t.ticketId===e)}async function Et(e,t){let n=_t(e);if(!ac.includes(t))throw new P(400,"invalid column");if(n.blockedReason&&t!==en(n.id))throw new P(409,"a blocked ticket can't change lanes \u2014 unblock it first");if(t==="done"){let s=T[n.id],o=s?x[s]:void 0;if(!o||o.status!=="stopped")throw new P(409,o?"Done is for finished work \u2014 let this run stop before moving it to Done.":"Done is for finished work \u2014 start and finish a run before moving it to Done.")}let r=en(n.id);return S[n.id]=t,await A(n.id),t!==r&&le("ticket.moved",{ticketId:n.id,ticketKey:n.externalIdentifier,from:r,to:t},{workspaceId:Ir(n)}),{ticketId:n.id,column:t}}async function Ae(e,t){let n=_t(e),r=[],s=null;if(typeof t.workingDir=="string"&&(n.workingDir=t.workingDir.trim()||void 0),t.model!==void 0&&(n.model=Re(t.model,n.model||ke)),t.effort!==void 0&&(n.effort=Te(t.effort,n.effort||ye)),"blockedReason"in t){let i=String(t.blockedReason??"").trim().slice(0,2e3),c=!!n.blockedReason,d=!!i;if(c&&!d){let u=D.filter(p=>p.ticketId===n.id&&!p.resolved).length;if(u>0)throw new P(409,"resolve open questions before unblocking",{unresolved:u})}n.blockedReason=i||void 0,d&&!c?(S[n.id]="needs_input",s={event:"ticket.blocked"}):!d&&c&&(S[n.id]="backlog",s={event:"ticket.unblocked"})}for(let i of["additionalContext","plan","conclusion"])if(i in t){let d=String(t[i]??"").trim().slice(0,2e4)||void 0;d!==n[i]&&(n[i]=d,n[`${i}UpdatedAt`]=d?new Date().toISOString():void 0,r.push(i))}if(n.provider==="local"){if(typeof t.title=="string"&&t.title.trim()&&t.title.trim()!==n.title&&(n.title=t.title.trim(),r.push("title")),typeof t.description=="string"){let c=t.description.trim()||void 0;c!==n.description&&(n.description=c,r.push("description"))}let i=Number(t.priority);[0,1,2,3,4].includes(i)&&i!==n.priority&&(n.priority=i,r.push("priority")),n.updatedAt=new Date().toISOString()}await A(n.id);let o=Ir(n);return s&&le(s.event,{ticketId:n.id,ticketKey:n.externalIdentifier,...n.blockedReason?{reason:n.blockedReason}:{}},{workspaceId:o}),r.length&&le("ticket.updated",{ticketId:n.id,ticketKey:n.externalIdentifier,fields:r},{workspaceId:o}),n}async function Ct(e,t={}){let n=_t(e);if(!n.blockedReason)throw new P(409,"ticket is not blocked");let r=D.filter(d=>d.ticketId===n.id&&!d.resolved).length;if(r>0)throw new P(409,"resolve open questions before unblocking",{unresolved:r});let s=T[n.id],o=s?x[s]:void 0,i=t.resume===!0&&!!o?.sessionId;n.blockedReason=void 0;let c=Ir(n);return le("ticket.unblocked",{ticketId:n.id,ticketKey:n.externalIdentifier},{workspaceId:c}),i&&o?(fe(o,"running",{stopReason:null,clearBlocker:!0,event:{kind:"state_change",text:"unblocked & resumed"},webhookEvent:"run.resumed"}),S[n.id]="in_progress",await U(o.id),await A(n.id),E()||_e(o.id,!0,"The block on this ticket has been resolved. Re-read the ticket with read_ticket (its open questions are now answered) and continue the work."),{ticket:n,run:o,resumed:!0}):(S[n.id]="backlog",await A(n.id),{ticket:n,resumed:!1})}async function Pt(e,t){let n=b.find(u=>u.id===e);if(!n)throw new P(404,"board not found");if(n.provider!=="local")throw new P(400,"tickets can only be created on the Local board");let r=String(t.title??"").trim();if(!r)throw new P(400,"title is required");let s=Number(t.priority),o=[0,1,2,3,4].includes(s)?s:0,i=++G.localSeq,c=new Date().toISOString(),d={id:`t_local${i}`,connectionId:n.id,provider:"local",externalIdentifier:`LOCAL-${i}`,project:"Local",title:r,status:"todo",statusRaw:"Backlog",priority:o,labels:[],url:"",workspaceId:n.workspaceId,workingDir:String(t.workingDir??"").trim()||(n.workspaceId?l.workspaceDirs?.[n.workspaceId]??"~":"~"),model:Re(t.model??l.defaultModel),effort:Te(t.effort??l.defaultEffort),description:String(t.description??"").trim()||void 0,createdAt:c,updatedAt:c};return h.push(d),S[d.id]="backlog",n.ticketCount+=1,await A(d.id),await N(n),d}async function Ot(e,t){_t(e);let n=String(t.body??"").trim();if(!n)throw new P(400,"empty note");let r=t.author==="assistant"?"assistant":"user",s=new Date().toISOString(),o={id:`note_${so()}`,ticketId:e,author:r,body:n.slice(0,5e3),createdAt:s,updatedAt:s};return j.push(o),await ht(o),o}async function Dt(e,t){let n=j.find(s=>s.id===e);if(!n)throw new P(404,"note not found");let r=String(t??"").trim();if(!r)throw new P(400,"empty note");return n.body=r.slice(0,5e3),n.updatedAt=new Date().toISOString(),await ht(n),n}async function jt(e){let t=j.findIndex(n=>n.id===e);if(t===-1)throw new P(404,"note not found");j.splice(t,1),await kn(e)}async function Mt(e,t){_t(e);let n=String(t??"").trim();if(!n)throw new P(400,"empty question");let r=new Date().toISOString(),s=D.filter(i=>i.ticketId===e).length,o={id:`oq_${so()}`,ticketId:e,question:n.slice(0,5e3),answer:void 0,resolved:!1,position:s,createdAt:r,updatedAt:r};return D.push(o),await ir(o.id),o}async function Bt(e,t){let n=D.find(r=>r.id===e);if(n===void 0)throw new P(404,"question not found");return typeof t.question=="string"&&t.question.trim()&&(n.question=t.question.trim().slice(0,5e3)),"answer"in t&&(n.answer=String(t.answer??"").trim().slice(0,2e4)||void 0),typeof t.resolved=="boolean"&&(n.resolved=t.resolved),n.updatedAt=new Date().toISOString(),await ir(n.id),n}async function $t(e){let t=D.findIndex(n=>n.id===e);if(t===-1)throw new P(404,"question not found");D.splice(t,1),await wn(e),await Rs()}Z();var q=class extends Error{constructor(n,r,s,o){super(s);this.status=n;this.code=r;this.extra=o;this.name="BatchError"}};function Lt(){return K.map(e=>L[e]).filter(Boolean)}function Ve(e){let t=L[e];if(!t)throw new q(404,"not_found","batch not found");return t}async function Ze(e){let t=(e.name??"").trim();if(!t)throw new q(400,"invalid","batch name is required");let n=[...new Set(e.ticketIds??[])];if(n.length===0)throw new q(400,"invalid","a batch needs at least one ticket");let r=new Set(["queued","running","paused"]);for(let I of K){let f=L[I];if(!f||!r.has(f.status))continue;let w=n.find(V=>f.ticketIds.includes(V));if(w){let V=h.find(He=>He.id===w);throw new q(409,"in_batch",`ticket ${V?.externalIdentifier??w} is already in the active batch "${f.name}"`)}}let s=new Set(n),o,i,c=!1;for(let I of n){let f=h.find(V=>V.id===I);if(!f)throw new q(404,"not_found",`ticket ${I} not found`);if(!J(I))throw new q(409,"not_assigned",`ticket ${f.externalIdentifier} isn't assigned \u2014 assign it before adding it to a batch`);let w=b.find(V=>V.id===f.connectionId);if(!w||w.linkStatus!=="linked"||!w.workspaceId)throw new q(409,"not_linked",`ticket ${f.externalIdentifier}'s connection isn't linked to a workspace`);if(o&&w.workspaceId!==o)throw new q(409,"mixed_workspaces","all tickets in a batch must belong to the same workspace");o=w.workspaceId,i=w.workspaceName??o,(f.dependsOn??[]).some(V=>s.has(V))&&(c=!0)}let d=c?"ordered":"unordered",u=new Date().toISOString(),p=`batch_${++G.batchSeq}`,g={id:p,name:t,workspaceId:o,workspaceName:i,ticketIds:n,mode:d,advanceMode:e.advanceMode??"on_success",stackDependentWork:e.stackDependentWork??d==="ordered",status:"idle",budgetCap:e.budgetCap??null,pausedReason:null,runsStarted:0,createdAt:u,updatedAt:u};return L[p]=g,K.unshift(p),await ue(p),g}async function oo(e,t){let n=Ve(e);if(n.status!=="idle"&&n.status!=="done")throw new q(409,"active","stop the batch before editing it");if(t.name!==void 0){let r=t.name.trim();if(!r)throw new q(400,"invalid","batch name is required");n.name=r}if(t.ticketIds!==void 0){let r=await Ze({name:n.name,ticketIds:t.ticketIds,advanceMode:t.advanceMode??n.advanceMode,stackDependentWork:t.stackDependentWork??n.stackDependentWork,budgetCap:t.budgetCap??n.budgetCap});n.ticketIds=r.ticketIds,n.mode=r.mode,n.advanceMode=r.advanceMode,n.stackDependentWork=r.stackDependentWork,n.budgetCap=r.budgetCap,delete L[r.id],K.splice(K.indexOf(r.id),1),await Xt(r.id)}else t.advanceMode!==void 0&&(n.advanceMode=t.advanceMode),t.stackDependentWork!==void 0&&(n.stackDependentWork=t.stackDependentWork),t.budgetCap!==void 0&&(n.budgetCap=t.budgetCap);return n.updatedAt=new Date().toISOString(),await ue(e),n}async function Dn(e){if(Ve(e).status==="running")throw new q(409,"running","stop the batch before deleting it");delete L[e];let n=K.indexOf(e);n!==-1&&K.splice(n,1),await Xt(e)}Z();var vr=e=>S[e];function Ne(e,t,n={}){le(e,{batch:{id:t.id,name:t.name,status:t.status,mode:t.mode,ticketCount:t.ticketIds.length},...n},{workspaceId:t.workspaceId})}function We(e,t,n){e.status=t,e.pausedReason=n,e.updatedAt=new Date().toISOString(),ue(e.id)}function Rr(e,t){let n=vr(e);return n==="done"?!0:t.ticketIds.includes(e)&&t.advanceMode==="on_success"&&n==="in_review"}function cc(e){return e.ticketIds.filter(t=>{let n=J(t);if(!n||n.status!=="not_started")return!1;let r=h.find(s=>s.id===t);return r?(r.dependsOn??[]).every(s=>Rr(s,e)):!1})}function io(e){return e.ticketIds.filter(t=>J(t)?.status==="running")}function dc(e){(async()=>{try{let t=await vt(e,{autonomy:"autonomous"});if(t.status!=="not_started")return;t.autonomy="autonomous",await qe(t.id,{skipDepGate:!0})}catch(t){console.error(`batch: failed to start ticket ${e}`,t)}})()}function ao(e,t){for(let n of K){let r=L[n];if(r?.ticketIds.includes(e)&&(!t||t(r)))return r}}function uc(e){return ao(e,t=>t.status==="running")}function co(){return K.map(e=>L[e]).find(e=>e?.status==="running")}function Ke(){if(co())return;let t=K.map(n=>L[n]).filter(n=>!!n&&n.status==="queued").sort((n,r)=>n.updatedAt.localeCompare(r.updatedAt))[0];t&&(t.runsStarted=0,We(t,"running",null),Ne("batch.started",t),jn(t))}async function uo(e,t,n,r){let s=ao(e,d=>d.stackDependentWork&&d.mode==="ordered");if(!s)return{};let o=h.find(d=>d.id===e);if(!o)return{};let i=[];for(let d of o.dependsOn??[]){if(!s.ticketIds.includes(d))continue;let u=J(d)?.worktrees?.find(p=>p.repo===t);u&&vr(d)!=="done"&&(h.find(p=>p.id===d)?.prMerged||await Us(n,u.branch,r)||i.push(u.branch))}let c=[...new Set(i)];return c.length>1?{ambiguous:c}:c.length===1?{startPoint:c[0]}:{}}function jn(e){if(e.status!=="running")return;let t=cc(e),n=io(e);if(t.length===0&&n.length===0){let r=e.ticketIds.filter(s=>J(s)?.status==="not_started");if(r.length>0){let s=u=>h.find(p=>p.id===u)?.externalIdentifier??u,o=r[0],i=(h.find(u=>u.id===o)?.dependsOn??[]).filter(u=>!Rr(u,e)).map(s),c=r.length>1?` (+${r.length-1} more)`:"",d=i.length?`${s(o)} can't start \u2014 waiting on ${i.join(", ")} (not satisfied in this batch)${c}. Resolve (approve / add to the batch) and Resume.`:`${s(o)} can't start${c}.`;We(e,"paused",d),Ne("batch.paused",e,{reason:d,stuck:r}),Ke();return}We(e,"done",null),Ne("batch.completed",e),Ke();return}for(let r of t){if(e.budgetCap!=null&&e.runsStarted>=e.budgetCap){We(e,"paused",`hit the budget cap of ${e.budgetCap} runs`),Ne("batch.paused",e,{reason:e.pausedReason}),Ke();return}e.runsStarted++,dc(r)}ue(e.id)}function lc(e,t){let n=uc(e);if(!n)return;let s=h.find(o=>o.id===e)?.externalIdentifier??e;t==="blocked"?(We(n,"paused",`${s} blocked \u2014 needs input`),Ne("batch.ticket_blocked",n,{ticket:{id:e,key:s}}),Ne("batch.paused",n,{reason:n.pausedReason}),Ke()):t==="error"?(We(n,"paused",`${s} failed`),Ne("batch.paused",n,{reason:n.pausedReason}),Ke()):jn(n)}function Gt(e){let t=Ve(e),r=t.ticketIds.filter(s=>h.some(o=>o.id===s)).map(s=>{let o=h.find(p=>p.id===s),i=J(s),c=vr(s)??"backlog",d=(o?.dependsOn??[]).filter(p=>!Rr(p,t)),u;return c==="done"?u="done":c==="in_review"?u="in_review":i?.status==="running"?u="running":o?.blockedReason?u="blocked":i?.status==="stopped"&&i.stopReason==="error"?u="failed":d.length?u="waiting":u="queued",{ticketId:s,externalIdentifier:o?.externalIdentifier??s,title:o?.title??s,state:u,column:c,unmetDeps:d}});return{batch:t,tickets:r}}async function Ut(e){let t=Ve(e);if(t.status==="running")throw new q(409,"running","batch is already running");let n=co();return n&&n.id!==e?(We(t,"queued",`waiting for "${n.name}" to finish`),t):(t.status!=="paused"&&(t.runsStarted=0),t.status="running",t.pausedReason=null,t.updatedAt=new Date().toISOString(),await ue(e),Ne("batch.started",t),jn(t),t)}async function qt(e){let t=Ve(e);if(t.status!=="running")throw new q(409,"not_running","batch isn't running");return t.status="paused",t.pausedReason="paused by you",t.updatedAt=new Date().toISOString(),await ue(e),Ne("batch.paused",t,{reason:t.pausedReason}),Ke(),t}async function Ft(e){let t=Ve(e),n=t.status==="running";for(let r of io(t)){let s=J(r);s&&await Fe(s.id).catch(()=>{})}return t.status="idle",t.pausedReason=null,t.updatedAt=new Date().toISOString(),await ue(e),n&&Ke(),t}function Tr(e){let t=new Set(e);return e.some(r=>(h.find(s=>s.id===r)?.dependsOn??[]).some(s=>t.has(s)))?"ordered":"unordered"}async function lo(e){delete L[e];let t=K.indexOf(e);t!==-1&&K.splice(t,1),await Xt(e)}async function tn(e){for(let t of[...K]){let n=L[t];if(!(!n||!n.ticketIds.includes(e))){if(n.ticketIds=n.ticketIds.filter(r=>r!==e),n.ticketIds.length===0){await lo(t);continue}n.mode=Tr(n.ticketIds),n.updatedAt=new Date().toISOString(),await ue(t),n.status==="running"&&jn(n)}}}async function Mn(e){for(let t of K){let n=L[t];if(!n||!n.ticketIds.includes(e))continue;let r=Tr(n.ticketIds);n.mode!==r&&(n.mode=r,n.updatedAt=new Date().toISOString(),await ue(t))}}function po(){for(let e of[...K]){let t=L[e];if(!t)continue;let n=t.ticketIds.filter(r=>h.some(s=>s.id===r));if(n.length===0){lo(e);continue}n.length!==t.ticketIds.length&&(t.ticketIds=n,t.mode=Tr(n),ue(e)),t.status==="running"&&We(t,"paused","interrupted by a daemon restart \u2014 resume to continue")}Ke()}eo(lc);Z();Z();import mo from"express";import fc from"cors";import{createRequire as mc}from"node:module";import{dirname as go,join as Ar}from"node:path";import{existsSync as gc}from"node:fs";var Bn=["ticket.moved","ticket.blocked","ticket.unblocked","ticket.updated","run.started","run.paused","run.resumed","run.completed","run.failed","run.stopped","schedule.run.started","schedule.run.completed","schedule.run.failed","batch.started","batch.completed","batch.paused","batch.ticket_blocked"];var Q={name:"id",in:"path",required:!0,schema:{type:"string"}},M=[{bearerAuth:[]}],me=(e,t=!0)=>({required:t,content:{"application/json":{schema:e}}}),F=(e,t="Success")=>({description:t,content:{"application/json":{schema:e}}}),v=e=>({$ref:`#/components/schemas/${e}`}),R=e=>({description:e,content:{"application/json":{schema:v("Error")}}});function pc(){let e={};for(let t of Bn)e[t]={post:{summary:`\`${t}\` event`,description:"Delivered to your configured webhook URL. Fixed headers (Content-Type, User-Agent, X-SuperClawd-Event) plus your custom headers; signed with HMAC-SHA256 in X-SuperClawd-Signature (sha256=<hex>) when a signing secret is set.",requestBody:me(v("WebhookEnvelope")),responses:{"2xx":{description:"Acknowledge with any 2xx; non-2xx is retried."}}}};return e}var fo={openapi:"3.1.0",info:{title:"SuperClawd Pipelines \u2014 Public API",version:"1.0.0",description:"Key-authenticated HTTP API for board, ticket, and run actions, plus outbound webhooks for ticket and run lifecycle events. Authenticate every request with `Authorization: Bearer <key>` (generate a key in Settings \u2192 API)."},servers:[{url:"/",description:"This Pipelines daemon (public API port)"}],security:M,tags:[{name:"Boards"},{name:"Tickets"},{name:"Notes"},{name:"Questions"},{name:"Runs"},{name:"Batches"}],paths:{"/v1/boards":{get:{tags:["Boards"],summary:"List boards (connections)",security:M,responses:{200:F({type:"array",items:v("Board")},"Boards"),401:R("Missing or invalid API key")}}},"/v1/boards/{id}/tickets":{get:{tags:["Boards"],summary:"List a board's tickets",security:M,parameters:[Q],responses:{200:F({type:"array",items:v("Ticket")},"Tickets"),401:R("Missing or invalid API key")}},post:{tags:["Boards"],summary:"Create a ticket on a Local board",description:"Local-board only (vendor boards import tickets, they don't author them). New tickets land in Backlog.",security:M,parameters:[Q],requestBody:me({type:"object",required:["title"],properties:{title:{type:"string"},description:{type:"string"},priority:{type:"integer",enum:[0,1,2,3,4],description:"0 none \xB7 1 urgent \xB7 2 high \xB7 3 medium \xB7 4 low"},workingDir:{type:"string"},model:{type:"string"},effort:{type:"string"}}}),responses:{201:F(v("Ticket"),"Created ticket"),400:R("Not a local board, or title missing"),404:R("Board not found")}}},"/v1/tickets/{id}":{get:{tags:["Tickets"],summary:"Get a ticket",security:M,parameters:[Q],responses:{200:F(v("Ticket"),"Ticket"),404:R("Ticket not found")}},patch:{tags:["Tickets"],summary:"Update ticket fields",description:"plan/conclusion/additionalContext/blockedReason/workingDir/model/effort apply to any ticket; title/description/priority to local-board tickets only. Setting blockedReason blocks (moves to the Blocked lane); clearing it unblocks (refused while open questions are unresolved).",security:M,parameters:[Q],requestBody:me(v("TicketUpdate")),responses:{200:F(v("Ticket"),"Updated ticket"),404:R("Ticket not found"),409:R("Unresolved open questions block unblocking")}}},"/v1/tickets/{id}/move":{post:{tags:["Tickets"],summary:"Move a ticket to a lane",description:"A blocked ticket can't change lanes until unblocked; Done is only allowed once the ticket's run is stopped.",security:M,parameters:[Q],requestBody:me(v("MoveRequest")),responses:{200:F(v("MoveResult"),"Moved"),400:R("Invalid column"),404:R("Ticket not found"),409:R("Blocked-lane freeze or Done gate")}}},"/v1/tickets/{id}/unblock":{post:{tags:["Tickets"],summary:"Unblock a ticket",description:"Refused while open questions are unresolved. With `resume: true` (and an existing run session) the run re-enters and the card goes to In Progress; otherwise it returns to Backlog.",security:M,parameters:[Q],requestBody:me(v("UnblockRequest"),!1),responses:{200:F(v("UnblockResult"),"Unblocked"),409:R("Not blocked, or unresolved open questions")}}},"/v1/tickets/{id}/notes":{get:{tags:["Notes"],summary:"List a ticket's notes (newest first)",security:M,parameters:[Q],responses:{200:F({type:"array",items:v("Note")},"Notes")}},post:{tags:["Notes"],summary:"Add a note",security:M,parameters:[Q],requestBody:me(v("NoteCreate")),responses:{201:F(v("Note"),"Created"),400:R("Empty note"),404:R("Ticket not found")}}},"/v1/notes/{id}":{patch:{tags:["Notes"],summary:"Edit a note",security:M,parameters:[Q],requestBody:me(v("NoteCreate")),responses:{200:F(v("Note"),"Updated"),404:R("Note not found")}},delete:{tags:["Notes"],summary:"Delete a note",security:M,parameters:[Q],responses:{200:F(v("Ok"),"Deleted"),404:R("Note not found")}}},"/v1/tickets/{id}/questions":{get:{tags:["Questions"],summary:"List a ticket's open questions",security:M,parameters:[Q],responses:{200:F({type:"array",items:v("Question")},"Questions")}},post:{tags:["Questions"],summary:"Add an open question",security:M,parameters:[Q],requestBody:me(v("QuestionCreate")),responses:{201:F(v("Question"),"Created"),400:R("Empty question"),404:R("Ticket not found")}}},"/v1/questions/{id}":{patch:{tags:["Questions"],summary:"Edit a question (text / answer / resolved)",security:M,parameters:[Q],requestBody:me(v("QuestionUpdate")),responses:{200:F(v("Question"),"Updated"),404:R("Question not found")}},delete:{tags:["Questions"],summary:"Delete a question",security:M,parameters:[Q],responses:{200:F(v("Ok"),"Deleted"),404:R("Question not found")}}},"/v1/tickets/{id}/run":{get:{tags:["Runs"],summary:"Get the ticket's run",security:M,parameters:[Q],responses:{200:F(v("Run"),"Run"),404:R("No run for this ticket")}}},"/v1/tickets/{id}/run/{action}":{post:{tags:["Runs"],summary:"Control the run (start | pause | resume | stop)",description:"start creates a run if none exists, then enqueues it. pause kills the process but keeps the session resumable; resume re-enters it; stop marks it stopped and kills it.",security:M,parameters:[Q,{name:"action",in:"path",required:!0,schema:{type:"string",enum:["start","pause","resume","stop"]}}],requestBody:me(v("RunStartRequest"),!1),responses:{200:F(v("Run"),"Run"),400:R("Unknown action"),404:R("No run for this ticket"),409:R("Unmet dependencies (on first start)")}}},"/v1/batches":{get:{tags:["Batches"],summary:"List batches",security:M,responses:{200:F({type:"array",items:v("Batch")},"Batches"),401:R("Missing or invalid API key")}},post:{tags:["Batches"],summary:"Create a batch",description:"A batch is a set of already-assigned tickets you press play on to run unattended. ordered/unordered is derived from the tickets' dependencies. Every ticket must be assigned and share one linked workspace, and can't already be in another active batch.",security:M,requestBody:me({type:"object",required:["name","ticketIds"],properties:{name:{type:"string"},ticketIds:{type:"array",items:{type:"string"}},advanceMode:{type:"string",enum:["on_success","approval"],description:"on_success: a member reaching In Review unblocks the next. approval: dependents wait for Done."},stackDependentWork:{type:"boolean",description:"Ordered batches only: a dependent member branches off its blocker's branch."},budgetCap:{type:["integer","null"],description:"Pause the batch after this many runs (null = no cap)."}}}),responses:{201:F(v("Batch"),"Created batch"),400:R("Invalid (name/tickets missing)"),409:R("A ticket isn't assigned, mixes workspaces, or is already in an active batch")}}},"/v1/batches/{id}":{get:{tags:["Batches"],summary:"Get a batch with its members' live state",security:M,parameters:[Q],responses:{200:F(v("BatchDetail"),"Batch detail"),404:R("Batch not found")}},delete:{tags:["Batches"],summary:"Delete a batch",description:"Removes the batch grouping only \u2014 the tickets and their runs are untouched. A running batch can't be deleted (stop it first).",security:M,parameters:[Q],responses:{200:F(v("Ok"),"Deleted"),404:R("Batch not found"),409:R("Batch is running")}}},"/v1/batches/{id}/{action}":{post:{tags:["Batches"],summary:"Control the batch (start | pause | stop)",description:"start plays or resumes it (queues behind any already-running batch). pause stops starting new members (in-flight runs finish). stop halts it and kills its running members.",security:M,parameters:[Q,{name:"action",in:"path",required:!0,schema:{type:"string",enum:["start","pause","stop"]}}],responses:{200:F(v("Batch"),"Batch"),400:R("Unknown action"),404:R("Batch not found"),409:R("Illegal transition (e.g. already running)")}}}},webhooks:pc(),components:{securitySchemes:{bearerAuth:{type:"http",scheme:"bearer",description:"The API key generated in Settings \u2192 API."}},schemas:{Error:{type:"object",properties:{error:{type:"string"},code:{type:"string"}},required:["error"]},Ok:{type:"object",properties:{ok:{type:"boolean"}}},Board:{type:"object",properties:{id:{type:"string"},provider:{type:"string"},workspaceId:{type:"string"},workspaceName:{type:"string"},linkStatus:{type:"string"},ticketCount:{type:"integer"}}},Ticket:{type:"object",description:"A board ticket (fields vary by provider).",properties:{id:{type:"string"},connectionId:{type:"string"},provider:{type:"string"},externalIdentifier:{type:"string"},title:{type:"string"},description:{type:"string"},column:{type:"string",enum:["backlog","in_progress","needs_input","in_review","done"]},blockedReason:{type:"string",nullable:!0},plan:{type:"string",nullable:!0},conclusion:{type:"string",nullable:!0},additionalContext:{type:"string",nullable:!0},comments:{type:"array",description:"Vendor comments/discussion synced read-only from the provider (oldest\u2192newest).",items:v("TicketComment")}}},TicketComment:{type:"object",description:"A ticket comment synced read-only from the vendor.",properties:{id:{type:"string"},author:{type:"string",nullable:!0},body:{type:"string"},createdAt:{type:"string",format:"date-time",nullable:!0},updatedAt:{type:"string",format:"date-time",nullable:!0}}},TicketUpdate:{type:"object",properties:{title:{type:"string"},description:{type:"string"},priority:{type:"integer",minimum:0,maximum:4},plan:{type:"string"},conclusion:{type:"string"},additionalContext:{type:"string"},blockedReason:{type:"string",nullable:!0,description:"Non-empty blocks; null/empty unblocks."},workingDir:{type:"string"},model:{type:"string"},effort:{type:"string"}}},MoveRequest:{type:"object",required:["column"],properties:{column:{type:"string",enum:["backlog","in_progress","needs_input","in_review","done"]}}},MoveResult:{type:"object",properties:{ticketId:{type:"string"},column:{type:"string"}}},UnblockRequest:{type:"object",properties:{resume:{type:"boolean",description:"Re-enter the existing run session on unblock."}}},UnblockResult:{type:"object",properties:{ticket:v("Ticket"),run:v("Run"),resumed:{type:"boolean"}}},Note:{type:"object",properties:{id:{type:"string"},ticketId:{type:"string"},author:{type:"string",enum:["user","assistant"]},body:{type:"string"},createdAt:{type:"string",format:"date-time"},updatedAt:{type:"string",format:"date-time"}}},NoteCreate:{type:"object",required:["body"],properties:{body:{type:"string",maxLength:5e3},author:{type:"string",enum:["user","assistant"]}}},Question:{type:"object",properties:{id:{type:"string"},ticketId:{type:"string"},question:{type:"string"},answer:{type:"string",nullable:!0},resolved:{type:"boolean"},position:{type:"integer"}}},QuestionCreate:{type:"object",required:["question"],properties:{question:{type:"string",maxLength:5e3}}},QuestionUpdate:{type:"object",properties:{question:{type:"string"},answer:{type:"string",nullable:!0},resolved:{type:"boolean"}}},RunStartRequest:{type:"object",description:"Only used by the `start` action.",properties:{details:{type:"string",description:"Calibration prompt appended to the run's guiding prompt."},attachments:{type:"array",items:{type:"string"},description:"File paths the agent reads from disk."},autonomy:{type:"string",enum:["supervised","autonomous"],description:"Applied only when the run doesn't exist yet; defaults to the workspace setting."}}},Run:{type:"object",properties:{id:{type:"string"},ticketId:{type:"string"},ticketKey:{type:"string"},title:{type:"string"},status:{type:"string",enum:["not_started","running","paused","blocked","stopped"]},stopReason:{type:"string",enum:["completed","user","error"],nullable:!0},workspaceId:{type:"string"}}},WebhookEnvelope:{type:"object",properties:{id:{type:"string",description:"Unique event id (evt_\u2026)."},event:{type:"string"},occurredAt:{type:"string",format:"date-time"},workspaceId:{type:"string"},data:{type:"object",additionalProperties:!0,description:"Event-specific payload (ticketId/runId, changed fields, \u2026)."}},required:["id","event","occurredAt","data"]},Batch:{type:"object",properties:{id:{type:"string"},name:{type:"string"},workspaceId:{type:"string"},workspaceName:{type:"string"},ticketIds:{type:"array",items:{type:"string"}},mode:{type:"string",enum:["ordered","unordered"]},advanceMode:{type:"string",enum:["on_success","approval"]},stackDependentWork:{type:"boolean"},status:{type:"string",enum:["idle","queued","running","paused","done"]},budgetCap:{type:["integer","null"]},pausedReason:{type:["string","null"]},runsStarted:{type:"integer"},createdAt:{type:"string",format:"date-time"},updatedAt:{type:"string",format:"date-time"}}},BatchDetail:{type:"object",properties:{batch:v("Batch"),tickets:{type:"array",items:{type:"object",properties:{ticketId:{type:"string"},externalIdentifier:{type:"string"},title:{type:"string"},state:{type:"string",enum:["done","running","blocked","failed","waiting","queued"]},column:{type:"string"},unmetDeps:{type:"array",items:{type:"string"}}}}}}}}}};function hc(){let e="dist/browser/standalone.js",t=go(mc(import.meta.url).resolve("@scalar/api-reference"));for(let n=0;n<6;n++){if(gc(Ar(t,e)))return Ar(t,e);t=go(t)}return Ar(t,e)}var kc=hc(),yc=`<!doctype html>
|
|
28
28
|
<html>
|
|
29
29
|
<head>
|
|
30
30
|
<meta charset="utf-8" />
|
|
@@ -62,26 +62,26 @@ ${s.map(i=>`- ${i}`).join(`
|
|
|
62
62
|
})();
|
|
63
63
|
</script>
|
|
64
64
|
</body>
|
|
65
|
-
</html>`,ho=e=>I[e]??"backlog";function yc(e,t,n){let r=l.apiKeyHash;if(!r){t.status(401).json({error:"public API key is not configured"});return}let o=/^Bearer\s+(.+)$/i.exec(e.headers.authorization??"")?.[1]?.trim();if(!o||!Qt(ft(o),r)){t.status(401).json({error:"invalid or missing API key"});return}n()}function ee(e,t){return t instanceof pe?e.status(t.status).json({error:t.message,...t.code?{code:t.code}:{},...t.extra??{}}):t instanceof P?e.status(t.status).json({error:t.message,...t.extra??{}}):t instanceof q?e.status(t.status).json({error:t.message,...t.code?{code:t.code}:{},...t.extra??{}}):(console.error("[public-api]",t),e.status(500).json({error:"internal error"}))}function wc(e){let t=Nn(e);return t?{...t,column:ho(t.id)}:null}function bc(){let e=mo();return e.use(pc()),e.use(mo.json()),e.get("/openapi.json",(t,n)=>n.json(fo)),e.get("/docs/scalar.js",(t,n)=>n.sendFile(hc)),e.get("/docs",(t,n)=>n.type("html").send(kc)),e.use("/v1",yc),e.get("/v1/boards",(t,n)=>{n.json(b.map(r=>({id:r.id,provider:r.provider,workspaceId:r.workspaceId,workspaceName:r.workspaceName,linkStatus:r.linkStatus,ticketCount:r.ticketCount})))}),e.get("/v1/boards/:id/tickets",(t,n)=>{let r=h.filter(s=>s.connectionId===t.params.id).map(s=>({...s,column:ho(s.id)}));n.json(r)}),e.post("/v1/boards/:id/tickets",async(t,n)=>{try{return n.status(201).json(await Ct(t.params.id,t.body??{}))}catch(r){return ee(n,r)}}),e.get("/v1/tickets/:id",(t,n)=>{let r=wc(t.params.id);return r?n.json(r):n.status(404).json({error:"ticket not found"})}),e.post("/v1/tickets/:id/move",async(t,n)=>{try{return n.json(await Nt(t.params.id,t.body?.column))}catch(r){return ee(n,r)}}),e.patch("/v1/tickets/:id",async(t,n)=>{try{return n.json(await Te(t.params.id,t.body??{}))}catch(r){return ee(n,r)}}),e.post("/v1/tickets/:id/unblock",async(t,n)=>{try{return n.json(await Et(t.params.id,{resume:t.body?.resume===!0}))}catch(r){return ee(n,r)}}),e.get("/v1/tickets/:id/notes",(t,n)=>{n.json(_t(t.params.id))}),e.post("/v1/tickets/:id/notes",async(t,n)=>{try{let r=await Pt(t.params.id,{body:String(t.body?.body??""),author:t.body?.author});return n.status(201).json(r)}catch(r){return ee(n,r)}}),e.patch("/v1/notes/:id",async(t,n)=>{try{return n.json(await Ot(t.params.id,String(t.body?.body??"")))}catch(r){return ee(n,r)}}),e.delete("/v1/notes/:id",async(t,n)=>{try{return await Dt(t.params.id),n.json({ok:!0})}catch(r){return ee(n,r)}}),e.get("/v1/tickets/:id/questions",(t,n)=>{n.json(At(t.params.id))}),e.post("/v1/tickets/:id/questions",async(t,n)=>{try{let r=await jt(t.params.id,String(t.body?.question??""));return n.status(201).json(r)}catch(r){return ee(n,r)}}),e.patch("/v1/questions/:id",async(t,n)=>{try{return n.json(await Mt(t.params.id,t.body??{}))}catch(r){return ee(n,r)}}),e.delete("/v1/questions/:id",async(t,n)=>{try{return await $t(t.params.id),n.json({ok:!0})}catch(r){return ee(n,r)}}),e.get("/v1/tickets/:id/run",(t,n)=>{let r=J(t.params.id);return r?n.json(r):n.status(404).json({error:"no run for this ticket"})}),e.post("/v1/tickets/:id/run/:action",async(t,n)=>{let r=t.params.action;try{if(r==="start"){let o=t.body?.autonomy,i=await xt(t.params.id,o==="supervised"||o==="autonomous"?{autonomy:o}:{});return n.json(await qe(i.id,{details:String(t.body?.details??""),attachments:Array.isArray(t.body?.attachments)?t.body.attachments.map(c=>String(c)):[]}))}let s=J(t.params.id);return s?r==="pause"?n.json(await vt(s.id)):r==="resume"?n.json(await Rt(s.id)):r==="stop"?n.json(await Fe(s.id)):n.status(400).json({error:"unknown action (use start | pause | resume | stop)"}):n.status(404).json({error:"no run for this ticket"})}catch(s){return ee(n,s)}}),e.get("/v1/batches",(t,n)=>{n.json(Bt())}),e.get("/v1/batches/:id",(t,n)=>{try{return n.json(Lt(t.params.id))}catch(r){return ee(n,r)}}),e.post("/v1/batches",async(t,n)=>{try{let r=await Ze({name:String(t.body?.name??""),ticketIds:Array.isArray(t.body?.ticketIds)?t.body.ticketIds.map(s=>String(s)):[],advanceMode:t.body?.advanceMode==="approval"?"approval":"on_success",stackDependentWork:t.body?.stackDependentWork!==!1,budgetCap:typeof t.body?.budgetCap=="number"?t.body.budgetCap:null});return n.status(201).json(r)}catch(r){return ee(n,r)}}),e.post("/v1/batches/:id/:action",async(t,n)=>{let{id:r,action:s}=t.params;try{return s==="start"?n.json(await Gt(r)):s==="pause"?n.json(await Ut(r)):s==="stop"?n.json(await qt(r)):n.status(400).json({error:"unknown action (use start | pause | stop)"})}catch(o){return ee(n,o)}}),e.delete("/v1/batches/:id",async(t,n)=>{try{return await Pn(t.params.id),n.json({ok:!0})}catch(r){return ee(n,r)}}),e}var tn=null;function ko(){return tn!==null}function $n(e){if(tn)return;tn=bc().listen(e,()=>{console.log(`[pipelines] public API ready http://localhost:${e}/v1`)})}async function yo(){let e=tn;e&&(tn=null,await new Promise(t=>e.close(()=>t())),console.log("[pipelines] public API stopped"))}var Bn=Number(process.env.PIPELINES_PUBLIC_API_PORT||63211),wo=4,Sc=/^[a-zA-Z0-9-]+$/,Ic=new Set(["host","content-length","content-type","user-agent","x-superclawd-event","x-superclawd-signature","transfer-encoding","connection"]),Ae=class extends Error{constructor(n,r){super(r);this.status=n;this.name="ApiSettingsError"}};function nn(){let e=l;return{publicApiEnabled:!!e.publicApiEnabled,port:Bn,running:ko(),apiKey:e.apiKeyHash?{last4:e.apiKeyLast4??"",createdAt:e.apiKeyCreatedAt??null}:null,webhook:{enabled:!!e.webhookEnabled,url:e.webhookUrl??"",headers:e.webhookHeaders??[],events:e.webhookEvents??[],hasSecret:!!e.webhookSecret}}}async function bo(e){return l.publicApiEnabled=e,await se(),e?$n(Bn):await yo(),nn()}async function So(){let e=mt("sck_",32);return l.apiKeyHash=ft(e),l.apiKeyLast4=e.slice(-4),l.apiKeyCreatedAt=new Date().toISOString(),await se(),{key:e}}async function Io(){return l.apiKeyHash=void 0,l.apiKeyLast4=void 0,l.apiKeyCreatedAt=void 0,await se(),nn()}async function xo(e){let t=l;if(e.url!==void 0){let n=String(e.url??"").trim();if(n&&!/^https?:\/\//i.test(n))throw new Ae(400,"webhook URL must start with http:// or https://");t.webhookUrl=n||void 0}if(e.headers!==void 0){let n=(e.headers??[]).filter(r=>r&&r.key);if(n.length>wo)throw new Ae(400,`at most ${wo} custom headers`);for(let r of n){if(!Sc.test(r.key)||r.key.length>64)throw new Ae(400,`invalid header name: ${r.key}`);if(Ic.has(r.key.toLowerCase()))throw new Ae(400,`header not allowed: ${r.key}`);if(String(r.value??"").length>512)throw new Ae(400,`header value too long: ${r.key}`)}t.webhookHeaders=n.map(r=>({key:r.key,value:String(r.value??"")}))}if(e.events!==void 0){let n=new Set(Mn);t.webhookEvents=(e.events??[]).filter(r=>n.has(r))}return e.enabled!==void 0&&(t.webhookEnabled=!!e.enabled),t.webhookEnabled&&!t.webhookSecret&&(t.webhookSecret=mt("whsec_",32)),await se(),nn()}async function vo(){let e=mt("whsec_",32);return l.webhookSecret=e,await se(),{secret:e}}async function Ro(){return Vs()}V();import Er from"node:fs";import vc from"node:os";import _o from"node:path";import{McpServer as Rc}from"@modelcontextprotocol/sdk/server/mcp.js";import{StreamableHTTPServerTransport as Tc}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{z as k}from"zod";var Ao=process.env.SUPERCLAWD_HOME||_o.join(vc.homedir(),".superclawd"),No=_o.join(Ao,"pipelines-mcp.json"),Ln=null;function Eo(){if(Ln)return Ln;try{let e=JSON.parse(Er.readFileSync(No,"utf8"));if(typeof e?.token=="string"&&e.token)return Ln=e.token}catch{}return Ln=mt("scpmcp_")}function _c(e){Er.mkdirSync(Ao,{recursive:!0}),Er.writeFileSync(No,JSON.stringify({port:e,token:Eo()}),{mode:384})}var To=e=>I[e]??"backlog";function Nr(e,t,n){let s=/^Bearer\s+(.+)$/i.exec(e.headers.authorization??"")?.[1]?.trim();if(s){if(Qt(s,Eo()))return n();let o=l.apiKeyHash;if(o&&Qt(ft(s),o))return n()}t.status(401).json({error:"invalid or missing MCP token"})}var _=e=>({content:[{type:"text",text:JSON.stringify(e,null,2)}]}),et=e=>({isError:!0,content:[{type:"text",text:e}]});function $(e){return e instanceof pe||e instanceof P||e instanceof q?et(e.message):(console.error("[pipelines-mcp]",e),et("internal error"))}function Ac(){let e=new Rc({name:"superclawd-pipelines",version:"0.1.0"});return e.tool("list_boards","List the connected boards (one per project-management connection in the active workspace).",{},async()=>_(b.map(t=>({id:t.id,provider:t.provider,workspaceId:t.workspaceId,workspaceName:t.workspaceName,linkStatus:t.linkStatus,ticketCount:t.ticketCount})))),e.tool("list_tickets","List the tickets on a board, with each ticket's current board column.",{boardId:k.string().describe("The board (connection) id from list_boards")},async({boardId:t})=>_(h.filter(n=>n.connectionId===t).map(n=>({...n,column:To(n.id)})))),e.tool("get_ticket","Get a single ticket (full fields + its current board column + its resolved dependencies).",{ticketId:k.string()},async({ticketId:t})=>{let n=Nn(t);return n?_({...n,column:To(n.id),...En(n)}):et("ticket not found")}),e.tool("create_ticket","Create a ticket on a Local board (vendor boards import tickets, so boardId must be a local board \u2014 see list_boards). New tickets land in Backlog.",{boardId:k.string().describe("A local board id from list_boards"),title:k.string(),description:k.string().optional(),priority:k.number().int().min(0).max(4).optional().describe("0 none \xB7 1 urgent \xB7 2 high \xB7 3 medium \xB7 4 low")},async({boardId:t,title:n,description:r,priority:s})=>{try{return _(await Ct(t,{title:n,description:r,priority:s}))}catch(o){return $(o)}}),e.tool("update_ticket","Update a ticket's editable fields. title/description/priority apply to Local-board tickets only; workingDir/model/effort apply to any ticket. (Use set_plan/set_conclusion/block_ticket for those.)",{ticketId:k.string(),title:k.string().optional(),description:k.string().optional(),priority:k.number().int().min(0).max(4).optional(),workingDir:k.string().optional(),model:k.string().optional(),effort:k.string().optional()},async({ticketId:t,...n})=>{let r={};for(let[s,o]of Object.entries(n))o!==void 0&&(r[s]=o);try{return _(await Te(t,r))}catch(s){return $(s)}}),e.tool("move_ticket","Move a ticket to a board column (backlog | in_progress | needs_input | in_review | done). Blocked tickets can't move until unblocked; Done requires a stopped run.",{ticketId:k.string(),column:k.string()},async({ticketId:t,column:n})=>{try{return _(await Nt(t,n))}catch(r){return $(r)}}),e.tool("add_note","Add a freeform note to a ticket.",{ticketId:k.string(),body:k.string()},async({ticketId:t,body:n})=>{try{return _(await Pt(t,{body:n}))}catch(r){return $(r)}}),e.tool("list_notes","List a ticket's notes (newest first), each with its id + author.",{ticketId:k.string()},async({ticketId:t})=>_(_t(t))),e.tool("edit_note","Edit a note's body.",{noteId:k.string(),body:k.string()},async({noteId:t,body:n})=>{try{return _(await Ot(t,n))}catch(r){return $(r)}}),e.tool("delete_note","Delete a note.",{noteId:k.string()},async({noteId:t})=>{try{return await Dt(t),_({ok:!0})}catch(n){return $(n)}}),e.tool("set_plan","Set (replace) a ticket's plan \u2014 the intended approach for the task (markdown).",{ticketId:k.string(),plan:k.string()},async({ticketId:t,plan:n})=>{try{return _(await Te(t,{plan:n}))}catch(r){return $(r)}}),e.tool("set_conclusion","Set (replace) a ticket's conclusion \u2014 the wrap-up / findings (markdown).",{ticketId:k.string(),conclusion:k.string()},async({ticketId:t,conclusion:n})=>{try{return _(await Te(t,{conclusion:n}))}catch(r){return $(r)}}),e.tool("block_ticket","Block a ticket with a reason \u2014 moves it to the Blocked lane until it's unblocked.",{ticketId:k.string(),reason:k.string()},async({ticketId:t,reason:n})=>{try{return _(await Te(t,{blockedReason:n}))}catch(r){return $(r)}}),e.tool("unblock_ticket","Unblock a ticket. Set resume=true to resume its saved run; otherwise it returns to Backlog. Fails if the ticket has unresolved open questions.",{ticketId:k.string(),resume:k.boolean().optional()},async({ticketId:t,resume:n})=>{try{return _(await Et(t,{resume:n===!0}))}catch(r){return $(r)}}),e.tool("set_dependencies","Set (replace) the tickets a ticket depends on \u2014 the ordering graph a batch uses to decide what runs before what. Pass the FULL desired list of blocker ticket ids on the same board; it replaces the current set (get_ticket shows the current dependsOn; pass [] to clear). Blockers must be on the same board and a cycle is rejected.",{ticketId:k.string(),dependsOn:k.array(k.string())},async({ticketId:t,dependsOn:n})=>{try{let r=await Cn(t,n);return await jn(t),_(r)}catch(r){return $(r)}}),e.tool("add_question","Add an open question to a ticket \u2014 a decision or blocker that needs a human answer.",{ticketId:k.string(),question:k.string()},async({ticketId:t,question:n})=>{try{return _(await jt(t,n))}catch(r){return $(r)}}),e.tool("list_questions","List a ticket's open questions (each with its answer + resolved state).",{ticketId:k.string()},async({ticketId:t})=>_(At(t))),e.tool("update_question","Update an open question \u2014 set its answer, mark it resolved (or reopen it), and/or edit its text. All open questions must be resolved before a ticket can be unblocked.",{questionId:k.string(),question:k.string().optional(),answer:k.string().optional(),resolved:k.boolean().optional()},async({questionId:t,question:n,answer:r,resolved:s})=>{let o={};n!==void 0&&(o.question=n),r!==void 0&&(o.answer=r),s!==void 0&&(o.resolved=s);try{return _(await Mt(t,o))}catch(i){return $(i)}}),e.tool("delete_question","Delete an open question from a ticket.",{questionId:k.string()},async({questionId:t})=>{try{return await $t(t),_({ok:!0})}catch(n){return $(n)}}),e.tool("start_run","Start (or resume) a run on a ticket. `autonomy` (supervised | autonomous) applies only when a run doesn't exist yet; `details` is optional calibration text.",{ticketId:k.string(),details:k.string().optional(),autonomy:k.enum(["supervised","autonomous"]).optional()},async({ticketId:t,details:n,autonomy:r})=>{try{let s=await xt(t,r?{autonomy:r}:{});return _(await qe(s.id,{details:n??"",attachments:[]}))}catch(s){return $(s)}}),e.tool("pause_run","Pause the active run on a ticket \u2014 kills the process but keeps the session resumable.",{ticketId:k.string()},async({ticketId:t})=>{let n=J(t);if(!n)return et("no run for this ticket");try{return _(await vt(n.id))}catch(r){return $(r)}}),e.tool("stop_run","Stop the active run on a ticket.",{ticketId:k.string()},async({ticketId:t})=>{let n=J(t);if(!n)return et("no run for this ticket");try{return _(await Fe(n.id))}catch(r){return $(r)}}),e.tool("resume_run","Resume a stopped or paused run on a ticket (re-enters the same session).",{ticketId:k.string()},async({ticketId:t})=>{let n=J(t);if(!n)return et("no run for this ticket");try{return _(await Rt(n.id))}catch(r){return $(r)}}),e.tool("get_run_status","Get the current run for a ticket (session status, stop reason, model/effort, latest result).",{ticketId:k.string()},async({ticketId:t})=>{let n=J(t);return n?_(n):et("no run for this ticket")}),e.tool("list_batches","List the batches (sets of assigned tickets that run unattended), with each batch's status.",{},async()=>_(Bt())),e.tool("get_batch","Get a batch and each member ticket's live state (done | running | blocked | failed | waiting | queued) plus unmet dependencies.",{batchId:k.string()},async({batchId:t})=>{try{return _(Lt(t))}catch(n){return $(n)}}),e.tool("create_batch","Create a batch from a set of already-assigned tickets (each must be assigned and share one workspace). Ordered vs. unordered is derived from the tickets' dependencies. Does NOT start it \u2014 call start_batch.",{name:k.string(),ticketIds:k.array(k.string()).describe("The assigned tickets to batch"),advanceMode:k.enum(["on_success","approval"]).optional().describe("on_success (default): a member reaching In Review unblocks the next. approval: dependents wait for Done."),stackDependentWork:k.boolean().optional().describe("Ordered batches only: a dependent member branches off its blocker's branch (default true)."),budgetCap:k.number().int().min(1).optional().describe("Pause the batch after this many runs.")},async({name:t,ticketIds:n,advanceMode:r,stackDependentWork:s,budgetCap:o})=>{try{return _(await Ze({name:t,ticketIds:n,advanceMode:r,stackDependentWork:s,budgetCap:o??null}))}catch(i){return $(i)}}),e.tool("start_batch","Play (or resume) a batch \u2014 starts its ready members autonomously. If another batch is already running, this one queues and starts when the slot frees.",{batchId:k.string()},async({batchId:t})=>{try{return _(await Gt(t))}catch(n){return $(n)}}),e.tool("pause_batch","Pause a running batch: stop starting new members (in-flight runs finish on their own).",{batchId:k.string()},async({batchId:t})=>{try{return _(await Ut(t))}catch(n){return $(n)}}),e.tool("stop_batch","Stop a batch and kill any of its still-running members (resets it to idle so it can be played again).",{batchId:k.string()},async({batchId:t})=>{try{return _(await qt(t))}catch(n){return $(n)}}),e}function Co(e,t){_c(t),e.post("/mcp",Nr,async(r,s)=>{let o=Ac(),i=new Tc({sessionIdGenerator:void 0});s.on("close",()=>{i.close(),o.close()});try{await o.connect(i),await i.handleRequest(r,s,r.body)}catch(c){console.error("[pipelines-mcp] request error",c),s.headersSent||s.status(500).json({error:"internal error"})}});let n=(r,s)=>s.status(405).json({error:"method not allowed"});e.get("/mcp",Nr,n),e.delete("/mcp",Nr,n),console.log("[pipelines] MCP endpoint ready at /mcp")}V();import{randomUUID as Oc}from"node:crypto";import{homedir as Nc}from"node:os";var Gn=["You are running in a RESTRICTED context (a schedule or conversation), not a board task.","You may read, investigate, analyze, run read-only commands, write non-code files (reports, notes) in non-git folders, and file board tickets with create_ticket.","You must NOT modify code or write into any git repository \u2014 there is no worktree here and repo writes are blocked. If a code change is warranted, create a board ticket describing exactly what to change (which repo, files, and why); the work happens on the board."].join(`
|
|
66
|
-
`);async function
|
|
65
|
+
</html>`,ho=e=>S[e]??"backlog";function wc(e,t,n){let r=l.apiKeyHash;if(!r){t.status(401).json({error:"public API key is not configured"});return}let o=/^Bearer\s+(.+)$/i.exec(e.headers.authorization??"")?.[1]?.trim();if(!o||!Jt(ft(o),r)){t.status(401).json({error:"invalid or missing API key"});return}n()}function ne(e,t){return t instanceof pe?e.status(t.status).json({error:t.message,...t.code?{code:t.code}:{},...t.extra??{}}):t instanceof P?e.status(t.status).json({error:t.message,...t.extra??{}}):t instanceof q?e.status(t.status).json({error:t.message,...t.code?{code:t.code}:{},...t.extra??{}}):(console.error("[public-api]",t),e.status(500).json({error:"internal error"}))}function bc(e){let t=Cn(e);return t?{...t,column:ho(t.id)}:null}function Ic(){let e=mo();return e.use(fc()),e.use(mo.json()),e.get("/openapi.json",(t,n)=>n.json(fo)),e.get("/docs/scalar.js",(t,n)=>n.sendFile(kc)),e.get("/docs",(t,n)=>n.type("html").send(yc)),e.use("/v1",wc),e.get("/v1/boards",(t,n)=>{n.json(b.map(r=>({id:r.id,provider:r.provider,workspaceId:r.workspaceId,workspaceName:r.workspaceName,linkStatus:r.linkStatus,ticketCount:r.ticketCount})))}),e.get("/v1/boards/:id/tickets",(t,n)=>{let r=h.filter(s=>s.connectionId===t.params.id).map(s=>({...s,column:ho(s.id)}));n.json(r)}),e.post("/v1/boards/:id/tickets",async(t,n)=>{try{return n.status(201).json(await Pt(t.params.id,t.body??{}))}catch(r){return ne(n,r)}}),e.get("/v1/tickets/:id",(t,n)=>{let r=bc(t.params.id);return r?n.json(r):n.status(404).json({error:"ticket not found"})}),e.post("/v1/tickets/:id/move",async(t,n)=>{try{return n.json(await Et(t.params.id,t.body?.column))}catch(r){return ne(n,r)}}),e.patch("/v1/tickets/:id",async(t,n)=>{try{return n.json(await Ae(t.params.id,t.body??{}))}catch(r){return ne(n,r)}}),e.post("/v1/tickets/:id/unblock",async(t,n)=>{try{return n.json(await Ct(t.params.id,{resume:t.body?.resume===!0}))}catch(r){return ne(n,r)}}),e.get("/v1/tickets/:id/notes",(t,n)=>{n.json(At(t.params.id))}),e.post("/v1/tickets/:id/notes",async(t,n)=>{try{let r=await Ot(t.params.id,{body:String(t.body?.body??""),author:t.body?.author});return n.status(201).json(r)}catch(r){return ne(n,r)}}),e.patch("/v1/notes/:id",async(t,n)=>{try{return n.json(await Dt(t.params.id,String(t.body?.body??"")))}catch(r){return ne(n,r)}}),e.delete("/v1/notes/:id",async(t,n)=>{try{return await jt(t.params.id),n.json({ok:!0})}catch(r){return ne(n,r)}}),e.get("/v1/tickets/:id/questions",(t,n)=>{n.json(Nt(t.params.id))}),e.post("/v1/tickets/:id/questions",async(t,n)=>{try{let r=await Mt(t.params.id,String(t.body?.question??""));return n.status(201).json(r)}catch(r){return ne(n,r)}}),e.patch("/v1/questions/:id",async(t,n)=>{try{return n.json(await Bt(t.params.id,t.body??{}))}catch(r){return ne(n,r)}}),e.delete("/v1/questions/:id",async(t,n)=>{try{return await $t(t.params.id),n.json({ok:!0})}catch(r){return ne(n,r)}}),e.get("/v1/tickets/:id/run",(t,n)=>{let r=J(t.params.id);return r?n.json(r):n.status(404).json({error:"no run for this ticket"})}),e.post("/v1/tickets/:id/run/:action",async(t,n)=>{let r=t.params.action;try{if(r==="start"){let o=t.body?.autonomy,i=await vt(t.params.id,o==="supervised"||o==="autonomous"?{autonomy:o}:{});return n.json(await qe(i.id,{details:String(t.body?.details??""),attachments:Array.isArray(t.body?.attachments)?t.body.attachments.map(c=>String(c)):[]}))}let s=J(t.params.id);return s?r==="pause"?n.json(await Rt(s.id)):r==="resume"?n.json(await Tt(s.id)):r==="stop"?n.json(await Fe(s.id)):n.status(400).json({error:"unknown action (use start | pause | resume | stop)"}):n.status(404).json({error:"no run for this ticket"})}catch(s){return ne(n,s)}}),e.get("/v1/batches",(t,n)=>{n.json(Lt())}),e.get("/v1/batches/:id",(t,n)=>{try{return n.json(Gt(t.params.id))}catch(r){return ne(n,r)}}),e.post("/v1/batches",async(t,n)=>{try{let r=await Ze({name:String(t.body?.name??""),ticketIds:Array.isArray(t.body?.ticketIds)?t.body.ticketIds.map(s=>String(s)):[],advanceMode:t.body?.advanceMode==="approval"?"approval":"on_success",stackDependentWork:t.body?.stackDependentWork!==!1,budgetCap:typeof t.body?.budgetCap=="number"?t.body.budgetCap:null});return n.status(201).json(r)}catch(r){return ne(n,r)}}),e.post("/v1/batches/:id/:action",async(t,n)=>{let{id:r,action:s}=t.params;try{return s==="start"?n.json(await Ut(r)):s==="pause"?n.json(await qt(r)):s==="stop"?n.json(await Ft(r)):n.status(400).json({error:"unknown action (use start | pause | stop)"})}catch(o){return ne(n,o)}}),e.delete("/v1/batches/:id",async(t,n)=>{try{return await Dn(t.params.id),n.json({ok:!0})}catch(r){return ne(n,r)}}),e}var nn=null;function ko(){return nn!==null}function $n(e){if(nn)return;nn=Ic().listen(e,()=>{console.log(`[pipelines] public API ready http://localhost:${e}/v1`)})}async function yo(){let e=nn;e&&(nn=null,await new Promise(t=>e.close(()=>t())),console.log("[pipelines] public API stopped"))}var Ln=Number(process.env.PIPELINES_PUBLIC_API_PORT||63211),wo=4,Sc=/^[a-zA-Z0-9-]+$/,xc=new Set(["host","content-length","content-type","user-agent","x-superclawd-event","x-superclawd-signature","transfer-encoding","connection"]),Ee=class extends Error{constructor(n,r){super(r);this.status=n;this.name="ApiSettingsError"}};function rn(){let e=l;return{publicApiEnabled:!!e.publicApiEnabled,port:Ln,running:ko(),apiKey:e.apiKeyHash?{last4:e.apiKeyLast4??"",createdAt:e.apiKeyCreatedAt??null}:null,webhook:{enabled:!!e.webhookEnabled,url:e.webhookUrl??"",headers:e.webhookHeaders??[],events:e.webhookEvents??[],hasSecret:!!e.webhookSecret}}}async function bo(e){return l.publicApiEnabled=e,await te(),e?$n(Ln):await yo(),rn()}async function Io(){let e=mt("sck_",32);return l.apiKeyHash=ft(e),l.apiKeyLast4=e.slice(-4),l.apiKeyCreatedAt=new Date().toISOString(),await te(),{key:e}}async function So(){return l.apiKeyHash=void 0,l.apiKeyLast4=void 0,l.apiKeyCreatedAt=void 0,await te(),rn()}async function xo(e){let t=l;if(e.url!==void 0){let n=String(e.url??"").trim();if(n&&!/^https?:\/\//i.test(n))throw new Ee(400,"webhook URL must start with http:// or https://");t.webhookUrl=n||void 0}if(e.headers!==void 0){let n=(e.headers??[]).filter(r=>r&&r.key);if(n.length>wo)throw new Ee(400,`at most ${wo} custom headers`);for(let r of n){if(!Sc.test(r.key)||r.key.length>64)throw new Ee(400,`invalid header name: ${r.key}`);if(xc.has(r.key.toLowerCase()))throw new Ee(400,`header not allowed: ${r.key}`);if(String(r.value??"").length>512)throw new Ee(400,`header value too long: ${r.key}`)}t.webhookHeaders=n.map(r=>({key:r.key,value:String(r.value??"")}))}if(e.events!==void 0){let n=new Set(Bn);t.webhookEvents=(e.events??[]).filter(r=>n.has(r))}return e.enabled!==void 0&&(t.webhookEnabled=!!e.enabled),t.webhookEnabled&&!t.webhookSecret&&(t.webhookSecret=mt("whsec_",32)),await te(),rn()}async function vo(){let e=mt("whsec_",32);return l.webhookSecret=e,await te(),{secret:e}}async function Ro(){return Vs()}Z();import Er from"node:fs";import Rc from"node:os";import _o from"node:path";import{McpServer as Tc}from"@modelcontextprotocol/sdk/server/mcp.js";import{StreamableHTTPServerTransport as _c}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{z as y}from"zod";var Ao=process.env.SUPERCLAWD_HOME||_o.join(Rc.homedir(),".superclawd"),No=_o.join(Ao,"pipelines-mcp.json"),Gn=null;function Eo(){if(Gn)return Gn;try{let e=JSON.parse(Er.readFileSync(No,"utf8"));if(typeof e?.token=="string"&&e.token)return Gn=e.token}catch{}return Gn=mt("scpmcp_")}function Ac(e){Er.mkdirSync(Ao,{recursive:!0}),Er.writeFileSync(No,JSON.stringify({port:e,token:Eo()}),{mode:384})}var To=e=>S[e]??"backlog";function Nr(e,t,n){let s=/^Bearer\s+(.+)$/i.exec(e.headers.authorization??"")?.[1]?.trim();if(s){if(Jt(s,Eo()))return n();let o=l.apiKeyHash;if(o&&Jt(ft(s),o))return n()}t.status(401).json({error:"invalid or missing MCP token"})}var _=e=>({content:[{type:"text",text:JSON.stringify(e,null,2)}]}),et=e=>({isError:!0,content:[{type:"text",text:e}]});function B(e){return e instanceof pe||e instanceof P||e instanceof q?et(e.message):(console.error("[pipelines-mcp]",e),et("internal error"))}function Nc(){let e=new Tc({name:"superclawd-pipelines",version:"0.1.0"});return e.tool("list_boards","List the connected boards (one per project-management connection in the active workspace).",{},async()=>_(b.map(t=>({id:t.id,provider:t.provider,workspaceId:t.workspaceId,workspaceName:t.workspaceName,linkStatus:t.linkStatus,ticketCount:t.ticketCount})))),e.tool("list_tickets","List the tickets on a board, with each ticket's current board column.",{boardId:y.string().describe("The board (connection) id from list_boards")},async({boardId:t})=>_(h.filter(n=>n.connectionId===t).map(n=>({...n,column:To(n.id)})))),e.tool("get_ticket","Get a single ticket (full fields + its current board column + its resolved dependencies).",{ticketId:y.string()},async({ticketId:t})=>{let n=Cn(t);return n?_({...n,column:To(n.id),...Pn(n)}):et("ticket not found")}),e.tool("create_ticket","Create a ticket on a Local board (vendor boards import tickets, so boardId must be a local board \u2014 see list_boards). New tickets land in Backlog.",{boardId:y.string().describe("A local board id from list_boards"),title:y.string(),description:y.string().optional(),priority:y.number().int().min(0).max(4).optional().describe("0 none \xB7 1 urgent \xB7 2 high \xB7 3 medium \xB7 4 low")},async({boardId:t,title:n,description:r,priority:s})=>{try{return _(await Pt(t,{title:n,description:r,priority:s}))}catch(o){return B(o)}}),e.tool("update_ticket","Update a ticket's editable fields. title/description/priority apply to Local-board tickets only; workingDir/model/effort apply to any ticket. (Use set_plan/set_conclusion/block_ticket for those.)",{ticketId:y.string(),title:y.string().optional(),description:y.string().optional(),priority:y.number().int().min(0).max(4).optional(),workingDir:y.string().optional(),model:y.string().optional(),effort:y.string().optional()},async({ticketId:t,...n})=>{let r={};for(let[s,o]of Object.entries(n))o!==void 0&&(r[s]=o);try{return _(await Ae(t,r))}catch(s){return B(s)}}),e.tool("move_ticket","Move a ticket to a board column (backlog | in_progress | needs_input | in_review | done). Blocked tickets can't move until unblocked; Done requires a stopped run.",{ticketId:y.string(),column:y.string()},async({ticketId:t,column:n})=>{try{return _(await Et(t,n))}catch(r){return B(r)}}),e.tool("add_note","Add a freeform note to a ticket.",{ticketId:y.string(),body:y.string()},async({ticketId:t,body:n})=>{try{return _(await Ot(t,{body:n}))}catch(r){return B(r)}}),e.tool("list_notes","List a ticket's notes (newest first), each with its id + author.",{ticketId:y.string()},async({ticketId:t})=>_(At(t))),e.tool("edit_note","Edit a note's body.",{noteId:y.string(),body:y.string()},async({noteId:t,body:n})=>{try{return _(await Dt(t,n))}catch(r){return B(r)}}),e.tool("delete_note","Delete a note.",{noteId:y.string()},async({noteId:t})=>{try{return await jt(t),_({ok:!0})}catch(n){return B(n)}}),e.tool("set_plan","Set (replace) a ticket's plan \u2014 the intended approach for the task (markdown).",{ticketId:y.string(),plan:y.string()},async({ticketId:t,plan:n})=>{try{return _(await Ae(t,{plan:n}))}catch(r){return B(r)}}),e.tool("set_conclusion","Set (replace) a ticket's conclusion \u2014 the wrap-up / findings (markdown).",{ticketId:y.string(),conclusion:y.string()},async({ticketId:t,conclusion:n})=>{try{return _(await Ae(t,{conclusion:n}))}catch(r){return B(r)}}),e.tool("block_ticket","Block a ticket with a reason \u2014 moves it to the Blocked lane until it's unblocked.",{ticketId:y.string(),reason:y.string()},async({ticketId:t,reason:n})=>{try{return _(await Ae(t,{blockedReason:n}))}catch(r){return B(r)}}),e.tool("unblock_ticket","Unblock a ticket. Set resume=true to resume its saved run; otherwise it returns to Backlog. Fails if the ticket has unresolved open questions.",{ticketId:y.string(),resume:y.boolean().optional()},async({ticketId:t,resume:n})=>{try{return _(await Ct(t,{resume:n===!0}))}catch(r){return B(r)}}),e.tool("set_dependencies","Set (replace) the tickets a ticket depends on \u2014 the ordering graph a batch uses to decide what runs before what. Pass the FULL desired list of blocker ticket ids on the same board; it replaces the current set (get_ticket shows the current dependsOn; pass [] to clear). Blockers must be on the same board and a cycle is rejected.",{ticketId:y.string(),dependsOn:y.array(y.string())},async({ticketId:t,dependsOn:n})=>{try{let r=await On(t,n);return await Mn(t),_(r)}catch(r){return B(r)}}),e.tool("add_question","Add an open question to a ticket \u2014 a decision or blocker that needs a human answer.",{ticketId:y.string(),question:y.string()},async({ticketId:t,question:n})=>{try{return _(await Mt(t,n))}catch(r){return B(r)}}),e.tool("list_questions","List a ticket's open questions (each with its answer + resolved state).",{ticketId:y.string()},async({ticketId:t})=>_(Nt(t))),e.tool("update_question","Update an open question \u2014 set its answer, mark it resolved (or reopen it), and/or edit its text. All open questions must be resolved before a ticket can be unblocked.",{questionId:y.string(),question:y.string().optional(),answer:y.string().optional(),resolved:y.boolean().optional()},async({questionId:t,question:n,answer:r,resolved:s})=>{let o={};n!==void 0&&(o.question=n),r!==void 0&&(o.answer=r),s!==void 0&&(o.resolved=s);try{return _(await Bt(t,o))}catch(i){return B(i)}}),e.tool("delete_question","Delete an open question from a ticket.",{questionId:y.string()},async({questionId:t})=>{try{return await $t(t),_({ok:!0})}catch(n){return B(n)}}),e.tool("start_run","Start (or resume) a run on a ticket. `autonomy` (supervised | autonomous) applies only when a run doesn't exist yet; `details` is optional calibration text.",{ticketId:y.string(),details:y.string().optional(),autonomy:y.enum(["supervised","autonomous"]).optional()},async({ticketId:t,details:n,autonomy:r})=>{try{let s=await vt(t,r?{autonomy:r}:{});return _(await qe(s.id,{details:n??"",attachments:[]}))}catch(s){return B(s)}}),e.tool("pause_run","Pause the active run on a ticket \u2014 kills the process but keeps the session resumable.",{ticketId:y.string()},async({ticketId:t})=>{let n=J(t);if(!n)return et("no run for this ticket");try{return _(await Rt(n.id))}catch(r){return B(r)}}),e.tool("stop_run","Stop the active run on a ticket.",{ticketId:y.string()},async({ticketId:t})=>{let n=J(t);if(!n)return et("no run for this ticket");try{return _(await Fe(n.id))}catch(r){return B(r)}}),e.tool("resume_run","Resume a stopped or paused run on a ticket (re-enters the same session).",{ticketId:y.string()},async({ticketId:t})=>{let n=J(t);if(!n)return et("no run for this ticket");try{return _(await Tt(n.id))}catch(r){return B(r)}}),e.tool("get_run_status","Get the current run for a ticket (session status, stop reason, model/effort, latest result).",{ticketId:y.string()},async({ticketId:t})=>{let n=J(t);return n?_(n):et("no run for this ticket")}),e.tool("list_batches","List the batches (sets of assigned tickets that run unattended), with each batch's status.",{},async()=>_(Lt())),e.tool("get_batch","Get a batch and each member ticket's live state (done | running | blocked | failed | waiting | queued) plus unmet dependencies.",{batchId:y.string()},async({batchId:t})=>{try{return _(Gt(t))}catch(n){return B(n)}}),e.tool("create_batch","Create a batch from a set of already-assigned tickets (each must be assigned and share one workspace). Ordered vs. unordered is derived from the tickets' dependencies. Does NOT start it \u2014 call start_batch.",{name:y.string(),ticketIds:y.array(y.string()).describe("The assigned tickets to batch"),advanceMode:y.enum(["on_success","approval"]).optional().describe("on_success (default): a member reaching In Review unblocks the next. approval: dependents wait for Done."),stackDependentWork:y.boolean().optional().describe("Ordered batches only: a dependent member branches off its blocker's branch (default true)."),budgetCap:y.number().int().min(1).optional().describe("Pause the batch after this many runs.")},async({name:t,ticketIds:n,advanceMode:r,stackDependentWork:s,budgetCap:o})=>{try{return _(await Ze({name:t,ticketIds:n,advanceMode:r,stackDependentWork:s,budgetCap:o??null}))}catch(i){return B(i)}}),e.tool("start_batch","Play (or resume) a batch \u2014 starts its ready members autonomously. If another batch is already running, this one queues and starts when the slot frees.",{batchId:y.string()},async({batchId:t})=>{try{return _(await Ut(t))}catch(n){return B(n)}}),e.tool("pause_batch","Pause a running batch: stop starting new members (in-flight runs finish on their own).",{batchId:y.string()},async({batchId:t})=>{try{return _(await qt(t))}catch(n){return B(n)}}),e.tool("stop_batch","Stop a batch and kill any of its still-running members (resets it to idle so it can be played again).",{batchId:y.string()},async({batchId:t})=>{try{return _(await Ft(t))}catch(n){return B(n)}}),e}function Co(e,t){Ac(t),e.post("/mcp",Nr,async(r,s)=>{let o=Nc(),i=new _c({sessionIdGenerator:void 0});s.on("close",()=>{i.close(),o.close()});try{await o.connect(i),await i.handleRequest(r,s,r.body)}catch(c){console.error("[pipelines-mcp] request error",c),s.headersSent||s.status(500).json({error:"internal error"})}});let n=(r,s)=>s.status(405).json({error:"method not allowed"});e.get("/mcp",Nr,n),e.delete("/mcp",Nr,n),console.log("[pipelines] MCP endpoint ready at /mcp")}Z();import{randomUUID as Dc}from"node:crypto";import{homedir as Ec}from"node:os";var Un=["You are running in a RESTRICTED context (a schedule or conversation), not a board task.","You may read, investigate, analyze, run read-only commands, write non-code files (reports, notes) in non-git folders, and file board tickets with create_ticket.","You must NOT modify code or write into any git repository \u2014 there is no worktree here and repo writes are blocked. If a code change is warranted, create a board ticket describing exactly what to change (which repo, files, and why); the work happens on the board."].join(`
|
|
66
|
+
`);async function qn(e){if(!e)return[];let t=Vt(e);return t===Ec()?[]:(await Ge(t)).map(n=>n.path)}var Pc={min:6e4,minute:6e4,minutes:6e4,hour:36e5,hours:36e5,hr:36e5,hrs:36e5,day:864e5,days:864e5,week:6048e5,weeks:6048e5};function Oc(e){let t=e.trim().toLowerCase();if(t==="hourly")return 36e5;if(t==="daily")return 864e5;if(t==="weekly")return 6048e5;let n=/every\s+(\d+)?\s*([a-z]+)/.exec(t);if(n){let r=n[1]?parseInt(n[1],10):1,s=Pc[n[2]];if(s&&r>0)return Math.max(6e4,r*s)}return 36e5}function sn(e,t){return new Date(e+Oc(t)).toISOString()}var jc=3e4,Fn=new Set,Cr=null;async function Pr(e,t=!0){let n=O[e];if(!n||Fn.has(e))return null;Fn.add(e);let r=++G.schedRunSeq,s={id:`srun_${r}`,scheduleId:e,status:"running",sessionId:Dc(),startedAt:new Date().toISOString(),finishedAt:null,output:"",model:n.model||l.defaultModel||ke,effort:n.effort||l.defaultEffort||ye};return(z[e]??=[]).unshift(s),n.lastRunAt=s.startedAt,t&&(n.nextRunAt=sn(Date.now(),n.cadence)),await gn(s,r),await zt(e),le("schedule.run.started",{scheduleId:e,scheduleRunId:s.id,scheduleName:n.name,status:s.status},{workspaceId:n.workspaceId}),Mc(n,s,r),s}async function Mc(e,t,n){try{if(E())t.output=`Ran "${e.name}" on its ${e.cadence} cadence. (mock output)`,t.status="success";else{let r=e.workingDir??l.workspaceDirs?.[e.workspaceId]??"~",s=await Ye({prompt:`${Un}
|
|
67
67
|
|
|
68
|
-
${e.prompt}`,workspace:e.workspaceId,workingDir:r,model:t.model,effort:t.effort,sessionId:t.sessionId,pipelinesTools:!0,pipelinesToolsMode:"restricted",sandbox:!0,sandboxRepos:await
|
|
68
|
+
${e.prompt}`,workspace:e.workspaceId,workingDir:r,model:t.model,effort:t.effort,sessionId:t.sessionId,pipelinesTools:!0,pipelinesToolsMode:"restricted",sandbox:!0,sandboxRepos:await qn(r)}),o=s.code===0?In(t.sessionId):null;t.output=(o??(s.code===0?s.stdout:s.stderr||s.stdout)).trim().slice(0,8e3),t.status=s.code===0?"success":"failed"}}catch(r){t.status="failed",t.output=String(r)}finally{t.finishedAt=new Date().toISOString(),await gn(t,n),Fn.delete(e.id),le(t.status==="success"?"schedule.run.completed":"schedule.run.failed",{scheduleId:e.id,scheduleRunId:t.id,scheduleName:e.name,status:t.status},{workspaceId:e.workspaceId})}}async function Bc(){let e=Date.now(),t=l.connectedWorkspaces,n=Object.values(O).filter(r=>r.enabled&&r.nextRunAt&&Date.parse(r.nextRunAt)<=e&&!Fn.has(r.id)&&(!t||t.includes(r.workspaceId)));for(let r of n)Pr(r.id,!0)}function Po(){Cr||(Cr=setInterval(()=>void Bc(),jc),Cr.unref?.())}async function Oo(){let e=0;for(let t of Object.values(z))for(let n of t)n.status==="running"&&(n.status="failed",n.finishedAt=new Date().toISOString(),n.output=n.output||"interrupted by daemon restart",await gn(n,Number(n.id.replace(/^srun_/,""))||0),e++);e&&console.log(`[pipelines] reconciled ${e} interrupted schedule run(s)`)}async function Wn(e,t,n){let r=new Array(e.length),s=0,o=async()=>{for(;s<e.length;){let c=s++;r[c]=await n(e[c],c)}},i=Math.max(1,Math.min(t,e.length));return await Promise.all(Array.from({length:i},()=>o())),r}function we(e,t){console.warn(`[pipelines] ${e} discovery hit the ~${t}-ticket page cap; older tickets may be absent from the import pool \u2014 narrow the connection's sync filters to surface them.`)}function $c(e){return new Promise(t=>setTimeout(t,e))}async function ae(e,t){let n=await fetch(e,t);if(n.status!==429)return n;let r=Number(n.headers.get("retry-after")),s=Number.isFinite(r)?Math.min(r*1e3,1e4):1e3;return await $c(s),fetch(e,t)}var Do="https://api.linear.app/graphql";function Lc(e){switch(e){case"triage":return"triage";case"backlog":return"backlog";case"unstarted":return"todo";case"started":return"in_progress";case"completed":return"done";case"canceled":return"canceled";default:return"todo"}}function Or(e,t,n){let r=typeof e.priority=="number"?e.priority:void 0;return{id:`lin_${e.id}`,connectionId:t,provider:"linear",externalIdentifier:e.identifier,title:e.title,description:e.description??void 0,status:Lc(e.state?.type),statusRaw:e.state?.name??"",assignee:e.assignee?.name?{name:e.assignee.name,avatarUrl:e.assignee.avatarUrl??void 0}:void 0,reporter:e.creator?.name?{name:e.creator.name,avatarUrl:e.creator.avatarUrl??void 0}:void 0,dueDate:e.dueDate??void 0,priority:r!==void 0&&r>=0&&r<=4?r:void 0,labels:(e.labels?.nodes??[]).map(s=>({name:s.name,color:s.color??void 0})),project:e.project?.name,parent:e.parent?.identifier?{identifier:e.parent.identifier,title:e.parent.title??""}:void 0,url:e.url,workspaceId:n,createdAt:e.createdAt,updatedAt:e.updatedAt}}function jo(e,t,n,r,s){let o=new Map(r.filter(u=>u.connectionId===n).map(u=>[u.externalIdentifier,u])),i=[];for(let u of t){let p=o.get(u.externalIdentifier);p&&(p.title=u.title,p.description=u.description,p.comments=u.comments??p.comments,p.status=u.status,p.statusRaw=u.statusRaw,p.assignee=u.assignee,p.reporter=u.reporter,p.dueDate=u.dueDate,p.priority=u.priority,p.labels=u.labels,p.project=u.project,p.parent=u.parent,p.url=u.url,p.createdAt=u.createdAt,p.updatedAt=u.updatedAt,i.push(p.id))}let c=e.filter(u=>!o.has(u.externalIdentifier)),d=s.filter(u=>u.connectionId!==n);return s.length=0,s.push(...d,...c),{refreshed:i,pool:c}}var Mo=`id identifier title description url priority createdAt updatedAt dueDate
|
|
69
69
|
state { name type } assignee { name avatarUrl } creator { name avatarUrl } project { name }
|
|
70
70
|
parent { identifier title }
|
|
71
|
-
labels { nodes { name color } }`,
|
|
71
|
+
labels { nodes { name color } }`,Gc=`query Issues($filter: IssueFilter, $after: String) {
|
|
72
72
|
issues(first: 100, filter: $filter, after: $after) {
|
|
73
73
|
nodes { ${Mo} }
|
|
74
74
|
pageInfo { hasNextPage endCursor }
|
|
75
|
-
} }`,
|
|
76
|
-
issues(first: 250, filter: { id: { in: $ids } }) { nodes { ${Mo} } } }`;function
|
|
75
|
+
} }`,Uc=`query IssuesById($ids: [ID!]) {
|
|
76
|
+
issues(first: 250, filter: { id: { in: $ids } }) { nodes { ${Mo} } } }`;function qc(e){let t={state:{type:{neq:"canceled"}}},n=(e?.labels??[]).map(r=>r.trim()).filter(Boolean);if(n.length&&(t.labels={or:n.map(r=>({name:{eqIgnoreCase:r}}))}),e?.assignedToMe)t.assignee={isMe:{eq:!0}};else if(e?.assignee?.trim()){let r=e.assignee.trim();t.assignee={or:[{email:{eqIgnoreCase:r}},{displayName:{eqIgnoreCase:r}}]}}return t}async function Bo(e,t,n){let r=await ae(Do,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e}`},body:JSON.stringify({query:t,variables:n})});if(!r.ok){let o=new Error(`Linear issues fetch failed (HTTP ${r.status})`);throw o.status=r.status,o}let s=await r.json();return{nodes:s.data?.issues?.nodes??[],pageInfo:s.data?.issues?.pageInfo}}async function $o(e,t){let n=qc(t),r=[],s;for(let o=0;o<5;o++){let{nodes:i,pageInfo:c}=await Bo(e,Gc,{filter:n,after:s});if(r.push(...i),!c?.hasNextPage||!c.endCursor)return r;s=c.endCursor}return we("Linear",r.length),r}async function Lo(e,t){return t.length?(await Bo(e,Uc,{ids:t})).nodes:[]}var Fc=`query IssueComments($id: String!) {
|
|
77
77
|
issue(id: $id) {
|
|
78
78
|
comments(first: 100) { nodes { id body createdAt updatedAt user { name } } }
|
|
79
|
-
} }`;async function Go(e,t){let n=await ae(Do,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e}`},body:JSON.stringify({query:
|
|
79
|
+
} }`;async function Go(e,t){let n=await ae(Do,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e}`},body:JSON.stringify({query:Fc,variables:{id:t}})});if(!n.ok){let s=new Error(`Linear comments fetch failed (HTTP ${n.status})`);throw s.status=n.status,s}return((await n.json()).data?.issue?.comments?.nodes??[]).filter(s=>(s.body??"").trim()).map(s=>({id:s.id,author:s.user?.name??void 0,body:s.body??"",createdAt:s.createdAt,updatedAt:s.updatedAt}))}var Uo=e=>`https://api.atlassian.com/ex/jira/${e}/rest/api/3`;function Hc(e){switch(e){case"new":return"todo";case"indeterminate":return"in_progress";case"done":return"done";default:return"todo"}}function Qc(e){switch((e??"").toLowerCase()){case"highest":case"urgent":case"critical":case"blocker":return 1;case"high":return 2;case"medium":case"normal":return 3;case"low":case"lowest":case"minor":case"trivial":return 4;default:return}}function qo(e){if(e==null)return;if(typeof e=="string")return e||void 0;let t=[],n=(r,s=!1)=>{if(!r||typeof r!="object")return;if(r.type==="text"){t.push(r.text??"");return}if(r.type==="hardBreak"){t.push(`
|
|
80
80
|
`);return}let o=r.type==="paragraph"||r.type==="heading"||r.type==="codeBlock"||r.type==="listItem"||r.type==="blockquote";r.type==="listItem"&&t.push("- ");for(let i of r.content??[])n(i,r.type==="listItem");o&&t.push(`
|
|
81
81
|
`)};return n(e),t.join("").replace(/\n{3,}/g,`
|
|
82
82
|
|
|
83
|
-
`).trim()||void 0}function jr(e,t,n,r){let s=e.fields??{},o=s.assignee?.avatarUrls?.["48x48"]??void 0,i=s.reporter?.avatarUrls?.["48x48"]??void 0;return{id:`jira_${e.id}`,connectionId:t,provider:"jira",externalIdentifier:e.key,title:s.summary??e.key,description:qo(s.description),status:
|
|
84
|
-
`)||void 0,parent:e.parent?.gid?{identifier:e.parent.gid,title:e.parent.name??""}:void 0,url:e.permalink_url||`https://app.asana.com/0/0/${e.gid}`,workspaceId:n,createdAt:e.created_at,updatedAt:e.modified_at}}async function
|
|
83
|
+
`).trim()||void 0}function jr(e,t,n,r){let s=e.fields??{},o=s.assignee?.avatarUrls?.["48x48"]??void 0,i=s.reporter?.avatarUrls?.["48x48"]??void 0;return{id:`jira_${e.id}`,connectionId:t,provider:"jira",externalIdentifier:e.key,title:s.summary??e.key,description:qo(s.description),status:Hc(s.status?.statusCategory?.key),statusRaw:s.status?.name??"",assignee:s.assignee?.displayName?{name:s.assignee.displayName,avatarUrl:o}:void 0,reporter:s.reporter?.displayName?{name:s.reporter.displayName,avatarUrl:i}:void 0,dueDate:s.duedate??void 0,priority:Qc(s.priority?.name??void 0),labels:(s.labels??[]).map(c=>({name:c})),project:s.project?.name,parent:s.parent?.key?{identifier:s.parent.key,title:s.parent.fields?.summary??"",type:s.parent.fields?.issuetype?.name}:void 0,url:r?`https://${r}/browse/${e.key}`:e.key,workspaceId:n,createdAt:s.created,updatedAt:s.updated}}var Jc=["summary","description","status","assignee","reporter","duedate","priority","labels","project","parent","created","updated"];function Dr(e){return e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}function zc(e){let t=["statusCategory != Done"],n=(e?.labels??[]).map(r=>r.trim()).filter(Boolean);return n.length&&t.push(`labels in (${n.map(r=>`"${Dr(r)}"`).join(", ")})`),e?.assignedToMe?t.push("assignee = currentUser()"):e?.assignee?.trim()&&t.push(`assignee = "${Dr(e.assignee.trim())}"`),`${t.join(" AND ")} ORDER BY updated DESC`}async function Fo(e,t,n,r,s=!1){let o=[],i,c=s?5:1;for(let d=0;d<c;d++){let u=await ae(`${Uo(e)}/search/jql`,{method:"POST",headers:{"content-type":"application/json",accept:"application/json",authorization:`Bearer ${t}`},body:JSON.stringify({jql:n,fields:Jc,maxResults:r,...i?{nextPageToken:i}:{}})});if(!u.ok){let g=new Error(`Jira search failed (HTTP ${u.status})`);throw g.status=u.status,g}let p=await u.json();if(o.push(...p.issues??[]),!s||p.isLast||!p.nextPageToken)return o;i=p.nextPageToken}return s&&we("Jira",o.length),o}function Wo(e,t,n){return Fo(e,t,zc(n),100,!0)}function Ko(e,t,n){if(!n.length)return Promise.resolve([]);let r=n.map(s=>`"${Dr(s)}"`).join(", ");return Fo(e,t,`id in (${r})`,250)}async function Ho(e,t,n){let r=await ae(`${Uo(e)}/issue/${encodeURIComponent(n)}/comment?maxResults=100&orderBy=created`,{headers:{accept:"application/json",authorization:`Bearer ${t}`}});if(!r.ok){let o=new Error(`Jira comments fetch failed (HTTP ${r.status})`);throw o.status=r.status,o}return((await r.json()).comments??[]).map(o=>({id:o.id,author:o.author?.displayName??void 0,body:qo(o.body)??"",createdAt:o.created,updatedAt:o.updated})).filter(o=>o.body.trim())}var Qo="https://app.asana.com/api/1.0",Jo=["name","notes","completed","assignee.name","due_on","tags.name","projects.name","parent.name","parent.gid","created_at","modified_at","permalink_url"].join(",");function Yc(e){return e?"done":"todo"}function Mr(e,t,n){return{id:`asana_${e.gid}`,connectionId:t,provider:"asana",externalIdentifier:e.gid,title:e.name||e.gid,description:e.notes?.trim()||void 0,status:Yc(e.completed),statusRaw:"",assignee:e.assignee?.name?{name:e.assignee.name}:void 0,dueDate:e.due_on??void 0,labels:(e.tags??[]).map(r=>r.name).filter(r=>!!r).map(r=>({name:r})),project:(e.projects??[]).map(r=>r.name).filter(r=>!!r).join(`
|
|
84
|
+
`)||void 0,parent:e.parent?.gid?{identifier:e.parent.gid,title:e.parent.name??""}:void 0,url:e.permalink_url||`https://app.asana.com/0/0/${e.gid}`,workspaceId:n,createdAt:e.created_at,updatedAt:e.modified_at}}async function Br(e,t){let n=await ae(`${Qo}${e}`,{headers:{authorization:`Bearer ${t}`,accept:"application/json"}});if(!n.ok){let s=new Error(`Asana request failed (HTTP ${n.status})`);throw s.status=n.status,s}return(await n.json()).data}async function Vc(e,t){let n=await ae(`${Qo}${e}`,{headers:{authorization:`Bearer ${t}`,accept:"application/json"}});if(!n.ok){let s=new Error(`Asana request failed (HTTP ${n.status})`);throw s.status=n.status,s}let r=await n.json();return{data:r.data??[],nextOffset:r.next_page?.offset}}function Zc(e,t){let n=t?.assignee?.trim()||"me";return new URLSearchParams({workspace:e,assignee:n,completed_since:"now",limit:"100",opt_fields:Jo}).toString()}async function zo(e,t,n){let r=Zc(e,n),s=[],o;for(let i=0;i<5;i++){let{data:c,nextOffset:d}=await Vc(`/tasks?${r}${o?`&offset=${o}`:""}`,t);if(s.push(...c),!d)return s;o=d}return we("Asana",s.length),s}async function Xo(e,t){return t.length?(await Wn(t,5,r=>Br(`/tasks/${r}?opt_fields=${Jo}`,e).catch(()=>null))).filter(r=>!!r):[]}async function Yo(e,t){return(await Br(`/tasks/${encodeURIComponent(t)}/stories?opt_fields=text,created_at,created_by.name,type`,e)??[]).filter(r=>r.type==="comment"&&(r.text??"").trim()).map(r=>({id:r.gid,author:r.created_by?.name??void 0,body:r.text??"",createdAt:r.created_at}))}async function Vo(e){try{let t=await Br("/workspaces?opt_fields=name",e);return Array.isArray(t)?t:[]}catch{return[]}}var td="https://api.clickup.com/api/v2";function nd(e){switch((e??"").toLowerCase()){case"done":case"closed":return"done";case"open":return"todo";default:return"in_progress"}}function rd(e){return(e??"").replace(/\b\w/g,t=>t.toUpperCase())}function sd(e){switch((e??"").toLowerCase()){case"urgent":return 1;case"high":return 2;case"normal":return 3;case"low":return 4;default:return}}function $r(e){if(e==null||e==="")return;let t=Number(e);return Number.isFinite(t)?new Date(t).toISOString():void 0}function od(e){return $r(e)?.slice(0,10)}function Lr(e,t,n){let r=e.assignees?.[0];return{id:`clickup_${e.id}`,connectionId:t,provider:"clickup",externalIdentifier:e.custom_id?.trim()||e.id,title:e.name||e.id,description:(e.text_content??e.description??"").trim()||void 0,status:nd(e.status?.type),statusRaw:rd(e.status?.status??void 0),assignee:r?.username?{name:r.username,avatarUrl:r.profilePicture??void 0}:void 0,reporter:e.creator?.username?{name:e.creator.username,avatarUrl:e.creator.profilePicture??void 0}:void 0,dueDate:od(e.due_date),priority:sd(e.priority?.priority),labels:(e.tags??[]).map(s=>s.name).filter(s=>!!s).map(s=>({name:s})),project:e.list?.name,url:e.url||`https://app.clickup.com/t/${e.id}`,workspaceId:n,createdAt:$r(e.date_created),updatedAt:$r(e.date_updated)}}async function Kn(e,t){let n=await ae(`${td}${e}`,{headers:{authorization:t,accept:"application/json"}});if(!n.ok){let r=new Error(`ClickUp request failed (HTTP ${n.status})`);throw r.status=n.status,r}return await n.json()}function id(e){let t=new URLSearchParams({archived:"false",include_closed:"false",subtasks:"true",order_by:"updated"});for(let n of(e?.labels??[]).map(r=>r.trim()).filter(Boolean))t.append("tags[]",n);return t.toString()}async function Zo(e,t,n){let r=id(n),s=[];for(let o=0;o<5;o++){let i=await Kn(`/team/${e}/task?${r}&page=${o}`,t);if(s.push(...i.tasks??[]),i.last_page||!i.tasks?.length)return s}return we("ClickUp",s.length),s}async function ei(e,t){return t.length?(await Wn(t,5,r=>Kn(`/task/${r}`,e).catch(()=>null))).filter(r=>!!r):[]}async function ti(e,t){let n=await Kn(`/task/${encodeURIComponent(t)}/comment`,e),r=s=>{let o=Number(s);return Number.isFinite(o)&&o>0?new Date(o).toISOString():void 0};return(n.comments??[]).filter(s=>(s.comment_text??"").trim()).map(s=>({id:String(s.id),author:s.user?.username??void 0,body:s.comment_text??"",createdAt:r(s.date)}))}async function ni(e){try{return(await Kn("/team",e)).teams??[]}catch{return[]}}var cd="https://api.monday.com/v2";function dd(e){let t=(e??"").toLowerCase();return t?/done|complete|closed|resolved|shipped/.test(t)?"done":/progress|working|review|doing|active/.test(t)?"in_progress":"todo":"todo"}function Gr(e,t,n,r){let s=e.column_values??[],o=g=>g.column?.title??"",i=s.find(g=>g.id==="status")??s.find(g=>g.type==="status"&&!/priorit/i.test(o(g))),c=[...new Set(s.filter(g=>g.type==="people"||g.type==="multiple-person").flatMap(g=>(g.text??"").split(",")).map(g=>g.trim()).filter(Boolean))],d=s.find(g=>/due|date/i.test(o(g)))??s.find(g=>g.type==="date"),u=e.board?.id,p=t&&u?`https://${t}.monday.com/boards/${u}/pulses/${e.id}`:"https://monday.com";return{id:`monday_${e.id}`,connectionId:n,provider:"monday",externalIdentifier:e.id,title:e.name||e.id,status:dd(i?.text??void 0),statusRaw:i?.text?.trim()||e.group?.title||"",assignee:c.length?{name:c.join(`
|
|
85
85
|
`)}:void 0,dueDate:d?.text?.trim()||void 0,labels:[],project:e.board?.name,url:p,workspaceId:r,createdAt:e.created_at,updatedAt:e.updated_at}}var ri=`
|
|
86
86
|
id
|
|
87
87
|
name
|
|
@@ -90,7 +90,7 @@ ${e.prompt}`,workspace:e.workspaceId,workingDir:r,model:t.model,effort:t.effort,
|
|
|
90
90
|
group { title }
|
|
91
91
|
board { id name }
|
|
92
92
|
column_values { id type text column { title } }
|
|
93
|
-
`;async function
|
|
93
|
+
`;async function Hn(e,t){let n=await ae(cd,{method:"POST",headers:{authorization:t,"content-type":"application/json","api-version":"2024-01"},body:JSON.stringify({query:e})});if(!n.ok){let s=new Error(`Monday request failed (HTTP ${n.status})`);throw s.status=n.status,s}let r=await n.json();if(r.errors)throw new Error(`Monday GraphQL error: ${JSON.stringify(r.errors)}`);return r.data}async function si(e,t){let r=(await Hn(`query { boards (limit: 50) { id name items_page (limit: 100) { cursor items { ${ri} } } } }`,e)).boards??[],s=[],o=r.length>=50;for(let i of r){for(let c of i.items_page?.items??[])s.push({...c,board:c.board??{id:i.id,name:i.name}});i.items_page?.cursor&&(o=!0)}return o&&we("Monday",s.length),s}async function oi(e,t){if(!t.length)return[];let n=t.map(s=>JSON.stringify(s)).join(", ");return(await Hn(`query { items (ids: [${n}]) { ${ri} } }`,e)).items??[]}async function ii(e,t){return((await Hn(`query { items (ids: [${JSON.stringify(t)}]) { updates (limit: 100) { id text_body body created_at creator { name } } } }`,e)).items?.[0]?.updates??[]).map(r=>({id:r.id,author:r.creator?.name??void 0,body:(r.text_body??r.body??"").trim(),createdAt:r.created_at})).filter(r=>r.body)}async function ai(e){try{return(await Hn(`
|
|
94
94
|
query {
|
|
95
95
|
me {
|
|
96
96
|
account {
|
|
@@ -99,54 +99,54 @@ ${e.prompt}`,workspace:e.workspaceId,workingDir:r,model:t.model,effort:t.effort,
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
|
-
`,e)).me?.account??null}catch{return null}}var Ur=process.env.SUPERCLAWD_API_URL||"https://api.superclawd.com";function qr(){let e=he();return e?{"content-type":"application/json","x-mcp-key-id":e.keyId,"x-mcp-secret":e.secret}:null}async function ci(e,t){let n=qr();if(!n)return{error:"Not authenticated. Run `superclawd login`."};try{let r=await fetch(`${Ur}/api/oauth/linear/start`,{method:"POST",headers:n,body:JSON.stringify({connectionId:e,daemonPort:t})}),s=await r.json().catch(()=>null);return r.ok?s?.configured===!1?{configured:!1,error:s?.message??"Linear isn't set up on the server yet."}:s?.url?{url:s.url}:{error:"The server didn't return an authorize URL."}:{error:s?.message??`Couldn't start OAuth (HTTP ${r.status}).`}}catch(r){return{error:`Couldn't reach the SuperClawd backend (${String(r)}).`}}}async function di(e){let t=qr();if(!t)return null;try{let n=await fetch(`${Ur}/api/oauth/linear/claim`,{method:"POST",headers:t,body:JSON.stringify({code:e})});return n.ok?await n.json():null}catch{return null}}async function ui(e){let t=qr();if(!t)return null;try{let n=await fetch(`${Ur}/api/oauth/linear/refresh`,{method:"POST",headers:t,body:JSON.stringify({refreshToken:e})});if(!n.ok)return null;let r=await n.json();return r?.accessToken?r:null}catch{return null}}var Fr=process.env.SUPERCLAWD_API_URL||"https://api.superclawd.com",ud="https://api.atlassian.com/oauth/token/accessible-resources";function Wr(){let e=he();return e?{"content-type":"application/json","x-mcp-key-id":e.keyId,"x-mcp-secret":e.secret}:null}async function li(e,t){let n=Wr();if(!n)return{error:"Not authenticated. Run `superclawd login`."};try{let r=await fetch(`${Fr}/api/oauth/jira/start`,{method:"POST",headers:n,body:JSON.stringify({connectionId:e,daemonPort:t})}),s=await r.json().catch(()=>null);return r.ok?s?.configured===!1?{configured:!1,error:s?.message??"Jira isn't set up on the server yet."}:s?.url?{url:s.url}:{error:"The server didn't return an authorize URL."}:{error:s?.message??`Couldn't start OAuth (HTTP ${r.status}).`}}catch(r){return{error:`Couldn't reach the SuperClawd backend (${String(r)}).`}}}async function pi(e){let t=Wr();if(!t)return null;try{let n=await fetch(`${Fr}/api/oauth/jira/claim`,{method:"POST",headers:t,body:JSON.stringify({code:e})});return n.ok?await n.json():null}catch{return null}}async function fi(e){let t=Wr();if(!t)return null;try{let n=await fetch(`${Fr}/api/oauth/jira/refresh`,{method:"POST",headers:t,body:JSON.stringify({refreshToken:e})});if(!n.ok)return null;let r=await n.json();return r?.accessToken?r:null}catch{return null}}async function mi(e){try{let t=await fetch(ud,{headers:{authorization:`Bearer ${e}`,accept:"application/json"}});if(!t.ok)return[];let n=await t.json();return Array.isArray(n)?n:[]}catch{return[]}}var Kr=process.env.SUPERCLAWD_API_URL||"https://api.superclawd.com";function Hr(){let e=he();return e?{"content-type":"application/json","x-mcp-key-id":e.keyId,"x-mcp-secret":e.secret}:null}async function gi(e,t){let n=Hr();if(!n)return{error:"Not authenticated. Run `superclawd login`."};try{let r=await fetch(`${Kr}/api/oauth/asana/start`,{method:"POST",headers:n,body:JSON.stringify({connectionId:e,daemonPort:t})}),s=await r.json().catch(()=>null);return r.ok?s?.configured===!1?{configured:!1,error:s?.message??"Asana isn't set up on the server yet."}:s?.url?{url:s.url}:{error:"The server didn't return an authorize URL."}:{error:s?.message??`Couldn't start OAuth (HTTP ${r.status}).`}}catch(r){return{error:`Couldn't reach the SuperClawd backend (${String(r)}).`}}}async function hi(e){let t=Hr();if(!t)return null;try{let n=await fetch(`${Kr}/api/oauth/asana/claim`,{method:"POST",headers:t,body:JSON.stringify({code:e})});return n.ok?await n.json():null}catch{return null}}async function ki(e){let t=Hr();if(!t)return null;try{let n=await fetch(`${Kr}/api/oauth/asana/refresh`,{method:"POST",headers:t,body:JSON.stringify({refreshToken:e})});if(!n.ok)return null;let r=await n.json();return r?.accessToken?r:null}catch{return null}}var yi=process.env.SUPERCLAWD_API_URL||"https://api.superclawd.com";function wi(){let e=he();return e?{"content-type":"application/json","x-mcp-key-id":e.keyId,"x-mcp-secret":e.secret}:null}async function bi(e,t){let n=wi();if(!n)return{error:"Not authenticated. Run `superclawd login`."};try{let r=await fetch(`${yi}/api/oauth/clickup/start`,{method:"POST",headers:n,body:JSON.stringify({connectionId:e,daemonPort:t})}),s=await r.json().catch(()=>null);return r.ok?s?.configured===!1?{configured:!1,error:s?.message??"ClickUp isn't set up on the server yet."}:s?.url?{url:s.url}:{error:"The server didn't return an authorize URL."}:{error:s?.message??`Couldn't start OAuth (HTTP ${r.status}).`}}catch(r){return{error:`Couldn't reach the SuperClawd backend (${String(r)}).`}}}async function Si(e){let t=wi();if(!t)return null;try{let n=await fetch(`${yi}/api/oauth/clickup/claim`,{method:"POST",headers:t,body:JSON.stringify({code:e})});return n.ok?await n.json():null}catch{return null}}function Ii(){return Promise.resolve(null)}var xi=process.env.SUPERCLAWD_API_URL||"https://api.superclawd.com";function vi(){let e=he();return e?{"content-type":"application/json","x-mcp-key-id":e.keyId,"x-mcp-secret":e.secret}:null}async function Ri(e,t){let n=vi();if(!n)return{error:"Not authenticated. Run `superclawd login`."};try{let r=await fetch(`${xi}/api/oauth/monday/start`,{method:"POST",headers:n,body:JSON.stringify({connectionId:e,daemonPort:t})}),s=await r.json().catch(()=>null);return r.ok?s?.configured===!1?{configured:!1,error:s?.message??"Monday isn't set up on the server yet."}:s?.url?{url:s.url}:{error:"The server didn't return an authorize URL."}:{error:s?.message??`Couldn't start OAuth (HTTP ${r.status}).`}}catch(r){return{error:`Couldn't reach the SuperClawd backend (${String(r)}).`}}}async function Ti(e){let t=vi();if(!t)return null;try{let n=await fetch(`${xi}/api/oauth/monday/claim`,{method:"POST",headers:t,body:JSON.stringify({code:e})});return n.ok?await n.json():null}catch{return null}}function _i(){return Promise.resolve(null)}var ld=100;async function sn(e,t,n){return await Promise.all(e.map(async r=>{try{let o=[...await t(r.id.slice(n.length))].sort((i,c)=>(i.createdAt??"").localeCompare(c.createdAt??""));r.comments=o.slice(-ld)}catch{}})),e}var pd={idPrefix:"lin_",async fetchDiscovery(e,t,n){return(await Bo(t,n)).map(r=>Or(r,e.id,e.workspaceId))},async fetchByIds(e,t,n){let r=(await Lo(t,n)).map(s=>Or(s,e.id,e.workspaceId));return sn(r,s=>Go(t,s),"lin_")},refresh:ui};function Ai(e){if(!e.cloudId)throw Object.assign(new Error("Jira connection has no cloudId (reconnect)"),{terminal:!0});return e.cloudId}var fd={idPrefix:"jira_",async fetchDiscovery(e,t,n){let r=e.providerAccountName??"";return(await Wo(Ai(e),t,n)).map(s=>jr(s,e.id,e.workspaceId,r))},async fetchByIds(e,t,n){let r=e.providerAccountName??"",s=Ai(e),o=(await Ko(s,t,n)).map(i=>jr(i,e.id,e.workspaceId,r));return sn(o,i=>Ho(s,t,i),"jira_")},refresh:fi};function Ni(e,t){if(!e.providerAccountId)throw Object.assign(new Error(`${t} connection has no account scope (reconnect)`),{terminal:!0});return e.providerAccountId}var md={idPrefix:"asana_",async fetchDiscovery(e,t,n){return(await zo(Ni(e,"Asana"),t,n)).map(r=>Mr(r,e.id,e.workspaceId))},async fetchByIds(e,t,n){let r=(await Xo(t,n)).map(s=>Mr(s,e.id,e.workspaceId));return sn(r,s=>Yo(t,s),"asana_")},refresh:ki},gd={idPrefix:"clickup_",async fetchDiscovery(e,t,n){return(await Zo(Ni(e,"ClickUp"),t,n)).map(r=>Lr(r,e.id,e.workspaceId))},async fetchByIds(e,t,n){let r=(await ei(t,n)).map(s=>Lr(s,e.id,e.workspaceId));return sn(r,s=>ti(t,s),"clickup_")},refresh:Ii},hd={idPrefix:"monday_",async fetchDiscovery(e,t,n){return(await si(t,n)).map(r=>Gr(r,e.providerAccountId,e.id,e.workspaceId))},async fetchByIds(e,t,n){let r=(await oi(t,n)).map(s=>Gr(s,e.providerAccountId,e.id,e.workspaceId));return sn(r,s=>ii(t,s),"monday_")},refresh:_i},kd={linear:pd,jira:fd,asana:md,clickup:gd,monday:hd};function on(e){return kd[e]??null}dt();async function nt(e){await ut.upsert({connectionId:e.connectionId,userId:E(),provider:e.provider,accessToken:lt(e.accessToken),refreshToken:e.refreshToken?lt(e.refreshToken):null,scope:e.scope??null,expiresAt:e.expiresAt??null})}async function Hn(e){let t=await ut.findByPk(e);if(!t)return null;let n=t.get({plain:!0});if(n.userId!==E())return null;let r=n.refreshToken??null;return{connectionId:e,provider:String(n.provider),accessToken:pt(String(n.accessToken)),refreshToken:r?pt(r):null,scope:n.scope??null,expiresAt:n.expiresAt??null}}async function Ei(e){await ut.destroy({where:{connectionId:e,userId:E()}})}async function Qr(e,t,n){if(!t.refreshToken)return null;let r=await n.refresh(t.provider,t.refreshToken);return r?(await n.saveToken({connectionId:e,provider:t.provider,accessToken:r.accessToken,refreshToken:r.refreshToken??t.refreshToken,scope:r.scope??t.scope,expiresAt:r.expiresAt??t.expiresAt}),r.accessToken):null}async function Ci(e,t){let n=await t.getToken(e);if(!n)return null;let r=t.now?t.now():Date.now();return(n.expiresAt?Date.parse(n.expiresAt)-r<6e4:!1)?Qr(e,n,t):n.accessToken}function Sd(e,t){return e.filter(n=>n.provider!=="local"&&n.linkStatus==="linked"&&(n.authStatus==="connected"||n.authStatus==="needs_refresh")&&(!t||n.workspaceId!=null&&t.includes(n.workspaceId)))}async function Id(e){if(e.isMock())return;let t=Sd(e.listConnections(),e.connectedWorkspaces());for(let n of t)try{await e.syncOne(n)}catch{}}var Jr=null;function Pi(e,t=6e5){Jr||(Jr=setInterval(()=>void Id(e),t),Jr.unref?.())}import{execFile as vd}from"node:child_process";function Qn(e){return new Promise(t=>{vd("gh",e,{maxBuffer:10*1024*1024},(n,r)=>{t({code:n&&typeof n.code=="number"?n.code:n?1:0,stdout:String(r??"")})})})}var Rd=new Set(["FAILURE","CANCELLED","TIMED_OUT","ACTION_REQUIRED","STARTUP_FAILURE","STALE"]),Td=new Set(["FAILURE","ERROR"]),Oi=new Set(["QUEUED","IN_PROGRESS","WAITING","PENDING","REQUESTED"]);function _d(e){if(!e.length)return{ci:"none",failing:[]};let t=[],n=!1;for(let r of e){let s=r.name||r.context||"check",o=r.conclusion&&Rd.has(r.conclusion)||r.state&&Td.has(r.state),i=r.status&&Oi.has(r.status)||r.state&&Oi.has(r.state)||r.__typename==="CheckRun"&&r.status!=="COMPLETED"&&!r.conclusion;o?t.push(s):i&&(n=!0)}return t.length?{ci:"failing",failing:t}:n?{ci:"pending",failing:[]}:{ci:"passing",failing:[]}}async function Ad(e){let t=await Qn(["pr","view",e,"--json","state,statusCheckRollup"]);if(t.code!==0)return{state:"unknown",ci:"unknown",failingChecks:[]};let n;try{n=JSON.parse(t.stdout)}catch{return{state:"unknown",ci:"unknown",failingChecks:[]}}let r=n.state==="MERGED"?"merged":n.state==="CLOSED"?"closed":n.state==="OPEN"?"open":"unknown",{ci:s,failing:o}=_d(n.statusCheckRollup??[]);return{state:r,ci:s,failingChecks:o}}function Di(e){let t=/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/.exec(e);return t?{owner:t[1],repo:t[2],num:Number(t[3])}:null}function ji(e){let t=Di(e);return t?`${t.repo}#${t.num}`:e}function Nd(e){if(!e.length)return{state:"unknown",ci:"unknown",failingChecks:[],perPr:[]};let t=e.map(i=>i.status.state),n=e.map(i=>i.status.ci),r=t.some(i=>i==="open")?"open":t.every(i=>i==="merged")?"merged":t.every(i=>i==="merged"||i==="closed")?"closed":"unknown",s=n.some(i=>i==="failing")?"failing":n.some(i=>i==="pending")?"pending":n.some(i=>i==="unknown")?"unknown":n.some(i=>i==="passing")?"passing":"none",o=[];for(let i of e){if(i.status.ci!=="failing")continue;let c=ji(i.url);if(i.status.failingChecks.length)for(let d of i.status.failingChecks)o.push(`${c} ${d}`);else o.push(`${c} checks`)}return{state:r,ci:s,failingChecks:o,perPr:e}}async function Mi(e){let t=await Promise.all(e.map(async n=>({url:n,status:await Ad(n)})));return Nd(t)}async function Ed(e){let t=Di(e);if(!t)return{count:0,digest:[]};let r=await Qn(["api","graphql","-f",`query=query($owner:String!,$repo:String!,$num:Int!){
|
|
102
|
+
`,e)).me?.account??null}catch{return null}}var Ur=process.env.SUPERCLAWD_API_URL||"https://api.superclawd.com";function qr(){let e=he();return e?{"content-type":"application/json","x-mcp-key-id":e.keyId,"x-mcp-secret":e.secret}:null}async function ci(e,t){let n=qr();if(!n)return{error:"Not authenticated. Run `superclawd login`."};try{let r=await fetch(`${Ur}/api/oauth/linear/start`,{method:"POST",headers:n,body:JSON.stringify({connectionId:e,daemonPort:t})}),s=await r.json().catch(()=>null);return r.ok?s?.configured===!1?{configured:!1,error:s?.message??"Linear isn't set up on the server yet."}:s?.url?{url:s.url}:{error:"The server didn't return an authorize URL."}:{error:s?.message??`Couldn't start OAuth (HTTP ${r.status}).`}}catch(r){return{error:`Couldn't reach the SuperClawd backend (${String(r)}).`}}}async function di(e){let t=qr();if(!t)return null;try{let n=await fetch(`${Ur}/api/oauth/linear/claim`,{method:"POST",headers:t,body:JSON.stringify({code:e})});return n.ok?await n.json():null}catch{return null}}async function ui(e){let t=qr();if(!t)return null;try{let n=await fetch(`${Ur}/api/oauth/linear/refresh`,{method:"POST",headers:t,body:JSON.stringify({refreshToken:e})});if(!n.ok)return null;let r=await n.json();return r?.accessToken?r:null}catch{return null}}var Fr=process.env.SUPERCLAWD_API_URL||"https://api.superclawd.com",ld="https://api.atlassian.com/oauth/token/accessible-resources";function Wr(){let e=he();return e?{"content-type":"application/json","x-mcp-key-id":e.keyId,"x-mcp-secret":e.secret}:null}async function li(e,t){let n=Wr();if(!n)return{error:"Not authenticated. Run `superclawd login`."};try{let r=await fetch(`${Fr}/api/oauth/jira/start`,{method:"POST",headers:n,body:JSON.stringify({connectionId:e,daemonPort:t})}),s=await r.json().catch(()=>null);return r.ok?s?.configured===!1?{configured:!1,error:s?.message??"Jira isn't set up on the server yet."}:s?.url?{url:s.url}:{error:"The server didn't return an authorize URL."}:{error:s?.message??`Couldn't start OAuth (HTTP ${r.status}).`}}catch(r){return{error:`Couldn't reach the SuperClawd backend (${String(r)}).`}}}async function pi(e){let t=Wr();if(!t)return null;try{let n=await fetch(`${Fr}/api/oauth/jira/claim`,{method:"POST",headers:t,body:JSON.stringify({code:e})});return n.ok?await n.json():null}catch{return null}}async function fi(e){let t=Wr();if(!t)return null;try{let n=await fetch(`${Fr}/api/oauth/jira/refresh`,{method:"POST",headers:t,body:JSON.stringify({refreshToken:e})});if(!n.ok)return null;let r=await n.json();return r?.accessToken?r:null}catch{return null}}async function mi(e){try{let t=await fetch(ld,{headers:{authorization:`Bearer ${e}`,accept:"application/json"}});if(!t.ok)return[];let n=await t.json();return Array.isArray(n)?n:[]}catch{return[]}}var Kr=process.env.SUPERCLAWD_API_URL||"https://api.superclawd.com";function Hr(){let e=he();return e?{"content-type":"application/json","x-mcp-key-id":e.keyId,"x-mcp-secret":e.secret}:null}async function gi(e,t){let n=Hr();if(!n)return{error:"Not authenticated. Run `superclawd login`."};try{let r=await fetch(`${Kr}/api/oauth/asana/start`,{method:"POST",headers:n,body:JSON.stringify({connectionId:e,daemonPort:t})}),s=await r.json().catch(()=>null);return r.ok?s?.configured===!1?{configured:!1,error:s?.message??"Asana isn't set up on the server yet."}:s?.url?{url:s.url}:{error:"The server didn't return an authorize URL."}:{error:s?.message??`Couldn't start OAuth (HTTP ${r.status}).`}}catch(r){return{error:`Couldn't reach the SuperClawd backend (${String(r)}).`}}}async function hi(e){let t=Hr();if(!t)return null;try{let n=await fetch(`${Kr}/api/oauth/asana/claim`,{method:"POST",headers:t,body:JSON.stringify({code:e})});return n.ok?await n.json():null}catch{return null}}async function ki(e){let t=Hr();if(!t)return null;try{let n=await fetch(`${Kr}/api/oauth/asana/refresh`,{method:"POST",headers:t,body:JSON.stringify({refreshToken:e})});if(!n.ok)return null;let r=await n.json();return r?.accessToken?r:null}catch{return null}}var yi=process.env.SUPERCLAWD_API_URL||"https://api.superclawd.com";function wi(){let e=he();return e?{"content-type":"application/json","x-mcp-key-id":e.keyId,"x-mcp-secret":e.secret}:null}async function bi(e,t){let n=wi();if(!n)return{error:"Not authenticated. Run `superclawd login`."};try{let r=await fetch(`${yi}/api/oauth/clickup/start`,{method:"POST",headers:n,body:JSON.stringify({connectionId:e,daemonPort:t})}),s=await r.json().catch(()=>null);return r.ok?s?.configured===!1?{configured:!1,error:s?.message??"ClickUp isn't set up on the server yet."}:s?.url?{url:s.url}:{error:"The server didn't return an authorize URL."}:{error:s?.message??`Couldn't start OAuth (HTTP ${r.status}).`}}catch(r){return{error:`Couldn't reach the SuperClawd backend (${String(r)}).`}}}async function Ii(e){let t=wi();if(!t)return null;try{let n=await fetch(`${yi}/api/oauth/clickup/claim`,{method:"POST",headers:t,body:JSON.stringify({code:e})});return n.ok?await n.json():null}catch{return null}}function Si(){return Promise.resolve(null)}var xi=process.env.SUPERCLAWD_API_URL||"https://api.superclawd.com";function vi(){let e=he();return e?{"content-type":"application/json","x-mcp-key-id":e.keyId,"x-mcp-secret":e.secret}:null}async function Ri(e,t){let n=vi();if(!n)return{error:"Not authenticated. Run `superclawd login`."};try{let r=await fetch(`${xi}/api/oauth/monday/start`,{method:"POST",headers:n,body:JSON.stringify({connectionId:e,daemonPort:t})}),s=await r.json().catch(()=>null);return r.ok?s?.configured===!1?{configured:!1,error:s?.message??"Monday isn't set up on the server yet."}:s?.url?{url:s.url}:{error:"The server didn't return an authorize URL."}:{error:s?.message??`Couldn't start OAuth (HTTP ${r.status}).`}}catch(r){return{error:`Couldn't reach the SuperClawd backend (${String(r)}).`}}}async function Ti(e){let t=vi();if(!t)return null;try{let n=await fetch(`${xi}/api/oauth/monday/claim`,{method:"POST",headers:t,body:JSON.stringify({code:e})});return n.ok?await n.json():null}catch{return null}}function _i(){return Promise.resolve(null)}var pd=100;async function on(e,t,n){return await Promise.all(e.map(async r=>{try{let o=[...await t(r.id.slice(n.length))].sort((i,c)=>(i.createdAt??"").localeCompare(c.createdAt??""));r.comments=o.slice(-pd)}catch{}})),e}var fd={idPrefix:"lin_",async fetchDiscovery(e,t,n){return(await $o(t,n)).map(r=>Or(r,e.id,e.workspaceId))},async fetchByIds(e,t,n){let r=(await Lo(t,n)).map(s=>Or(s,e.id,e.workspaceId));return on(r,s=>Go(t,s),"lin_")},refresh:ui};function Ai(e){if(!e.cloudId)throw Object.assign(new Error("Jira connection has no cloudId (reconnect)"),{terminal:!0});return e.cloudId}var md={idPrefix:"jira_",async fetchDiscovery(e,t,n){let r=e.providerAccountName??"";return(await Wo(Ai(e),t,n)).map(s=>jr(s,e.id,e.workspaceId,r))},async fetchByIds(e,t,n){let r=e.providerAccountName??"",s=Ai(e),o=(await Ko(s,t,n)).map(i=>jr(i,e.id,e.workspaceId,r));return on(o,i=>Ho(s,t,i),"jira_")},refresh:fi};function Ni(e,t){if(!e.providerAccountId)throw Object.assign(new Error(`${t} connection has no account scope (reconnect)`),{terminal:!0});return e.providerAccountId}var gd={idPrefix:"asana_",async fetchDiscovery(e,t,n){return(await zo(Ni(e,"Asana"),t,n)).map(r=>Mr(r,e.id,e.workspaceId))},async fetchByIds(e,t,n){let r=(await Xo(t,n)).map(s=>Mr(s,e.id,e.workspaceId));return on(r,s=>Yo(t,s),"asana_")},refresh:ki},hd={idPrefix:"clickup_",async fetchDiscovery(e,t,n){return(await Zo(Ni(e,"ClickUp"),t,n)).map(r=>Lr(r,e.id,e.workspaceId))},async fetchByIds(e,t,n){let r=(await ei(t,n)).map(s=>Lr(s,e.id,e.workspaceId));return on(r,s=>ti(t,s),"clickup_")},refresh:Si},kd={idPrefix:"monday_",async fetchDiscovery(e,t,n){return(await si(t,n)).map(r=>Gr(r,e.providerAccountId,e.id,e.workspaceId))},async fetchByIds(e,t,n){let r=(await oi(t,n)).map(s=>Gr(s,e.providerAccountId,e.id,e.workspaceId));return on(r,s=>ii(t,s),"monday_")},refresh:_i},yd={linear:fd,jira:md,asana:gd,clickup:hd,monday:kd};function an(e){return yd[e]??null}dt();async function nt(e){await ut.upsert({connectionId:e.connectionId,userId:C(),provider:e.provider,accessToken:lt(e.accessToken),refreshToken:e.refreshToken?lt(e.refreshToken):null,scope:e.scope??null,expiresAt:e.expiresAt??null})}async function Qn(e){let t=await ut.findByPk(e);if(!t)return null;let n=t.get({plain:!0});if(n.userId!==C())return null;let r=n.refreshToken??null;return{connectionId:e,provider:String(n.provider),accessToken:pt(String(n.accessToken)),refreshToken:r?pt(r):null,scope:n.scope??null,expiresAt:n.expiresAt??null}}async function Ei(e){await ut.destroy({where:{connectionId:e,userId:C()}})}async function Qr(e,t,n){if(!t.refreshToken)return null;let r=await n.refresh(t.provider,t.refreshToken);return r?(await n.saveToken({connectionId:e,provider:t.provider,accessToken:r.accessToken,refreshToken:r.refreshToken??t.refreshToken,scope:r.scope??t.scope,expiresAt:r.expiresAt??t.expiresAt}),r.accessToken):null}async function Ci(e,t){let n=await t.getToken(e);if(!n)return null;let r=t.now?t.now():Date.now();return(n.expiresAt?Date.parse(n.expiresAt)-r<6e4:!1)?Qr(e,n,t):n.accessToken}function Sd(e,t){return e.filter(n=>n.provider!=="local"&&n.linkStatus==="linked"&&(n.authStatus==="connected"||n.authStatus==="needs_refresh")&&(!t||n.workspaceId!=null&&t.includes(n.workspaceId)))}async function xd(e){if(e.isMock())return;let t=Sd(e.listConnections(),e.connectedWorkspaces());for(let n of t)try{await e.syncOne(n)}catch{}}var Jr=null;function Pi(e,t=6e5){Jr||(Jr=setInterval(()=>void xd(e),t),Jr.unref?.())}import{execFile as Rd}from"node:child_process";function Jn(e){return new Promise(t=>{Rd("gh",e,{maxBuffer:10*1024*1024},(n,r)=>{t({code:n&&typeof n.code=="number"?n.code:n?1:0,stdout:String(r??"")})})})}var Td=new Set(["FAILURE","CANCELLED","TIMED_OUT","ACTION_REQUIRED","STARTUP_FAILURE","STALE"]),_d=new Set(["FAILURE","ERROR"]),Oi=new Set(["QUEUED","IN_PROGRESS","WAITING","PENDING","REQUESTED"]);function Ad(e){if(!e.length)return{ci:"none",failing:[]};let t=[],n=!1;for(let r of e){let s=r.name||r.context||"check",o=r.conclusion&&Td.has(r.conclusion)||r.state&&_d.has(r.state),i=r.status&&Oi.has(r.status)||r.state&&Oi.has(r.state)||r.__typename==="CheckRun"&&r.status!=="COMPLETED"&&!r.conclusion;o?t.push(s):i&&(n=!0)}return t.length?{ci:"failing",failing:t}:n?{ci:"pending",failing:[]}:{ci:"passing",failing:[]}}async function Nd(e){let t=await Jn(["pr","view",e,"--json","state,statusCheckRollup"]);if(t.code!==0)return{state:"unknown",ci:"unknown",failingChecks:[]};let n;try{n=JSON.parse(t.stdout)}catch{return{state:"unknown",ci:"unknown",failingChecks:[]}}let r=n.state==="MERGED"?"merged":n.state==="CLOSED"?"closed":n.state==="OPEN"?"open":"unknown",{ci:s,failing:o}=Ad(n.statusCheckRollup??[]);return{state:r,ci:s,failingChecks:o}}function Di(e){let t=/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/.exec(e);return t?{owner:t[1],repo:t[2],num:Number(t[3])}:null}function ji(e){let t=Di(e);return t?`${t.repo}#${t.num}`:e}function Ed(e){if(!e.length)return{state:"unknown",ci:"unknown",failingChecks:[],perPr:[]};let t=e.map(i=>i.status.state),n=e.map(i=>i.status.ci),r=t.some(i=>i==="open")?"open":t.every(i=>i==="merged")?"merged":t.every(i=>i==="merged"||i==="closed")?"closed":"unknown",s=n.some(i=>i==="failing")?"failing":n.some(i=>i==="pending")?"pending":n.some(i=>i==="unknown")?"unknown":n.some(i=>i==="passing")?"passing":"none",o=[];for(let i of e){if(i.status.ci!=="failing")continue;let c=ji(i.url);if(i.status.failingChecks.length)for(let d of i.status.failingChecks)o.push(`${c} ${d}`);else o.push(`${c} checks`)}return{state:r,ci:s,failingChecks:o,perPr:e}}async function Mi(e){let t=await Promise.all(e.map(async n=>({url:n,status:await Nd(n)})));return Ed(t)}async function Cd(e){let t=Di(e);if(!t)return{count:0,digest:[]};let r=await Jn(["api","graphql","-f",`query=query($owner:String!,$repo:String!,$num:Int!){
|
|
103
103
|
repository(owner:$owner,name:$repo){ pullRequest(number:$num){
|
|
104
104
|
reviewThreads(first:100){ nodes {
|
|
105
105
|
isResolved isOutdated
|
|
106
106
|
comments(first:1){ nodes { body path line author { login } } }
|
|
107
107
|
} } } } }`,"-F",`owner=${t.owner}`,"-F",`repo=${t.repo}`,"-F",`num=${t.num}`]);if(r.code!==0)return{count:0,digest:[]};try{let s=JSON.parse(r.stdout)?.data?.repository?.pullRequest?.reviewThreads?.nodes??[],o=[];for(let i of s){if(i.isResolved)continue;let c=i.comments?.nodes?.[0];if(!c)continue;let d=c.path?`${c.path}${c.line?`:${c.line}`:""}`:"PR",u=i.isOutdated?" [outdated \u2014 the line has since changed]":"";o.push(`${d}${u} \u2014 ${String(c.body??"").split(`
|
|
108
|
-
`)[0].slice(0,160)}`)}return{count:o.length,digest:o}}catch{return{count:0,digest:[]}}}async function
|
|
109
|
-
`):""}function
|
|
108
|
+
`)[0].slice(0,160)}`)}return{count:o.length,digest:o}}catch{return{count:0,digest:[]}}}async function Bi(e){let t=await Promise.all(e.map(async r=>({tag:ji(r),c:await Cd(r)}))),n=[];for(let{tag:r,c:s}of t)for(let o of s.digest)n.push(`${r} ${o}`);return{count:n.length,digest:n}}var $i="<!-- superclawd:related -->",Li="<!-- /superclawd:related -->";function Pd(e,t,n){let r=t.filter(s=>s!==n);return r.length?[$i,`**Ticket:** ${e}`,"","**Related PRs (same ticket):**",...r.map(s=>`- ${s}`),Li].join(`
|
|
109
|
+
`):""}function Od(e,t){let n=o=>o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),r=new RegExp(`\\n*${n($i)}[\\s\\S]*?${n(Li)}`),s=e.replace(r,"").trimEnd();return t?(s?s+`
|
|
110
110
|
|
|
111
|
-
`:"")+t:s}async function Gi(e,t){if(!(t.length<2))for(let n of t){let r=await
|
|
111
|
+
`:"")+t:s}async function Gi(e,t){if(!(t.length<2))for(let n of t){let r=await Jn(["pr","view",n,"--json","body"]);if(r.code!==0)continue;let s="";try{s=JSON.parse(r.stdout)?.body??""}catch{continue}let o=Od(s,Pd(e,t,n));o!==s&&await Jn(["pr","edit",n,"--body",o])}}function jd(e){return Math.max(1,e.prMonitorIntervalMinutes??30)*6e4}function cn(e){return e.prMonitorMaxEngagements??5}function Ui(e,t,n){return e.prMonitorLastAt?n-Date.parse(e.prMonitorLastAt)>=jd(t):!0}function qi(e,t,n,r){if(n.state==="merged")return{kind:"disarm",note:"PR merged \u2014 monitoring stopped.",merged:!0};if(n.state==="closed")return{kind:"disarm",note:"PR was closed \u2014 monitoring stopped."};let s=(e.prMonitorCount??0)>=cn(t);return n.ci==="failing"?s?{kind:"disarm",note:`Monitor reached its ${cn(t)}-engagement cap with CI still failing \u2014 needs a human.`}:{kind:"engage",reason:"ci",prompt:Md(e,n)}:n.ci==="passing"?t.prMonitorReplyToComments&&(r?.count??0)>0?s?{kind:"disarm",note:`Monitor reached its ${cn(t)}-engagement cap with review comments still open \u2014 needs a human.`}:{kind:"engage",reason:"comments",prompt:Bd(e,r)}:{kind:"disarm",note:t.prMonitorReplyToComments?"CI green and no open review comments \u2014 ready for review.":"CI is green \u2014 ready for review."}:{kind:"stamp"}}function Fi(e){let t=(e.prLinks??[]).map(n=>n.url).filter(Boolean);return t.length===0?"the open PR":t.length===1?t[0]:`this ticket's PRs:
|
|
112
112
|
${t.map(n=>`- ${n}`).join(`
|
|
113
|
-
`)}`}function
|
|
113
|
+
`)}`}function Md(e,t){let n=Fi(e),r=t.failingChecks.length?t.failingChecks.join(", "):"one or more checks";return[`This is an automated PR-monitor pass for ${n}
|
|
114
114
|
|
|
115
115
|
CI is FAILING (${r}).`,"Continue in your existing worktree(s)/branch(es) (per the note above): diagnose and FIX the failing checks, commit, and push to the SAME branch(es) to update the affected PR(s).","Do EXACTLY ONE pass of work, then STOP \u2014 the system re-checks CI on its own schedule, so do NOT wait, poll, sleep, or loop. If you conclude the failures aren't something you can or should fix, record a short note (add_note) explaining why and stop."].join(`
|
|
116
116
|
|
|
117
|
-
`)}function
|
|
117
|
+
`)}function Bd(e,t){let n=Fi(e),r=t.digest.map(s=>`- ${s}`).join(`
|
|
118
118
|
`);return[`This is an automated PR-monitor pass for ${n}
|
|
119
119
|
|
|
120
120
|
CI is green, but there are unresolved review comments:`,r,"In your existing worktree(s)/branch(es) (per the note above), address each one: make the requested change and reply on the thread, OR \u2014 if a comment is wrong or doesn't make sense \u2014 reply explaining why you're not making the change. Commit and push any code changes to the SAME branch(es), then RESOLVE the threads you've handled (`gh api graphql` with resolveReviewThread). Keep replies concise and factual.","Do EXACTLY ONE pass, then STOP \u2014 the system re-checks on its own schedule, so do NOT wait, poll, sleep, or loop."].join(`
|
|
121
121
|
|
|
122
|
-
`)}var Ft=3,m=zn();m.use(Bd());m.use(zn.json());var cn=Number(process.env.PIPELINES_PORT||process.env.PIPELINES_MOCK_PORT||63210),Zr=Number(process.env.PIPELINES_PUBLIC_PORT||cn),Gd=e=>new Promise(t=>setTimeout(t,e)),rt=e=>I[e]??"backlog";function dn(e,t){return t instanceof pe?e.status(t.status).json({error:t.message,...t.code?{code:t.code}:{},...t.extra??{}}):(console.error(t),e.status(500).json({error:"internal error"}))}function ge(e,t){return t instanceof P?e.status(t.status).json({error:t.message,...t.extra??{}}):(console.error(t),e.status(500).json({error:"internal error"}))}function it(e,t){return t instanceof q?e.status(t.status).json({error:t.message,...t.code?{code:t.code}:{},...t.extra??{}}):(console.error(t),e.status(500).json({error:"internal error"}))}var Hi=()=>new Set(l.connectedWorkspaces??[]),Xn=e=>l.workspaceDirs?.[e]??"~";function Yn(){let e=Hi();B.length=0,B.push(...Xt().filter(t=>e.has(t.id)))}async function Ud(){let e=Xt();if(C()){l.connectedWorkspaces=e.map(r=>r.id);return}if(!Array.isArray(l.connectedWorkspaces)){let r=[...new Set(b.filter(s=>s.workspaceId).map(s=>s.workspaceId))].filter(s=>e.some(o=>o.id===s)).slice(0,xn);l.connectedWorkspaces=r,await se()}Yn();let t={...l.workspaceDirs??{}},n=!1;for(let r of B)t[r.id]||(t[r.id]="~",n=!0);n&&(l.workspaceDirs=t,await se()),await pn()}m.get("/api/session",(e,t)=>t.json(Se));m.get("/api/workspaces",(e,t)=>t.json(B));m.get("/api/teams",async(e,t)=>{if(C())return t.json(Ee);let n=typeof e.query.workspaceId=="string"?e.query.workspaceId:"";if(!n)return t.json([]);let r=await Yt(n);t.json(r?.teams??[])});m.get("/api/agents",async(e,t)=>{if(C())return t.json(Ce);let n=typeof e.query.workspaceId=="string"?e.query.workspaceId:"";if(!n)return t.json([]);let r=await Yt(n);t.json(r?.agents??[])});m.get("/api/workspaces/available",(e,t)=>{let n=Hi();t.json(Xt().map(r=>({...r,connected:n.has(r.id),workingDir:Xn(r.id)})))});m.post("/api/workspaces/:id/connect",async(e,t)=>{let n=Xt().find(s=>s.id===e.params.id);if(!n)return t.status(404).json({error:"workspace not accessible"});let r=l.connectedWorkspaces??[];if(!r.includes(n.id)){if(r.length>=xn)return t.status(409).json({error:`You can connect at most ${xn} workspaces.`});l.connectedWorkspaces=[...r,n.id]}return l.workspaceDirs?.[n.id]||(l.workspaceDirs={...l.workspaceDirs??{},[n.id]:"~"}),await se(),Yn(),C()||await pn(),t.json({id:n.id,connected:!0})});m.post("/api/workspaces/:id/working-dir",async(e,t)=>{let n=e.params.id,r=String(e.body?.workingDir??"").trim()||"~";return l.workspaceDirs={...l.workspaceDirs??{},[n]:r},await se(),t.json({id:n,workingDir:r})});m.post("/api/workspaces/:id/disconnect",async(e,t)=>{let n=e.params.id,r=l.connectedWorkspaces??[];return r.includes(n)&&(l.connectedWorkspaces=r.filter(s=>s!==n),await se(),Yn()),t.json({id:n,connected:!1})});m.get("/api/database/export",(e,t)=>{let n=b.map(r=>({...r,ticketCount:h.filter(s=>s.connectionId===r.id).length}));t.json({version:2,workspaces:B,teams:Ee,agents:Ce,connections:n,tickets:h,settings:l,schedules:O,scheduleOrder:ne,scheduleRuns:z,searchable:re,notes:j,openQuestions:D,messagePresets:H,state:G})});m.post("/api/database/import",async(e,t)=>{let n=e.body;if(!n||typeof n!="object")return t.status(400).json({error:"invalid snapshot"});let r=(o,i)=>{Array.isArray(i)&&(o.length=0,o.push(...i))},s=(o,i)=>{if(i&&typeof i=="object"){for(let c of Object.keys(o))delete o[c];Object.assign(o,i)}};r(B,n.workspaces),r(Ee,n.teams),r(Ce,n.agents),r(b,n.connections);for(let o of b)o.provider!=="local"&&(o.authStatus="expired");r(h,n.tickets),r(re,n.searchable),r(j,n.notes),r(D,n.openQuestions),r(H,n.messagePresets),r(ne,n.scheduleOrder),s(O,n.schedules),s(z,n.scheduleRuns),n.settings&&typeof n.settings=="object"&&Object.assign(l,n.settings),n.state&&typeof n.state=="object"&&Object.assign(G,n.state);for(let o of Object.keys(x))delete x[o];for(let o of Object.keys(T))delete T[o];for(let o of Object.keys(I))delete I[o];for(let o of Object.keys(W))delete W[o];return X.length=0,ce.length=0,await _s(),t.json({ok:!0})});m.post("/api/database/reset",async(e,t)=>(await As(),Yn(),t.json({ok:!0})));m.get("/api/fs/browse",(e,t)=>{let n=String(e.query.path??"").trim(),r=e.query.files==="1",s=n||Jn.homedir();s==="~"?s=Jn.homedir():s.startsWith("~/")&&(s=te.join(Jn.homedir(),s.slice(2))),s=te.resolve(s);try{let o=Ne.readdirSync(s,{withFileTypes:!0}).filter(c=>!c.name.startsWith(".")).filter(c=>c.isDirectory()||r&&c.isFile()).map(c=>({name:c.name,path:te.join(s,c.name),type:c.isDirectory()?"dir":"file"})).sort((c,d)=>c.type!==d.type?c.type==="dir"?-1:1:c.name.localeCompare(d.name)),i=te.dirname(s);return t.json({path:s,parent:i===s?null:i,home:Jn.homedir(),entries:o})}catch(o){return t.status(400).json({error:`can't read ${s}: ${String(o)}`})}});m.get("/api/connections",(e,t)=>t.json(b));function es(e){let t=e??{},n=Array.isArray(t.labels)?t.labels.map(i=>String(i).trim()).filter(Boolean).slice(0,3):[],r=!!t.assignedToMe,s=r?"":String(t.assignee??"").trim(),o={};return n.length&&(o.labels=n),r?o.assignedToMe=!0:s&&(o.assignee=s),Object.keys(o).length?o:null}var Qi={linear:"Linear",jira:"Jira",asana:"Asana",clickup:"ClickUp",monday:"Monday"},qd=new Set(Object.keys(Qi));function Ji(e,t,n){return e==="jira"?li(t,n):e==="asana"?gi(t,n):e==="clickup"?bi(t,n):e==="monday"?Ri(t,n):ci(t,n)}m.post("/api/connections",async(e,t)=>{let n=String(e.body?.provider??"");if(!qd.has(n))return t.status(400).json({error:"unsupported provider"});let r=Qi[n],s=String(e.body?.name??"").trim(),o=String(e.body?.workingDir??"").trim(),i=B.find(p=>p.id===e.body?.workspaceId);if(e.body?.workspaceId&&!i)return t.status(400).json({error:"workspace not accessible"});if(i&&b.filter(g=>g.provider!=="local"&&g.linkStatus==="linked"&&g.workspaceId===i.id).length>=Ft)return t.json({configured:!0,message:`That workspace already has ${Ft} connections (the max).`});let d={id:`conn_${ot().slice(0,8)}`,provider:n,providerAccountName:C()?`${r} (demo)`:r,...s?{displayName:s}:{},workspaceId:i?.id??null,workspaceName:i?.name??null,workingDir:o||null,linkStatus:i?"linked":"unlinked",authStatus:C()?"connected":"needs_refresh",lastSyncedAt:null,ticketCount:0,syncFilters:es(e.body?.syncFilters)};if(C())return b.push(d),await A(d),t.status(201).json({connection:d,mock:!0});let u=await Ji(n,d.id,Zr);return u.url?(b.push(d),await A(d),t.status(201).json({connection:d,url:u.url})):t.json({configured:u.configured===!1?!1:void 0,message:u.error??`Couldn't start the ${r} connection.`})});m.post("/api/connections/:id/link",async(e,t)=>{let n=b.find(o=>o.id===e.params.id);if(!n)return t.status(404).json({error:"connection not found"});let r=B.find(o=>o.id===e.body?.workspaceId);return r?b.filter(o=>o.id!==n.id&&o.provider!=="local"&&o.linkStatus==="linked"&&o.workspaceId===r.id).length>=Ft?t.status(409).json({error:`A workspace can have at most ${Ft} connections.`}):(n.workspaceId=r.id,n.workspaceName=r.name,typeof e.body?.workingDir=="string"&&e.body.workingDir.trim()&&(n.workingDir=e.body.workingDir.trim()),typeof e.body?.name=="string"&&e.body.name.trim()&&(n.displayName=e.body.name.trim()),"syncFilters"in(e.body??{})&&n.provider!=="local"&&(n.syncFilters=es(e.body.syncFilters)),n.linkStatus="linked",delete n.linkLostReason,n.lastSyncedAt=new Date().toISOString(),await A(n),t.json(n)):t.status(400).json({error:"workspace not accessible"})});m.patch("/api/connections/:id",async(e,t)=>{let n=b.find(r=>r.id===e.params.id);if(!n)return t.status(404).json({error:"connection not found"});if(typeof e.body?.name=="string"){let r=e.body.name.trim();r?n.displayName=r:delete n.displayName}if(e.body?.unlink===!0){if(n.provider==="local")return t.status(400).json({error:"a local board can't be unlinked"});n.workspaceId=null,n.workspaceName=null,n.workingDir=null,n.linkStatus="unlinked"}if(typeof e.body?.workspaceId=="string"&&n.provider!=="local"){let r=B.find(s=>s.id===e.body.workspaceId);if(!r)return t.status(400).json({error:"workspace not accessible"});if(r.id!==n.workspaceId&&b.filter(o=>o.id!==n.id&&o.provider!=="local"&&o.linkStatus==="linked"&&o.workspaceId===r.id).length>=Ft)return t.status(409).json({error:`A workspace can have at most ${Ft} connections.`});n.workspaceId=r.id,n.workspaceName=r.name,n.linkStatus="linked"}return typeof e.body?.workingDir=="string"&&e.body.workingDir.trim()&&(n.workingDir=e.body.workingDir.trim()),"syncFilters"in(e.body??{})&&n.provider!=="local"&&(n.syncFilters=es(e.body.syncFilters)),await A(n),t.json(n)});m.delete("/api/connections/:id",async(e,t)=>{let n=b.find(s=>s.id===e.params.id);if(!n)return t.status(404).json({error:"connection not found"});if(n.provider==="local")return t.status(400).json({error:"a local board can't be disconnected"});for(let s of h.filter(o=>o.connectionId===n.id)){let o=T[s.id];o&&(await St(o),delete x[o],delete T[s.id]),delete I[s.id],await Dn(s.id),await fn(s.id)}for(let s=h.length-1;s>=0;s--)h[s].connectionId===n.id&&h.splice(s,1);for(let s=re.length-1;s>=0;s--)re[s].connectionId===n.id&&re.splice(s,1);let r=b.findIndex(s=>s.id===n.id);return r!==-1&&b.splice(r,1),await gs(n.id),await Ei(n.id),t.status(204).end()});m.post("/api/connections/:id/oauth/start",async(e,t)=>{let n=b.find(s=>s.id===e.params.id);if(!n)return t.status(404).json({error:"connection not found"});if(!on(n.provider))return t.status(400).json({error:"not a vendor connection"});let r=await Ji(n.provider,n.id,Zr);return r.url?t.json({url:r.url}):t.status(r.configured===!1?501:502).json({error:r.error??"Couldn't start OAuth."})});m.get("/api/oauth/linear/deposit",async(e,t)=>{let n=String(e.query.code??""),r=String(e.query.connectionId??""),s=b.find(i=>i.id===r);if(!n||!s)return t.redirect("/settings/connections?linear=error");let o=await di(n);return o?(await nt({connectionId:s.id,provider:"linear",accessToken:o.accessToken,refreshToken:o.refreshToken,scope:o.scope,expiresAt:o.expiresAt}),s.authStatus="connected",await A(s),t.redirect("/settings/connections?linear=connected")):(s.authStatus="error",await A(s),t.redirect("/settings/connections?linear=error"))});m.get("/api/oauth/jira/deposit",async(e,t)=>{let n=String(e.query.code??""),r=String(e.query.connectionId??""),s=b.find(u=>u.id===r);if(!n||!s)return t.redirect("/settings/connections?jira=error");let o=await pi(n);if(!o)return s.authStatus="error",await A(s),t.redirect("/settings/connections?jira=error");let c=(await mi(o.accessToken))[0];if(!c?.id)return s.authStatus="error",await A(s),t.redirect("/settings/connections?jira=error");await nt({connectionId:s.id,provider:"jira",accessToken:o.accessToken,refreshToken:o.refreshToken,scope:o.scope,expiresAt:o.expiresAt}),s.cloudId=c.id;let d=c.url?c.url.replace(/^https?:\/\//,"").replace(/\/+$/,""):void 0;return s.providerAccountName=d||c.name||"Jira",s.authStatus="connected",await A(s),t.redirect("/settings/connections?jira=connected")});m.get("/api/oauth/asana/deposit",async(e,t)=>{let n=String(e.query.code??""),r=String(e.query.connectionId??""),s=b.find(d=>d.id===r);if(!n||!s)return t.redirect("/settings/connections?asana=error");let o=await hi(n);if(!o)return s.authStatus="error",await A(s),t.redirect("/settings/connections?asana=error");let c=(await Vo(o.accessToken))[0];return c?.gid?(await nt({connectionId:s.id,provider:"asana",accessToken:o.accessToken,refreshToken:o.refreshToken,scope:o.scope,expiresAt:o.expiresAt}),s.providerAccountId=c.gid,s.providerAccountName=c.name||"Asana",s.authStatus="connected",await A(s),t.redirect("/settings/connections?asana=connected")):(s.authStatus="error",await A(s),t.redirect("/settings/connections?asana=error"))});m.get("/api/oauth/clickup/deposit",async(e,t)=>{let n=String(e.query.code??""),r=String(e.query.connectionId??""),s=b.find(d=>d.id===r);if(!n||!s)return t.redirect("/settings/connections?clickup=error");let o=await Si(n);if(!o)return s.authStatus="error",await A(s),t.redirect("/settings/connections?clickup=error");let c=(await ni(o.accessToken))[0];return c?.id?(await nt({connectionId:s.id,provider:"clickup",accessToken:o.accessToken}),s.providerAccountId=c.id,s.providerAccountName=c.name||"ClickUp",s.authStatus="connected",await A(s),t.redirect("/settings/connections?clickup=connected")):(s.authStatus="error",await A(s),t.redirect("/settings/connections?clickup=error"))});m.get("/api/oauth/monday/deposit",async(e,t)=>{let n=String(e.query.code??""),r=String(e.query.connectionId??""),s=b.find(c=>c.id===r);if(!n||!s)return t.redirect("/settings/connections?monday=error");let o=await Ti(n);if(!o)return s.authStatus="error",await A(s),t.redirect("/settings/connections?monday=error");let i=await ai(o.accessToken);return await nt({connectionId:s.id,provider:"monday",accessToken:o.accessToken}),i?.slug&&(s.providerAccountId=i.slug),s.providerAccountName=i?.name||"Monday",s.authStatus="connected",await A(s),t.redirect("/settings/connections?monday=connected")});var Fd=6e4,Wi={getToken:Hn,saveToken:nt,refresh:(e,t)=>{let n=on(e);return n?n.refresh(t):Promise.resolve(null)}};async function Wd(e){let t=on(e.provider);if(!t)throw Object.assign(new Error("not a vendor connection"),{terminal:!0});let n=await Ci(e.id,Wi);if(!n)throw Object.assign(new Error("no usable token"),{terminal:!0});let r=h.filter(u=>u.connectionId===e.id&&u.id.startsWith(t.idPrefix)).map(u=>u.id.slice(t.idPrefix.length)),s=u=>Promise.all([t.fetchDiscovery(e,u,e.syncFilters),t.fetchByIds(e,u,r)]),o,i;try{[o,i]=await s(n)}catch(u){let p=u?.status;if(p!==401&&p!==403)throw u;let g=await Hn(e.id),S=g?await Qr(e.id,g,Wi):null;if(!S){let f=u instanceof Error?u:new Error(String(u));throw Object.assign(f,{terminal:!0})}[o,i]=await s(S)}let{refreshed:c,pool:d}=jo(o,i,e.id,h,re);for(let u of c)await N(u);return e.lastSyncedAt=new Date().toISOString(),e.ticketCount=h.filter(u=>u.connectionId===e.id).length,e.authStatus="connected",await A(e),{refreshed:c.length,available:d.length}}async function ts(e){if(C())return e.lastSyncedAt=new Date().toISOString(),await A(e),{synced:!0};if(!on(e.provider))return{synced:!1,skipped:"not_syncable"};if(!await Hn(e.id))return e.authStatus==="connected"&&(e.authStatus="expired",await A(e)),{synced:!1,skipped:"not_syncable"};let t=re.filter(r=>r.connectionId===e.id).length;if((e.lastSyncedAt?Date.now()-Date.parse(e.lastSyncedAt):1/0)<Fd&&t>0)return{synced:!1,skipped:"fresh"};try{return{synced:!0,...await Wd(e)}}catch(r){return e.authStatus=r?.terminal?"expired":"needs_refresh",await A(e),{synced:!1,error:String(r)}}}function Kd(){return async(e,t,n)=>{let r=h.find(o=>o.id===e.params.id),s=r?b.find(o=>o.id===r.connectionId):void 0;s&&await ts(s),n()}}m.post("/api/connections/:id/sync",async(e,t)=>{let n=b.find(s=>s.id===e.params.id);if(!n)return t.status(404).json({error:"connection not found"});let r=await ts(n);return r.error?t.status(502).json({error:`sync failed: ${r.error}`}):t.json({synced:r.synced,skipped:r.skipped,refreshed:r.refreshed??0,available:r.available})});m.get("/api/board",(e,t)=>{let n=String(e.query.connectionId??""),r=new Set(b.filter(o=>o.linkStatus==="linked").map(o=>o.id)),s=h.filter(o=>r.has(o.connectionId)&&(!n||o.connectionId===n)).map(o=>{let i=T[o.id],c=i?x[i]:void 0;return{ticket:o,column:rt(o.id),blockedByDeps:It(o,d=>h.find(u=>u.id===d),rt).length>0,run:c?{id:c.id,ticketId:c.ticketId,status:c.status,stopReason:c.stopReason,branchName:c.branchName,assignmentKind:c.assignmentKind,teamName:c.teamName,agentName:c.agentName,autonomy:c.autonomy,prUrl:c.prUrl,blocker:c.blocker}:void 0}});t.json(s)});m.post("/api/tickets/:id/move",async(e,t)=>{try{return t.json(await Nt(e.params.id,e.body?.column))}catch(n){return ge(t,n)}});m.post("/api/tickets/:id/reorder",async(e,t)=>{let n=h.findIndex(i=>i.id===e.params.id);if(n===-1)return t.status(404).json({error:"ticket not found"});let[r]=h.splice(n,1),s=e.body?.beforeId,o=s?h.findIndex(i=>i.id===s):-1;return o===-1?h.push(r):h.splice(o,0,r),await hs(),t.json({ticketId:r.id})});m.get("/api/tickets/:id",Kd(),async(e,t)=>{let n=h.find(u=>u.id===e.params.id);if(!n)return t.status(404).json({error:"ticket not found"});let r=T[n.id],s=r?x[r]:null;if(s&&s.sessionId&&s.status!=="running"){let u=X.some(p=>p.ticketId===n.id);if(!s.resultText||!u){let p=ar(s.sessionId);if(p.response&&!s.resultText&&(s.resultText=p.response.slice(0,5e4),await U(s.id)),p.response&&!u){let g={id:`rr_${ot()}`,ticketId:n.id,runId:s.id,sessionId:s.sessionId,prompt:(p.prompt??"").slice(0,5e4),response:p.response.slice(0,5e4),status:"completed",createdAt:s.updatedAt??new Date().toISOString()};X.unshift(g),await gn(g)}}}let o=X.find(u=>u.ticketId===n.id)??null,i=b.find(u=>u.id===n.connectionId),{dependencies:c,blockedByDeps:d}=En(n);return t.json({ticket:n,column:rt(n.id),run:s,latestResult:o,connectionName:i?.displayName??i?.providerAccountName??"\u2014",workspaceName:i?.workspaceName??null,dependencies:c,blockedByDeps:d})});m.get("/api/tickets/:id/history",async(e,t)=>{let n=Math.min(Math.max(Number(e.query.limit)||10,1),100),r=Math.max(Number(e.query.offset)||0,0);t.json(await Ss(e.params.id,n,r))});m.get("/api/tickets/:id/notes",(e,t)=>{t.json(_t(e.params.id))});m.post("/api/tickets/:id/notes",async(e,t)=>{try{let n=await Pt(e.params.id,{body:String(e.body?.body??""),author:e.body?.author});return t.status(201).json(n)}catch(n){return ge(t,n)}});m.get("/api/schedules/:id/notes",(e,t)=>{let n=j.filter(r=>r.scheduleId===e.params.id).slice().reverse();t.json(n)});m.post("/api/schedules/:id/notes",async(e,t)=>{let n=O[e.params.id];if(!n)return t.status(404).json({error:"schedule not found"});let r=String(e.body?.body??"").trim();if(!r)return t.status(400).json({error:"empty note"});let s=e.body?.author==="assistant"?"assistant":"user",o=new Date().toISOString(),i={id:`note_${ot()}`,scheduleId:n.id,author:s,body:r.slice(0,5e3),createdAt:o,updatedAt:o};return j.push(i),await ht(i),t.status(201).json(i)});m.patch("/api/notes/:id",async(e,t)=>{try{return t.json(await Ot(e.params.id,String(e.body?.body??"")))}catch(n){return ge(t,n)}});m.delete("/api/notes/:id",async(e,t)=>{try{return await Dt(e.params.id),t.json({ok:!0})}catch(n){return ge(t,n)}});m.get("/api/tickets/:id/open-questions",(e,t)=>{t.json(At(e.params.id))});m.post("/api/tickets/:id/open-questions",async(e,t)=>{try{let n=await jt(e.params.id,String(e.body?.question??""));return t.status(201).json(n)}catch(n){return ge(t,n)}});m.patch("/api/open-questions/:qid",async(e,t)=>{try{return t.json(await Mt(e.params.qid,e.body??{}))}catch(n){return ge(t,n)}});m.delete("/api/open-questions/:qid",async(e,t)=>{try{return await $t(e.params.qid),t.json({ok:!0})}catch(n){return ge(t,n)}});var st=te.join(te.dirname(un),"pipelines-uploads"),Hd={png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",webp:"image/webp",pdf:"application/pdf",json:"application/json",txt:"text/plain",md:"text/markdown",csv:"text/csv"},Qd=/^paste-[a-f0-9]+(\.[a-z0-9]+)?$/i,Jd=e=>(e.split(".").pop()??"").toLowerCase().replace(/[^a-z0-9]/g,"").slice(0,8);m.get("/api/uploads/stats",async(e,t)=>{let n=0,r=0,s=await Ne.promises.readdir(st).catch(()=>[]);for(let o of s){let i=await Ne.promises.stat(te.join(st,o)).catch(()=>null);i?.isFile()&&(n++,r+=i.size)}return t.json({count:n,bytes:r})});m.post("/api/uploads/clear",async(e,t)=>{let n=0,r=0,s=await Ne.promises.readdir(st).catch(()=>[]);for(let o of s){let i=te.join(st,o),c=await Ne.promises.stat(i).catch(()=>null);c?.isFile()&&(r+=c.size,await Ne.promises.unlink(i).catch(()=>{}),n++)}return t.json({removed:n,bytes:r})});m.post("/api/uploads",zn.raw({type:"application/octet-stream",limit:"25mb"}),async(e,t)=>{let n=e.body;if(!Buffer.isBuffer(n)||n.length===0)return t.status(400).json({error:"empty upload"});let r=Jd(String(e.query.name??""));await Ne.promises.mkdir(st,{recursive:!0});let s=`paste-${ot().slice(0,8)}${r?`.${r}`:""}`,o=te.join(st,s);return await Ne.promises.writeFile(o,n),t.status(201).json({path:o,filename:s})});m.get("/api/uploads/:name",(e,t)=>{let n=e.params.name;if(!Qd.test(n))return t.status(400).end();let r=n.split(".").pop().toLowerCase();return t.type(Hd[r]??"application/octet-stream"),t.sendFile(n,{root:st},s=>{s&&!t.headersSent&&t.status(404).end()})});var zi=e=>e.split(/[/\\]/).pop()??e,Ki=10;m.get("/api/message-presets",(e,t)=>{t.json(H.slice().sort((n,r)=>n.position-r.position))});m.post("/api/message-presets",async(e,t)=>{if(H.length>=Ki)return t.status(422).json({error:`at most ${Ki} presets`});let n=String(e.body?.title??"").trim(),r=String(e.body?.prompt??"").trim();if(!n||!r)return t.status(400).json({error:"title and prompt are required"});let s=new Date().toISOString(),o={id:`preset_${ot()}`,title:n.slice(0,50),prompt:r.slice(0,5e3),enabled:e.body?.enabled!==!1,position:H.length,createdAt:s,updatedAt:s};return H.push(o),await kn(o.id),t.status(201).json(o)});m.patch("/api/message-presets/:id",async(e,t)=>{let n=H.find(r=>r.id===e.params.id);if(!n)return t.status(404).json({error:"preset not found"});if(typeof e.body?.title=="string"){let r=e.body.title.trim();if(!r)return t.status(400).json({error:"empty title"});n.title=r.slice(0,50)}if(typeof e.body?.prompt=="string"){let r=e.body.prompt.trim();if(!r)return t.status(400).json({error:"empty prompt"});n.prompt=r.slice(0,5e3)}return typeof e.body?.enabled=="boolean"&&(n.enabled=e.body.enabled),n.updatedAt=new Date().toISOString(),await kn(n.id),t.json(n)});m.delete("/api/message-presets/:id",async(e,t)=>{let n=H.findIndex(r=>r.id===e.params.id);return n===-1?t.status(404).json({error:"preset not found"}):(H.splice(n,1),H.forEach((r,s)=>r.position=s),await xs(e.params.id),await vs(),t.json({ok:!0}))});m.post("/api/tickets/:id/refresh",async(e,t)=>{let n=h.find(r=>r.id===e.params.id);return n?(await Gd(400),t.json({ticket:n,refreshedAt:new Date().toISOString()})):t.status(404).json({error:"ticket not found"})});m.get("/api/search",(e,t)=>{let n=String(e.query.q??"").trim().toLowerCase(),r=String(e.query.connectionId??""),s=Math.min(Math.max(Number(e.query.limit)||20,1),100),o=Math.max(Number(e.query.offset)||0,0),i=new Set(b.filter(p=>p.linkStatus==="linked").map(p=>p.id)),c=new Set(h.map(p=>p.externalIdentifier)),d=re.filter(p=>r?p.connectionId===r:i.has(p.connectionId)).filter(p=>i.has(p.connectionId)).filter(p=>!n||p.title.toLowerCase().includes(n)||p.externalIdentifier.toLowerCase().includes(n)),u=d.slice(o,o+s).map(p=>({externalId:p.id,externalIdentifier:p.externalIdentifier,title:p.title,status:p.status,statusRaw:p.statusRaw,provider:p.provider,connectionId:p.connectionId,url:p.url,imported:c.has(p.externalIdentifier)}));t.json({hits:u,total:d.length})});m.post("/api/import",async(e,t)=>{let n=re.findIndex(s=>s.id===e.body?.externalId);if(n===-1)return t.status(404).json({error:"ticket not in search results"});let[r]=re.splice(n,1);return r.workspaceId&&(r.workingDir=Xn(r.workspaceId)),r.model=xe(l.defaultModel),r.effort=ve(l.defaultEffort),h.push(r),I[r.id]="backlog",await N(r.id),t.status(201).json(r)});m.patch("/api/tickets/:id",async(e,t)=>{try{return t.json(await Te(e.params.id,e.body??{}))}catch(n){return ge(t,n)}});m.post("/api/board/:connectionId/tickets",async(e,t)=>{try{let n=await Ct(e.params.connectionId,e.body??{});return t.status(201).json(n)}catch(n){return ge(t,n)}});m.post("/api/tickets/:id/duplicate",async(e,t)=>{let n=h.find(c=>c.id===e.params.id);if(!n)return t.status(404).json({error:"ticket not found"});if(n.provider!=="local")return t.status(400).json({error:"only local tickets can be duplicated"});let r=b.find(c=>c.id===n.connectionId),s=(String(e.body?.title??"").trim()||n.title).slice(0,200),o=++G.localSeq,i={id:`t_local${o}`,connectionId:n.connectionId,provider:"local",externalIdentifier:`LOCAL-${o}`,project:"Local",title:s,status:"todo",statusRaw:"Backlog",priority:0,labels:[],url:"",workspaceId:n.workspaceId,workingDir:n.workspaceId?Xn(n.workspaceId):"~",model:xe(l.defaultModel),effort:ve(l.defaultEffort),description:n.description,additionalContext:n.additionalContext,additionalContextUpdatedAt:n.additionalContext?new Date().toISOString():void 0,createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()};h.push(i),I[i.id]="backlog",r&&(r.ticketCount+=1),await N(i.id),r&&await A(r);for(let c of j.filter(d=>d.ticketId===n.id)){let d=new Date().toISOString(),u={id:`note_${ot()}`,ticketId:i.id,author:c.author,body:c.body,createdAt:d,updatedAt:d};j.push(u),await ht(u)}return t.status(201).json(i)});m.put("/api/tickets/:id/dependencies",async(e,t)=>{try{let n=await Cn(e.params.id,e.body?.dependsOn);return await jn(e.params.id),t.json(n)}catch(n){return ge(t,n)}});m.get("/api/runs/:id",(e,t)=>{let n=x[e.params.id];return n?t.json(n):t.status(404).json({error:"run not found"})});m.get("/api/runs/:id/transcript",(e,t)=>{let n=x[e.params.id];if(!n)return t.status(404).json({error:"run not found"});let r=n.sessionId?cr(n.sessionId):[];return t.json({events:r,status:n.status,running:n.status==="running"})});m.get("/api/sessions/:sessionId/transcript",(e,t)=>{let n=cr(e.params.sessionId);return t.json({events:n,status:"ended",running:!1})});m.post("/api/runs",async(e,t)=>{try{let n=await wr(e.body);return t.status(201).json(n)}catch(n){return dn(t,n)}});m.post("/api/runs/:id/start",async(e,t)=>{try{let n=await qe(e.params.id,{details:String(e.body?.additionalDetails??""),attachments:ns(e.body)});return t.json(n)}catch(n){return dn(t,n)}});m.post("/api/runs/:id/pause",async(e,t)=>{try{return t.json(await vt(e.params.id))}catch(n){return dn(t,n)}});m.post("/api/runs/:id/resume",async(e,t)=>{try{return t.json(await Rt(e.params.id))}catch(n){return dn(t,n)}});m.post("/api/tickets/:id/unblock",async(e,t)=>{try{let n=await Et(e.params.id,{resume:e.body?.resume===!0});return t.json(n)}catch(n){return ge(t,n)}});m.get("/api/tickets/:id/repos",async(e,t)=>{let n=h.find(o=>o.id===e.params.id);if(!n)return t.status(404).json({error:"ticket not found"});let r=n.workingDir?.trim();if(!r)return t.json({repos:[],note:"ticket has no working dir set"});let s=await Ge(r);return t.json({repos:s.map(o=>o.name),singleRepo:s.length===1&&s[0].name==="."})});m.post("/api/tickets/:id/worktree",async(e,t)=>{let n=h.find(at=>at.id===e.params.id);if(!n)return t.status(404).json({error:"ticket not found"});let r=T[n.id],s=r?x[r]:void 0;if(!s)return t.status(409).json({error:"ticket has no run"});let o=n.workingDir?.trim();if(!o)return t.status(409).json({error:"ticket has no working dir set"});let i=await Ls(o,e.body?.repo);if(!i){let at=await Ge(o);return t.status(400).json({error:at.length===0?"no git repositories found in the working dir":`name the repo to work on; found: ${at.map(ra=>ra.name).join(", ")}`})}let c=s.worktrees??=[],d=c.find(at=>at.repo===i.name);if(d)return t.json(d);let u=(l.branchPrefix||"feature").trim().replace(/\/+$/,""),p=i.name==="."?"":`-${i.name}`,g=n.attempt??1,S=g>1?`-v${g}`:"",f=`${u}/${n.externalIdentifier}${p}${S}`,w=await Gs(i.path),Y=await pr(i.path,w),He=await uo(n.id,i.name,i.path,Y);if(He.ambiguous)return t.status(409).json({code:"ambiguous_base",error:`${n.externalIdentifier} depends on multiple tickets that both changed ${i.name} and aren't merged yet (${He.ambiguous.join(", ")}) \u2014 chain them (make one depend on the other) so there's a single base to build on. This is a hard block, not a transient error: do NOT retry start_repo_work for this repo. Block this ticket and stop; a human must chain the dependencies.`});He.startPoint&&(w=He.startPoint,Y=He.startPoint);let na=(i.name==="."?"repo":i.name)+S,rs=te.join(lr,n.externalIdentifier,na),ss=await qs({repoPath:i.path,worktreePath:rs,branch:f,base:w,startPoint:Y});if(!ss.ok)return t.status(500).json({error:ss.error});let os={repo:i.name,branch:f,worktreePath:rs,base:w,compareUrl:await Ks(i.path,w,f),createdAt:new Date().toISOString()};return c.push(os),s.branchName=s.branchName||f,s.events.push({id:`e${s.events.length+1}`,ts:be(),kind:"mcp_tool",text:`claimed worktree for ${i.name} \u2192 ${f}`}),await U(s.id),t.json(os)});m.post("/api/tickets/:id/pr-links",async(e,t)=>{let n=h.find(i=>i.id===e.params.id);if(!n)return t.status(404).json({error:"ticket not found"});let r=String(e.body?.url??"").trim();if(!/^https?:\/\//.test(r))return t.status(400).json({error:"a valid http(s) PR url is required"});let s=n.prLinks??=[];s.some(i=>i.url===r)||s.push({url:r,title:String(e.body?.title??"").trim()||void 0,createdAt:new Date().toISOString()}),await N(n.id);let o=x[T[n.id]];return o&&(o.prUrl=r,await U(o.id)),!C()&&s.length>1&&Gi(n.externalIdentifier,s.map(i=>i.url)).catch(()=>{}),t.status(201).json({prLinks:s})});m.get("/api/tickets/:id/pr-links",(e,t)=>{let n=h.find(r=>r.id===e.params.id);return t.json({prLinks:n?.prLinks??[]})});m.post("/api/tickets/:id/monitor",async(e,t)=>{let n=h.find(s=>s.id===e.params.id);if(!n)return t.status(404).json({error:"ticket not found"});if(e.body?.on===!0){if(!n.prLinks?.length)return t.status(409).json({error:"no PR to monitor yet"});if(!T[n.id])return t.status(409).json({error:"no run/session to continue from"});n.prMonitor=!0,n.prMonitorNote=void 0,n.prMonitorLastAt=void 0,(n.prMonitorCount??0)>=an(l)&&(n.prMonitorCount=0)}else n.prMonitor=!1;return await N(n.id),t.json({prMonitor:n.prMonitor})});var zr=null,Xr=!1;async function zd(e,t,n){let r=new Date().toISOString(),s=o=>t.events.push({id:`e${t.events.length+1}`,ts:be(),kind:"state_change",text:o});if(n.kind==="stamp"){e.prMonitorLastAt=r,await N(e.id);return}if(n.kind==="engage"){e.prMonitorCount=(e.prMonitorCount??0)+1,e.prMonitorLastAt=r,await N(e.id),t.status="running",t.stopReason=void 0,t.updatedAt=r,s(`PR monitor: ${n.reason==="comments"?"open review comments":"CI failing"} \u2192 engaging agent (pass ${e.prMonitorCount})`),await U(t.id),Re(t.id,!0,n.prompt);return}e.prMonitor=!1,e.prMonitorNote=n.note,e.prMonitorLastAt=r,n.merged&&(e.prMerged=!0),s(`PR monitor: ${n.note}`),await U(t.id),await N(e.id)}async function Xd(){if(!(C()||Xr)){Xr=!0;try{let e=Date.now();for(let t of h){if(!t.prMonitor)continue;let n=rt(t.id);if(n!=="in_review"&&n!=="done")continue;let r=T[t.id]?x[T[t.id]]:void 0;if(!r||r.status==="running"||!Ui(t,l,e))continue;let s=(t.prLinks??[]).map(c=>c.url).filter(Boolean);if(!s.length){t.prMonitorLastAt=new Date().toISOString(),await N(t.id);continue}let o=await Mi(s),i=o.ci==="passing"&&l.prMonitorReplyToComments?await $i(s):void 0;await zd(t,r,qi(t,l,o,i))}}finally{Xr=!1}}}function Yd(){zr||(zr=setInterval(()=>void Xd(),6e4),zr.unref?.())}m.post("/api/runs/:id/stop",async(e,t)=>{try{return t.json(await Fe(e.params.id))}catch(n){return dn(t,n)}});m.post("/api/runs/:id/complete",async(e,t)=>{let n=x[e.params.id];return n?(n.status="stopped",n.stopReason="completed",n.blocker=void 0,n.events.push({id:`e${n.events.length+1}`,ts:be(),kind:"mcp_tool",text:"mark_complete(...)"}),n.updatedAt=new Date().toISOString(),I[n.ticketId]="in_review",await U(n.id),await N(n.ticketId),t.json(n)):t.status(404).json({error:"run not found"})});m.post("/api/runs/:id/answer",async(e,t)=>{let n=x[e.params.id];if(!n||!n.blocker)return t.status(409).json({error:"no open blocker"});n.blocker.status="answered";let r=e.body?.decision?`human: ${e.body.decision}`:`human answered: ${e.body?.response??""}`;n.events.push({id:`e${n.events.length+1}`,ts:be(),kind:"mcp_tool",text:r}),n.blocker=void 0,n.status="running",n.updatedAt=new Date().toISOString(),I[n.ticketId]="in_progress",await U(n.id),await N(n.ticketId);let s=String(e.body?.decision??e.body?.response??"").trim();return C()||Re(n.id,!0,`The human responded: ${s}`),t.json(n)});m.post("/api/runs/:id/approve",async(e,t)=>{let n=x[e.params.id];return n?n.status!=="stopped"?t.status(409).json({error:"can only approve a stopped run"}):(n.events.push({id:`e${n.events.length+1}`,ts:be(),kind:"state_change",text:"approved by human \u2192 Done"}),n.updatedAt=new Date().toISOString(),I[n.ticketId]="done",await U(n.id),await N(n.ticketId),t.json(n)):t.status(404).json({error:"run not found"})});m.post("/api/runs/:id/respond",async(e,t)=>{let n=x[e.params.id];if(!n)return t.status(404).json({error:"run not found"});if(h.find(c=>c.id===n.ticketId)?.prMonitor)return t.status(409).json({error:"PR monitor is armed \u2014 turn it off to send a message"});if(n.status!=="stopped"&&n.status!=="blocked")return t.status(409).json({error:"run is not waiting for a response"});let r=String(e.body?.text??"").trim(),s=Array.isArray(e.body?.attachments)?e.body.attachments.map(c=>String(c)).filter(Boolean):[];if(!r&&s.length===0)return t.status(400).json({error:"empty response"});let o=n.status==="blocked";n.blocker&&(n.blocker=void 0),n.status="running",n.stopReason=void 0,n.events.push({id:`e${n.events.length+1}`,ts:be(),kind:o?"mcp_tool":"state_change",text:`human responded: ${r.slice(0,80)}${s.length?` (+${s.length} file${s.length>1?"s":""})`:""}`}),n.updatedAt=new Date().toISOString(),I[n.ticketId]="in_progress",await U(n.id),await N(n.ticketId);let i=r+(s.length?`
|
|
122
|
+
`)}var Wt=3,m=Xn();m.use(Ld());m.use(Xn.json());var dn=Number(process.env.PIPELINES_PORT||process.env.PIPELINES_MOCK_PORT||63210),Zr=Number(process.env.PIPELINES_PUBLIC_PORT||dn),Ud=e=>new Promise(t=>setTimeout(t,e)),rt=e=>S[e]??"backlog";function un(e,t){return t instanceof pe?e.status(t.status).json({error:t.message,...t.code?{code:t.code}:{},...t.extra??{}}):(console.error(t),e.status(500).json({error:"internal error"}))}function ge(e,t){return t instanceof P?e.status(t.status).json({error:t.message,...t.extra??{}}):(console.error(t),e.status(500).json({error:"internal error"}))}function it(e,t){return t instanceof q?e.status(t.status).json({error:t.message,...t.code?{code:t.code}:{},...t.extra??{}}):(console.error(t),e.status(500).json({error:"internal error"}))}var Hi=()=>new Set(l.connectedWorkspaces??[]),Yn=e=>l.workspaceDirs?.[e]??"~";function Vn(){let e=Hi();$.length=0,$.push(...Yt().filter(t=>e.has(t.id)))}async function qd(){let e=Yt();if(E()){l.connectedWorkspaces=e.map(r=>r.id);return}if(!Array.isArray(l.connectedWorkspaces)){let r=[...new Set(b.filter(s=>s.workspaceId).map(s=>s.workspaceId))].filter(s=>e.some(o=>o.id===s)).slice(0,vn);l.connectedWorkspaces=r,await te()}Vn();let t={...l.workspaceDirs??{}},n=!1;for(let r of $)t[r.id]||(t[r.id]="~",n=!0);n&&(l.workspaceDirs=t,await te()),await fn()}m.get("/api/session",(e,t)=>t.json(Ie));m.get("/api/workspaces",(e,t)=>t.json($));m.get("/api/teams",async(e,t)=>{if(E())return t.json(Se);let n=typeof e.query.workspaceId=="string"?e.query.workspaceId:"";if(!n)return t.json([]);let r=await yt(n);t.json(r?.teams??[])});m.get("/api/agents",async(e,t)=>{if(E())return t.json(xe);let n=typeof e.query.workspaceId=="string"?e.query.workspaceId:"";if(!n)return t.json([]);let r=await yt(n);t.json(r?.agents??[])});m.get("/api/workspaces/available",(e,t)=>{let n=Hi();t.json(Yt().map(r=>({...r,connected:n.has(r.id),workingDir:Yn(r.id)})))});m.post("/api/workspaces/:id/connect",async(e,t)=>{let n=Yt().find(s=>s.id===e.params.id);if(!n)return t.status(404).json({error:"workspace not accessible"});let r=l.connectedWorkspaces??[];if(!r.includes(n.id)){if(r.length>=vn)return t.status(409).json({error:`You can connect at most ${vn} workspaces.`});l.connectedWorkspaces=[...r,n.id]}return l.workspaceDirs?.[n.id]||(l.workspaceDirs={...l.workspaceDirs??{},[n.id]:"~"}),await te(),Vn(),E()||await fn(),t.json({id:n.id,connected:!0})});m.post("/api/workspaces/:id/working-dir",async(e,t)=>{let n=e.params.id,r=String(e.body?.workingDir??"").trim()||"~";return l.workspaceDirs={...l.workspaceDirs??{},[n]:r},await te(),t.json({id:n,workingDir:r})});m.post("/api/workspaces/:id/disconnect",async(e,t)=>{let n=e.params.id,r=l.connectedWorkspaces??[];return r.includes(n)&&(l.connectedWorkspaces=r.filter(s=>s!==n),await te(),Vn()),t.json({id:n,connected:!1})});m.get("/api/database/export",(e,t)=>{let n=b.map(r=>({...r,ticketCount:h.filter(s=>s.connectionId===r.id).length}));t.json({version:2,workspaces:$,teams:Se,agents:xe,connections:n,tickets:h,settings:l,schedules:O,scheduleOrder:se,scheduleRuns:z,searchable:Y,notes:j,openQuestions:D,messagePresets:H,state:G})});m.post("/api/database/import",async(e,t)=>{let n=e.body;if(!n||typeof n!="object")return t.status(400).json({error:"invalid snapshot"});let r=(o,i)=>{Array.isArray(i)&&(o.length=0,o.push(...i))},s=(o,i)=>{if(i&&typeof i=="object"){for(let c of Object.keys(o))delete o[c];Object.assign(o,i)}};r($,n.workspaces),r(Se,n.teams),r(xe,n.agents),r(b,n.connections);for(let o of b)o.provider!=="local"&&(o.authStatus="expired");r(h,n.tickets),r(Y,n.searchable),r(j,n.notes),r(D,n.openQuestions),r(H,n.messagePresets),r(se,n.scheduleOrder),s(O,n.schedules),s(z,n.scheduleRuns),n.settings&&typeof n.settings=="object"&&Object.assign(l,n.settings),n.state&&typeof n.state=="object"&&Object.assign(G,n.state);for(let o of Object.keys(x))delete x[o];for(let o of Object.keys(T))delete T[o];for(let o of Object.keys(S))delete S[o];for(let o of Object.keys(W))delete W[o];return X.length=0,ce.length=0,await _s(),t.json({ok:!0})});m.post("/api/database/reset",async(e,t)=>(await As(),Vn(),t.json({ok:!0})));m.get("/api/fs/browse",(e,t)=>{let n=String(e.query.path??"").trim(),r=e.query.files==="1",s=n||zn.homedir();s==="~"?s=zn.homedir():s.startsWith("~/")&&(s=re.join(zn.homedir(),s.slice(2))),s=re.resolve(s);try{let o=Ce.readdirSync(s,{withFileTypes:!0}).filter(c=>!c.name.startsWith(".")).filter(c=>c.isDirectory()||r&&c.isFile()).map(c=>({name:c.name,path:re.join(s,c.name),type:c.isDirectory()?"dir":"file"})).sort((c,d)=>c.type!==d.type?c.type==="dir"?-1:1:c.name.localeCompare(d.name)),i=re.dirname(s);return t.json({path:s,parent:i===s?null:i,home:zn.homedir(),entries:o})}catch(o){return t.status(400).json({error:`can't read ${s}: ${String(o)}`})}});m.get("/api/connections",(e,t)=>t.json(b));function es(e){let t=e??{},n=Array.isArray(t.labels)?t.labels.map(i=>String(i).trim()).filter(Boolean).slice(0,3):[],r=!!t.assignedToMe,s=r?"":String(t.assignee??"").trim(),o={};return n.length&&(o.labels=n),r?o.assignedToMe=!0:s&&(o.assignee=s),Object.keys(o).length?o:null}function Fd(e){let t=e??{},n=t.kind==="team"||t.kind==="agent"?t.kind:"anyone",r=n==="team"?String(t.teamId??"").trim():"",s=n==="agent"?String(t.agentId??"").trim():"";return n==="team"&&r?{kind:n,teamId:r}:n==="agent"&&s?{kind:n,agentId:s}:{kind:"anyone"}}var Qi={linear:"Linear",jira:"Jira",asana:"Asana",clickup:"ClickUp",monday:"Monday"},Wd=new Set(Object.keys(Qi));function Ji(e,t,n){return e==="jira"?li(t,n):e==="asana"?gi(t,n):e==="clickup"?bi(t,n):e==="monday"?Ri(t,n):ci(t,n)}m.post("/api/connections",async(e,t)=>{let n=String(e.body?.provider??"");if(!Wd.has(n))return t.status(400).json({error:"unsupported provider"});let r=Qi[n],s=String(e.body?.name??"").trim(),o=String(e.body?.workingDir??"").trim(),i=$.find(p=>p.id===e.body?.workspaceId);if(e.body?.workspaceId&&!i)return t.status(400).json({error:"workspace not accessible"});if(i&&b.filter(g=>g.provider!=="local"&&g.linkStatus==="linked"&&g.workspaceId===i.id).length>=Wt)return t.json({configured:!0,message:`That workspace already has ${Wt} connections (the max).`});let d={id:`conn_${ot().slice(0,8)}`,provider:n,providerAccountName:E()?`${r} (demo)`:r,...s?{displayName:s}:{},workspaceId:i?.id??null,workspaceName:i?.name??null,workingDir:o||null,linkStatus:i?"linked":"unlinked",authStatus:E()?"connected":"needs_refresh",lastSyncedAt:null,ticketCount:0,syncFilters:es(e.body?.syncFilters),autoIngestTickets:n!=="local"?!!e.body?.autoIngestTickets:void 0};if(E())return b.push(d),await N(d),t.status(201).json({connection:d,mock:!0});let u=await Ji(n,d.id,Zr);return u.url?(b.push(d),await N(d),t.status(201).json({connection:d,url:u.url})):t.json({configured:u.configured===!1?!1:void 0,message:u.error??`Couldn't start the ${r} connection.`})});m.post("/api/connections/:id/link",async(e,t)=>{let n=b.find(o=>o.id===e.params.id);if(!n)return t.status(404).json({error:"connection not found"});let r=$.find(o=>o.id===e.body?.workspaceId);return r?b.filter(o=>o.id!==n.id&&o.provider!=="local"&&o.linkStatus==="linked"&&o.workspaceId===r.id).length>=Wt?t.status(409).json({error:`A workspace can have at most ${Wt} connections.`}):(n.workspaceId=r.id,n.workspaceName=r.name,typeof e.body?.workingDir=="string"&&e.body.workingDir.trim()&&(n.workingDir=e.body.workingDir.trim()),typeof e.body?.name=="string"&&e.body.name.trim()&&(n.displayName=e.body.name.trim()),"syncFilters"in(e.body??{})&&n.provider!=="local"&&(n.syncFilters=es(e.body.syncFilters)),"autoIngestTickets"in(e.body??{})&&n.provider!=="local"&&(n.autoIngestTickets=!!e.body.autoIngestTickets),n.linkStatus="linked",delete n.linkLostReason,n.lastSyncedAt=new Date().toISOString(),await N(n),t.json(n)):t.status(400).json({error:"workspace not accessible"})});m.patch("/api/connections/:id",async(e,t)=>{let n=b.find(r=>r.id===e.params.id);if(!n)return t.status(404).json({error:"connection not found"});if(typeof e.body?.name=="string"){let r=e.body.name.trim();r?n.displayName=r:delete n.displayName}if(e.body?.unlink===!0){if(n.provider==="local")return t.status(400).json({error:"a local board can't be unlinked"});n.workspaceId=null,n.workspaceName=null,n.workingDir=null,n.linkStatus="unlinked"}if(typeof e.body?.workspaceId=="string"&&n.provider!=="local"){let r=$.find(s=>s.id===e.body.workspaceId);if(!r)return t.status(400).json({error:"workspace not accessible"});if(r.id!==n.workspaceId&&b.filter(o=>o.id!==n.id&&o.provider!=="local"&&o.linkStatus==="linked"&&o.workspaceId===r.id).length>=Wt)return t.status(409).json({error:`A workspace can have at most ${Wt} connections.`});n.workspaceId=r.id,n.workspaceName=r.name,n.linkStatus="linked"}return typeof e.body?.workingDir=="string"&&e.body.workingDir.trim()&&(n.workingDir=e.body.workingDir.trim()),"syncFilters"in(e.body??{})&&n.provider!=="local"&&(n.syncFilters=es(e.body.syncFilters)),"autoIngestTickets"in(e.body??{})&&n.provider!=="local"&&(n.autoIngestTickets=!!e.body.autoIngestTickets),await N(n),t.json(n)});m.delete("/api/connections/:id",async(e,t)=>{let n=b.find(s=>s.id===e.params.id);if(!n)return t.status(404).json({error:"connection not found"});if(n.provider==="local")return t.status(400).json({error:"a local board can't be disconnected"});for(let s of h.filter(o=>o.connectionId===n.id)){let o=T[s.id];o&&(await St(o),delete x[o],delete T[s.id]),delete S[s.id],await tn(s.id),await mn(s.id)}for(let s=h.length-1;s>=0;s--)h[s].connectionId===n.id&&h.splice(s,1);for(let s=Y.length-1;s>=0;s--)Y[s].connectionId===n.id&&Y.splice(s,1);let r=b.findIndex(s=>s.id===n.id);return r!==-1&&b.splice(r,1),await gs(n.id),await Ei(n.id),t.status(204).end()});m.post("/api/connections/:id/oauth/start",async(e,t)=>{let n=b.find(s=>s.id===e.params.id);if(!n)return t.status(404).json({error:"connection not found"});if(!an(n.provider))return t.status(400).json({error:"not a vendor connection"});let r=await Ji(n.provider,n.id,Zr);return r.url?t.json({url:r.url}):t.status(r.configured===!1?501:502).json({error:r.error??"Couldn't start OAuth."})});m.get("/api/oauth/linear/deposit",async(e,t)=>{let n=String(e.query.code??""),r=String(e.query.connectionId??""),s=b.find(i=>i.id===r);if(!n||!s)return t.redirect("/settings/connections?linear=error");let o=await di(n);return o?(await nt({connectionId:s.id,provider:"linear",accessToken:o.accessToken,refreshToken:o.refreshToken,scope:o.scope,expiresAt:o.expiresAt}),s.authStatus="connected",await N(s),t.redirect("/settings/connections?linear=connected")):(s.authStatus="error",await N(s),t.redirect("/settings/connections?linear=error"))});m.get("/api/oauth/jira/deposit",async(e,t)=>{let n=String(e.query.code??""),r=String(e.query.connectionId??""),s=b.find(u=>u.id===r);if(!n||!s)return t.redirect("/settings/connections?jira=error");let o=await pi(n);if(!o)return s.authStatus="error",await N(s),t.redirect("/settings/connections?jira=error");let c=(await mi(o.accessToken))[0];if(!c?.id)return s.authStatus="error",await N(s),t.redirect("/settings/connections?jira=error");await nt({connectionId:s.id,provider:"jira",accessToken:o.accessToken,refreshToken:o.refreshToken,scope:o.scope,expiresAt:o.expiresAt}),s.cloudId=c.id;let d=c.url?c.url.replace(/^https?:\/\//,"").replace(/\/+$/,""):void 0;return s.providerAccountName=d||c.name||"Jira",s.authStatus="connected",await N(s),t.redirect("/settings/connections?jira=connected")});m.get("/api/oauth/asana/deposit",async(e,t)=>{let n=String(e.query.code??""),r=String(e.query.connectionId??""),s=b.find(d=>d.id===r);if(!n||!s)return t.redirect("/settings/connections?asana=error");let o=await hi(n);if(!o)return s.authStatus="error",await N(s),t.redirect("/settings/connections?asana=error");let c=(await Vo(o.accessToken))[0];return c?.gid?(await nt({connectionId:s.id,provider:"asana",accessToken:o.accessToken,refreshToken:o.refreshToken,scope:o.scope,expiresAt:o.expiresAt}),s.providerAccountId=c.gid,s.providerAccountName=c.name||"Asana",s.authStatus="connected",await N(s),t.redirect("/settings/connections?asana=connected")):(s.authStatus="error",await N(s),t.redirect("/settings/connections?asana=error"))});m.get("/api/oauth/clickup/deposit",async(e,t)=>{let n=String(e.query.code??""),r=String(e.query.connectionId??""),s=b.find(d=>d.id===r);if(!n||!s)return t.redirect("/settings/connections?clickup=error");let o=await Ii(n);if(!o)return s.authStatus="error",await N(s),t.redirect("/settings/connections?clickup=error");let c=(await ni(o.accessToken))[0];return c?.id?(await nt({connectionId:s.id,provider:"clickup",accessToken:o.accessToken}),s.providerAccountId=c.id,s.providerAccountName=c.name||"ClickUp",s.authStatus="connected",await N(s),t.redirect("/settings/connections?clickup=connected")):(s.authStatus="error",await N(s),t.redirect("/settings/connections?clickup=error"))});m.get("/api/oauth/monday/deposit",async(e,t)=>{let n=String(e.query.code??""),r=String(e.query.connectionId??""),s=b.find(c=>c.id===r);if(!n||!s)return t.redirect("/settings/connections?monday=error");let o=await Ti(n);if(!o)return s.authStatus="error",await N(s),t.redirect("/settings/connections?monday=error");let i=await ai(o.accessToken);return await nt({connectionId:s.id,provider:"monday",accessToken:o.accessToken}),i?.slug&&(s.providerAccountId=i.slug),s.providerAccountName=i?.name||"Monday",s.authStatus="connected",await N(s),t.redirect("/settings/connections?monday=connected")});var Kd=6e4,Wi={getToken:Qn,saveToken:nt,refresh:(e,t)=>{let n=an(e);return n?n.refresh(t):Promise.resolve(null)}};async function Hd(e){let t=l.importDelegate;if(!t||t.kind==="anyone")return{assignmentKind:"anyone"};let n=t.kind==="team"?{assignmentKind:"team",teamId:t.teamId??void 0}:{assignmentKind:"agent",agentId:t.agentId??void 0},r=E()?{teams:Se,agents:xe}:await yt(e);return!r||(t.kind==="team"?r.teams.some(o=>o.id===t.teamId):r.agents.some(o=>o.id===t.agentId))?n:(l.importDelegate={kind:"anyone"},await te(),{assignmentKind:"anyone"})}async function zi(e){if(e.workspaceId&&(e.workingDir=Yn(e.workspaceId)),e.model=Re(l.defaultModel),e.effort=Te(l.defaultEffort),h.push(e),S[e.id]="backlog",await A(e.id),!l.autoAssignImports)return;let t=b.find(n=>n.id===e.connectionId);if(!(!t||t.linkStatus!=="linked"||!t.workspaceId))try{let n=await Hd(t.workspaceId);await En({ticketId:e.id,assignmentKind:n.assignmentKind,teamId:n.teamId,agentId:n.agentId,autonomy:l.defaultAutonomy})}catch{}}async function Qd(e){let t=an(e.provider);if(!t)throw Object.assign(new Error("not a vendor connection"),{terminal:!0});let n=await Ci(e.id,Wi);if(!n)throw Object.assign(new Error("no usable token"),{terminal:!0});let r=h.filter(p=>p.connectionId===e.id&&p.id.startsWith(t.idPrefix)).map(p=>p.id.slice(t.idPrefix.length)),s=p=>Promise.all([t.fetchDiscovery(e,p,e.syncFilters),t.fetchByIds(e,p,r)]),o,i;try{[o,i]=await s(n)}catch(p){let g=p?.status;if(g!==401&&g!==403)throw p;let I=await Qn(e.id),f=I?await Qr(e.id,I,Wi):null;if(!f){let w=p instanceof Error?p:new Error(String(p));throw Object.assign(w,{terminal:!0})}[o,i]=await s(f)}let{refreshed:c,pool:d}=jo(o,i,e.id,h,Y);for(let p of c)await A(p);let u=d.length;if(e.autoIngestTickets&&d.length){for(let p of d){let g=Y.findIndex(I=>I.id===p.id);g!==-1&&Y.splice(g,1),await zi(p)}u=0}return e.lastSyncedAt=new Date().toISOString(),e.ticketCount=h.filter(p=>p.connectionId===e.id&&!p.dismissedAt).length,e.authStatus="connected",await N(e),{refreshed:c.length,available:u}}async function ts(e){if(E())return e.lastSyncedAt=new Date().toISOString(),await N(e),{synced:!0};if(!an(e.provider))return{synced:!1,skipped:"not_syncable"};if(!await Qn(e.id))return e.authStatus==="connected"&&(e.authStatus="expired",await N(e)),{synced:!1,skipped:"not_syncable"};let t=Y.filter(r=>r.connectionId===e.id).length;if((e.lastSyncedAt?Date.now()-Date.parse(e.lastSyncedAt):1/0)<Kd&&t>0)return{synced:!1,skipped:"fresh"};try{return{synced:!0,...await Qd(e)}}catch(r){return e.authStatus=r?.terminal?"expired":"needs_refresh",await N(e),{synced:!1,error:String(r)}}}function Jd(){return async(e,t,n)=>{let r=h.find(o=>o.id===e.params.id),s=r?b.find(o=>o.id===r.connectionId):void 0;s&&await ts(s),n()}}m.post("/api/connections/:id/sync",async(e,t)=>{let n=b.find(s=>s.id===e.params.id);if(!n)return t.status(404).json({error:"connection not found"});let r=await ts(n);return r.error?t.status(502).json({error:`sync failed: ${r.error}`}):t.json({synced:r.synced,skipped:r.skipped,refreshed:r.refreshed??0,available:r.available})});m.get("/api/board",(e,t)=>{let n=String(e.query.connectionId??""),r=new Set(b.filter(o=>o.linkStatus==="linked").map(o=>o.id)),s=h.filter(o=>!o.dismissedAt&&r.has(o.connectionId)&&(!n||o.connectionId===n)).map(o=>{let i=T[o.id],c=i?x[i]:void 0;return{ticket:o,column:rt(o.id),blockedByDeps:xt(o,d=>h.find(u=>u.id===d),rt).length>0,run:c?{id:c.id,ticketId:c.ticketId,status:c.status,stopReason:c.stopReason,branchName:c.branchName,assignmentKind:c.assignmentKind,teamName:c.teamName,agentName:c.agentName,autonomy:c.autonomy,prUrl:c.prUrl,blocker:c.blocker}:void 0}});t.json(s)});m.post("/api/tickets/:id/move",async(e,t)=>{try{return t.json(await Et(e.params.id,e.body?.column))}catch(n){return ge(t,n)}});m.post("/api/tickets/:id/reorder",async(e,t)=>{let n=h.findIndex(i=>i.id===e.params.id);if(n===-1)return t.status(404).json({error:"ticket not found"});let[r]=h.splice(n,1),s=e.body?.beforeId,o=s?h.findIndex(i=>i.id===s):-1;return o===-1?h.push(r):h.splice(o,0,r),await hs(),t.json({ticketId:r.id})});m.get("/api/tickets/:id",Jd(),async(e,t)=>{let n=h.find(u=>u.id===e.params.id);if(!n)return t.status(404).json({error:"ticket not found"});let r=T[n.id],s=r?x[r]:null;if(s&&s.sessionId&&s.status!=="running"){let u=X.some(p=>p.ticketId===n.id);if(!s.resultText||!u){let p=cr(s.sessionId);if(p.response&&!s.resultText&&(s.resultText=p.response.slice(0,5e4),await U(s.id)),p.response&&!u){let g={id:`rr_${ot()}`,ticketId:n.id,runId:s.id,sessionId:s.sessionId,prompt:(p.prompt??"").slice(0,5e4),response:p.response.slice(0,5e4),status:"completed",createdAt:s.updatedAt??new Date().toISOString()};X.unshift(g),await hn(g)}}}let o=X.find(u=>u.ticketId===n.id)??null,i=b.find(u=>u.id===n.connectionId),{dependencies:c,blockedByDeps:d}=Pn(n);return t.json({ticket:n,column:rt(n.id),run:s,latestResult:o,connectionName:i?.displayName??i?.providerAccountName??"\u2014",workspaceName:i?.workspaceName??null,dependencies:c,blockedByDeps:d})});m.get("/api/tickets/:id/history",async(e,t)=>{let n=Math.min(Math.max(Number(e.query.limit)||10,1),100),r=Math.max(Number(e.query.offset)||0,0);t.json(await Is(e.params.id,n,r))});m.get("/api/tickets/:id/notes",(e,t)=>{t.json(At(e.params.id))});m.post("/api/tickets/:id/notes",async(e,t)=>{try{let n=await Ot(e.params.id,{body:String(e.body?.body??""),author:e.body?.author});return t.status(201).json(n)}catch(n){return ge(t,n)}});m.get("/api/schedules/:id/notes",(e,t)=>{let n=j.filter(r=>r.scheduleId===e.params.id).slice().reverse();t.json(n)});m.post("/api/schedules/:id/notes",async(e,t)=>{let n=O[e.params.id];if(!n)return t.status(404).json({error:"schedule not found"});let r=String(e.body?.body??"").trim();if(!r)return t.status(400).json({error:"empty note"});let s=e.body?.author==="assistant"?"assistant":"user",o=new Date().toISOString(),i={id:`note_${ot()}`,scheduleId:n.id,author:s,body:r.slice(0,5e3),createdAt:o,updatedAt:o};return j.push(i),await ht(i),t.status(201).json(i)});m.patch("/api/notes/:id",async(e,t)=>{try{return t.json(await Dt(e.params.id,String(e.body?.body??"")))}catch(n){return ge(t,n)}});m.delete("/api/notes/:id",async(e,t)=>{try{return await jt(e.params.id),t.json({ok:!0})}catch(n){return ge(t,n)}});m.get("/api/tickets/:id/open-questions",(e,t)=>{t.json(Nt(e.params.id))});m.post("/api/tickets/:id/open-questions",async(e,t)=>{try{let n=await Mt(e.params.id,String(e.body?.question??""));return t.status(201).json(n)}catch(n){return ge(t,n)}});m.patch("/api/open-questions/:qid",async(e,t)=>{try{return t.json(await Bt(e.params.qid,e.body??{}))}catch(n){return ge(t,n)}});m.delete("/api/open-questions/:qid",async(e,t)=>{try{return await $t(e.params.qid),t.json({ok:!0})}catch(n){return ge(t,n)}});var st=re.join(re.dirname(ln),"pipelines-uploads"),zd={png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",webp:"image/webp",pdf:"application/pdf",json:"application/json",txt:"text/plain",md:"text/markdown",csv:"text/csv"},Xd=/^paste-[a-f0-9]+(\.[a-z0-9]+)?$/i,Yd=e=>(e.split(".").pop()??"").toLowerCase().replace(/[^a-z0-9]/g,"").slice(0,8);m.get("/api/uploads/stats",async(e,t)=>{let n=0,r=0,s=await Ce.promises.readdir(st).catch(()=>[]);for(let o of s){let i=await Ce.promises.stat(re.join(st,o)).catch(()=>null);i?.isFile()&&(n++,r+=i.size)}return t.json({count:n,bytes:r})});m.post("/api/uploads/clear",async(e,t)=>{let n=0,r=0,s=await Ce.promises.readdir(st).catch(()=>[]);for(let o of s){let i=re.join(st,o),c=await Ce.promises.stat(i).catch(()=>null);c?.isFile()&&(r+=c.size,await Ce.promises.unlink(i).catch(()=>{}),n++)}return t.json({removed:n,bytes:r})});m.post("/api/uploads",Xn.raw({type:"application/octet-stream",limit:"25mb"}),async(e,t)=>{let n=e.body;if(!Buffer.isBuffer(n)||n.length===0)return t.status(400).json({error:"empty upload"});let r=Yd(String(e.query.name??""));await Ce.promises.mkdir(st,{recursive:!0});let s=`paste-${ot().slice(0,8)}${r?`.${r}`:""}`,o=re.join(st,s);return await Ce.promises.writeFile(o,n),t.status(201).json({path:o,filename:s})});m.get("/api/uploads/:name",(e,t)=>{let n=e.params.name;if(!Xd.test(n))return t.status(400).end();let r=n.split(".").pop().toLowerCase();return t.type(zd[r]??"application/octet-stream"),t.sendFile(n,{root:st},s=>{s&&!t.headersSent&&t.status(404).end()})});var Xi=e=>e.split(/[/\\]/).pop()??e,Ki=10;m.get("/api/message-presets",(e,t)=>{t.json(H.slice().sort((n,r)=>n.position-r.position))});m.post("/api/message-presets",async(e,t)=>{if(H.length>=Ki)return t.status(422).json({error:`at most ${Ki} presets`});let n=String(e.body?.title??"").trim(),r=String(e.body?.prompt??"").trim();if(!n||!r)return t.status(400).json({error:"title and prompt are required"});let s=new Date().toISOString(),o={id:`preset_${ot()}`,title:n.slice(0,50),prompt:r.slice(0,5e3),enabled:e.body?.enabled!==!1,position:H.length,createdAt:s,updatedAt:s};return H.push(o),await yn(o.id),t.status(201).json(o)});m.patch("/api/message-presets/:id",async(e,t)=>{let n=H.find(r=>r.id===e.params.id);if(!n)return t.status(404).json({error:"preset not found"});if(typeof e.body?.title=="string"){let r=e.body.title.trim();if(!r)return t.status(400).json({error:"empty title"});n.title=r.slice(0,50)}if(typeof e.body?.prompt=="string"){let r=e.body.prompt.trim();if(!r)return t.status(400).json({error:"empty prompt"});n.prompt=r.slice(0,5e3)}return typeof e.body?.enabled=="boolean"&&(n.enabled=e.body.enabled),n.updatedAt=new Date().toISOString(),await yn(n.id),t.json(n)});m.delete("/api/message-presets/:id",async(e,t)=>{let n=H.findIndex(r=>r.id===e.params.id);return n===-1?t.status(404).json({error:"preset not found"}):(H.splice(n,1),H.forEach((r,s)=>r.position=s),await xs(e.params.id),await vs(),t.json({ok:!0}))});m.post("/api/tickets/:id/refresh",async(e,t)=>{let n=h.find(r=>r.id===e.params.id);return n?(await Ud(400),t.json({ticket:n,refreshedAt:new Date().toISOString()})):t.status(404).json({error:"ticket not found"})});m.get("/api/search",(e,t)=>{let n=String(e.query.q??"").trim().toLowerCase(),r=String(e.query.connectionId??""),s=Math.min(Math.max(Number(e.query.limit)||20,1),100),o=Math.max(Number(e.query.offset)||0,0),i=new Set(b.filter(p=>p.linkStatus==="linked").map(p=>p.id)),c=new Set(h.map(p=>p.externalIdentifier)),d=Y.filter(p=>r?p.connectionId===r:i.has(p.connectionId)).filter(p=>i.has(p.connectionId)).filter(p=>!n||p.title.toLowerCase().includes(n)||p.externalIdentifier.toLowerCase().includes(n)),u=d.slice(o,o+s).map(p=>({externalId:p.id,externalIdentifier:p.externalIdentifier,title:p.title,status:p.status,statusRaw:p.statusRaw,provider:p.provider,connectionId:p.connectionId,url:p.url,imported:c.has(p.externalIdentifier)}));t.json({hits:u,total:d.length})});m.post("/api/import",async(e,t)=>{let n=Y.findIndex(s=>s.id===e.body?.externalId);if(n===-1)return t.status(404).json({error:"ticket not in search results"});let[r]=Y.splice(n,1);return await zi(r),t.status(201).json(r)});m.patch("/api/tickets/:id",async(e,t)=>{try{return t.json(await Ae(e.params.id,e.body??{}))}catch(n){return ge(t,n)}});m.post("/api/board/:connectionId/tickets",async(e,t)=>{try{let n=await Pt(e.params.connectionId,e.body??{});return t.status(201).json(n)}catch(n){return ge(t,n)}});m.post("/api/tickets/:id/duplicate",async(e,t)=>{let n=h.find(c=>c.id===e.params.id);if(!n)return t.status(404).json({error:"ticket not found"});if(n.provider!=="local")return t.status(400).json({error:"only local tickets can be duplicated"});let r=b.find(c=>c.id===n.connectionId),s=(String(e.body?.title??"").trim()||n.title).slice(0,200),o=++G.localSeq,i={id:`t_local${o}`,connectionId:n.connectionId,provider:"local",externalIdentifier:`LOCAL-${o}`,project:"Local",title:s,status:"todo",statusRaw:"Backlog",priority:0,labels:[],url:"",workspaceId:n.workspaceId,workingDir:n.workspaceId?Yn(n.workspaceId):"~",model:Re(l.defaultModel),effort:Te(l.defaultEffort),description:n.description,additionalContext:n.additionalContext,additionalContextUpdatedAt:n.additionalContext?new Date().toISOString():void 0,createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()};h.push(i),S[i.id]="backlog",r&&(r.ticketCount+=1),await A(i.id),r&&await N(r);for(let c of j.filter(d=>d.ticketId===n.id)){let d=new Date().toISOString(),u={id:`note_${ot()}`,ticketId:i.id,author:c.author,body:c.body,createdAt:d,updatedAt:d};j.push(u),await ht(u)}return t.status(201).json(i)});m.put("/api/tickets/:id/dependencies",async(e,t)=>{try{let n=await On(e.params.id,e.body?.dependsOn);return await Mn(e.params.id),t.json(n)}catch(n){return ge(t,n)}});m.get("/api/runs/:id",(e,t)=>{let n=x[e.params.id];return n?t.json(n):t.status(404).json({error:"run not found"})});m.get("/api/runs/:id/transcript",(e,t)=>{let n=x[e.params.id];if(!n)return t.status(404).json({error:"run not found"});let r=n.sessionId?dr(n.sessionId):[];return t.json({events:r,status:n.status,running:n.status==="running"})});m.get("/api/sessions/:sessionId/transcript",(e,t)=>{let n=dr(e.params.sessionId);return t.json({events:n,status:"ended",running:!1})});m.post("/api/runs",async(e,t)=>{try{let n=await En(e.body);return t.status(201).json(n)}catch(n){return un(t,n)}});m.post("/api/runs/:id/start",async(e,t)=>{try{let n=await qe(e.params.id,{details:String(e.body?.additionalDetails??""),attachments:ns(e.body)});return t.json(n)}catch(n){return un(t,n)}});m.post("/api/runs/:id/pause",async(e,t)=>{try{return t.json(await Rt(e.params.id))}catch(n){return un(t,n)}});m.post("/api/runs/:id/resume",async(e,t)=>{try{return t.json(await Tt(e.params.id))}catch(n){return un(t,n)}});m.post("/api/tickets/:id/unblock",async(e,t)=>{try{let n=await Ct(e.params.id,{resume:e.body?.resume===!0});return t.json(n)}catch(n){return ge(t,n)}});m.get("/api/tickets/:id/repos",async(e,t)=>{let n=h.find(o=>o.id===e.params.id);if(!n)return t.status(404).json({error:"ticket not found"});let r=n.workingDir?.trim();if(!r)return t.json({repos:[],note:"ticket has no working dir set"});let s=await Ge(r);return t.json({repos:s.map(o=>o.name),singleRepo:s.length===1&&s[0].name==="."})});m.post("/api/tickets/:id/worktree",async(e,t)=>{let n=h.find(at=>at.id===e.params.id);if(!n)return t.status(404).json({error:"ticket not found"});let r=T[n.id],s=r?x[r]:void 0;if(!s)return t.status(409).json({error:"ticket has no run"});let o=n.workingDir?.trim();if(!o)return t.status(409).json({error:"ticket has no working dir set"});let i=await Ls(o,e.body?.repo);if(!i){let at=await Ge(o);return t.status(400).json({error:at.length===0?"no git repositories found in the working dir":`name the repo to work on; found: ${at.map(sa=>sa.name).join(", ")}`})}let c=s.worktrees??=[],d=c.find(at=>at.repo===i.name);if(d)return t.json(d);let u=(l.branchPrefix||"feature").trim().replace(/\/+$/,""),p=i.name==="."?"":`-${i.name}`,g=n.attempt??1,I=g>1?`-v${g}`:"",f=`${u}/${n.externalIdentifier}${p}${I}`,w=await Gs(i.path),V=await fr(i.path,w),He=await uo(n.id,i.name,i.path,V);if(He.ambiguous)return t.status(409).json({code:"ambiguous_base",error:`${n.externalIdentifier} depends on multiple tickets that both changed ${i.name} and aren't merged yet (${He.ambiguous.join(", ")}) \u2014 chain them (make one depend on the other) so there's a single base to build on. This is a hard block, not a transient error: do NOT retry start_repo_work for this repo. Block this ticket and stop; a human must chain the dependencies.`});He.startPoint&&(w=He.startPoint,V=He.startPoint);let ra=(i.name==="."?"repo":i.name)+I,rs=re.join(pr,n.externalIdentifier,ra),ss=await qs({repoPath:i.path,worktreePath:rs,branch:f,base:w,startPoint:V});if(!ss.ok)return t.status(500).json({error:ss.error});let os={repo:i.name,branch:f,worktreePath:rs,base:w,compareUrl:await Ks(i.path,w,f),createdAt:new Date().toISOString()};return c.push(os),s.branchName=s.branchName||f,s.events.push({id:`e${s.events.length+1}`,ts:be(),kind:"mcp_tool",text:`claimed worktree for ${i.name} \u2192 ${f}`}),await U(s.id),t.json(os)});m.post("/api/tickets/:id/pr-links",async(e,t)=>{let n=h.find(i=>i.id===e.params.id);if(!n)return t.status(404).json({error:"ticket not found"});let r=String(e.body?.url??"").trim();if(!/^https?:\/\//.test(r))return t.status(400).json({error:"a valid http(s) PR url is required"});let s=n.prLinks??=[];s.some(i=>i.url===r)||s.push({url:r,title:String(e.body?.title??"").trim()||void 0,createdAt:new Date().toISOString()}),await A(n.id);let o=x[T[n.id]];return o&&(o.prUrl=r,await U(o.id)),!E()&&s.length>1&&Gi(n.externalIdentifier,s.map(i=>i.url)).catch(()=>{}),t.status(201).json({prLinks:s})});m.get("/api/tickets/:id/pr-links",(e,t)=>{let n=h.find(r=>r.id===e.params.id);return t.json({prLinks:n?.prLinks??[]})});m.post("/api/tickets/:id/monitor",async(e,t)=>{let n=h.find(s=>s.id===e.params.id);if(!n)return t.status(404).json({error:"ticket not found"});if(e.body?.on===!0){if(!n.prLinks?.length)return t.status(409).json({error:"no PR to monitor yet"});if(!T[n.id])return t.status(409).json({error:"no run/session to continue from"});n.prMonitor=!0,n.prMonitorNote=void 0,n.prMonitorLastAt=void 0,(n.prMonitorCount??0)>=cn(l)&&(n.prMonitorCount=0)}else n.prMonitor=!1;return await A(n.id),t.json({prMonitor:n.prMonitor})});var zr=null,Xr=!1;async function Vd(e,t,n){let r=new Date().toISOString(),s=o=>t.events.push({id:`e${t.events.length+1}`,ts:be(),kind:"state_change",text:o});if(n.kind==="stamp"){e.prMonitorLastAt=r,await A(e.id);return}if(n.kind==="engage"){e.prMonitorCount=(e.prMonitorCount??0)+1,e.prMonitorLastAt=r,await A(e.id),t.status="running",t.stopReason=void 0,t.updatedAt=r,s(`PR monitor: ${n.reason==="comments"?"open review comments":"CI failing"} \u2192 engaging agent (pass ${e.prMonitorCount})`),await U(t.id),_e(t.id,!0,n.prompt);return}e.prMonitor=!1,e.prMonitorNote=n.note,e.prMonitorLastAt=r,n.merged&&(e.prMerged=!0),s(`PR monitor: ${n.note}`),await U(t.id),await A(e.id)}async function Zd(){if(!(E()||Xr)){Xr=!0;try{let e=Date.now();for(let t of h){if(!t.prMonitor)continue;let n=rt(t.id);if(n!=="in_review"&&n!=="done")continue;let r=T[t.id]?x[T[t.id]]:void 0;if(!r||r.status==="running"||!Ui(t,l,e))continue;let s=(t.prLinks??[]).map(c=>c.url).filter(Boolean);if(!s.length){t.prMonitorLastAt=new Date().toISOString(),await A(t.id);continue}let o=await Mi(s),i=o.ci==="passing"&&l.prMonitorReplyToComments?await Bi(s):void 0;await Vd(t,r,qi(t,l,o,i))}}finally{Xr=!1}}}function eu(){zr||(zr=setInterval(()=>void Zd(),6e4),zr.unref?.())}m.post("/api/runs/:id/stop",async(e,t)=>{try{return t.json(await Fe(e.params.id))}catch(n){return un(t,n)}});m.post("/api/runs/:id/complete",async(e,t)=>{let n=x[e.params.id];return n?(n.status="stopped",n.stopReason="completed",n.blocker=void 0,n.events.push({id:`e${n.events.length+1}`,ts:be(),kind:"mcp_tool",text:"mark_complete(...)"}),n.updatedAt=new Date().toISOString(),S[n.ticketId]="in_review",await U(n.id),await A(n.ticketId),t.json(n)):t.status(404).json({error:"run not found"})});m.post("/api/runs/:id/answer",async(e,t)=>{let n=x[e.params.id];if(!n||!n.blocker)return t.status(409).json({error:"no open blocker"});n.blocker.status="answered";let r=e.body?.decision?`human: ${e.body.decision}`:`human answered: ${e.body?.response??""}`;n.events.push({id:`e${n.events.length+1}`,ts:be(),kind:"mcp_tool",text:r}),n.blocker=void 0,n.status="running",n.updatedAt=new Date().toISOString(),S[n.ticketId]="in_progress",await U(n.id),await A(n.ticketId);let s=String(e.body?.decision??e.body?.response??"").trim();return E()||_e(n.id,!0,`The human responded: ${s}`),t.json(n)});m.post("/api/runs/:id/approve",async(e,t)=>{let n=x[e.params.id];return n?n.status!=="stopped"?t.status(409).json({error:"can only approve a stopped run"}):(n.events.push({id:`e${n.events.length+1}`,ts:be(),kind:"state_change",text:"approved by human \u2192 Done"}),n.updatedAt=new Date().toISOString(),S[n.ticketId]="done",await U(n.id),await A(n.ticketId),t.json(n)):t.status(404).json({error:"run not found"})});m.post("/api/runs/:id/respond",async(e,t)=>{let n=x[e.params.id];if(!n)return t.status(404).json({error:"run not found"});if(h.find(c=>c.id===n.ticketId)?.prMonitor)return t.status(409).json({error:"PR monitor is armed \u2014 turn it off to send a message"});if(n.status!=="stopped"&&n.status!=="blocked")return t.status(409).json({error:"run is not waiting for a response"});let r=String(e.body?.text??"").trim(),s=Array.isArray(e.body?.attachments)?e.body.attachments.map(c=>String(c)).filter(Boolean):[];if(!r&&s.length===0)return t.status(400).json({error:"empty response"});let o=n.status==="blocked";n.blocker&&(n.blocker=void 0),n.status="running",n.stopReason=void 0,n.events.push({id:`e${n.events.length+1}`,ts:be(),kind:o?"mcp_tool":"state_change",text:`human responded: ${r.slice(0,80)}${s.length?` (+${s.length} file${s.length>1?"s":""})`:""}`}),n.updatedAt=new Date().toISOString(),S[n.ticketId]="in_progress",await U(n.id),await A(n.ticketId);let i=r+(s.length?`
|
|
123
123
|
|
|
124
124
|
Attached files (read these):
|
|
125
125
|
${s.map(c=>`- ${c}`).join(`
|
|
126
|
-
`)}`:"");return
|
|
126
|
+
`)}`:"");return E()||_e(n.id,!0,i),t.json(n)});m.post("/api/tickets/:id/start-over",async(e,t)=>{let n=h.find(d=>d.id===e.params.id);if(!n)return t.status(404).json({error:"ticket not found"});let r=T[n.id],s=(r?x[r]?.worktrees:void 0)??[];r&&(await St(r),delete x[r],delete T[n.id],await ks(r)),await _n(n.externalIdentifier);let o=n.workingDir?.trim();if(o)for(let d of s){let u=d.repo==="."?o:re.join(o,d.repo);await Ws(u,d.branch)}let i=e.body?.keep??{},c=d=>i[d]===!1;n.prLinks=void 0,n.attempt=(n.attempt??1)+1;for(let d=X.length-1;d>=0;d--)X[d].ticketId===n.id&&X.splice(d,1);if(await bs(n.id),c("plan")&&(n.plan=void 0,n.planUpdatedAt=void 0),c("conclusion")&&(n.conclusion=void 0,n.conclusionUpdatedAt=void 0),c("context")&&(n.additionalContext=void 0,n.additionalContextUpdatedAt=void 0),c("questions")){for(let d=D.length-1;d>=0;d--)if(D[d].ticketId===n.id){let[u]=D.splice(d,1);await wn(u.id)}}if(c("notes")){for(let d=j.length-1;d>=0;d--)if(j[d].ticketId===n.id){let[u]=j.splice(d,1);await kn(u.id)}}return S[n.id]="backlog",await A(n.id),t.json({ticketId:n.id,column:"backlog"})});m.post("/api/tickets/:id/summarize",async(e,t)=>{let n=h.find(s=>s.id===e.params.id);if(!n)return t.status(404).json({error:"ticket not found"});let r=await iu(n);return r?t.json({summary:r}):t.status(502).json({error:"could not generate a summary"})});m.delete("/api/tickets/:id",async(e,t)=>{let n=h.findIndex(o=>o.id===e.params.id);if(n===-1)return t.status(404).json({error:"ticket not found"});let[r]=h.splice(n,1),s=T[r.id];return s&&(delete x[s],delete T[r.id]),delete S[r.id],Y.push(r),await _n(r.externalIdentifier),await tn(r.id),await mn(r.id),t.status(204).end()});m.post("/api/tickets/:id/dismiss",async(e,t)=>{let n=h.find(r=>r.id===e.params.id);return n?(n.dismissedAt=new Date().toISOString(),await tn(n.id),await A(n.id),t.json(n)):t.status(404).json({error:"ticket not found"})});m.post("/api/tickets/:id/restore",async(e,t)=>{let n=h.find(r=>r.id===e.params.id);return n?(n.dismissedAt=void 0,S[n.id]="backlog",await A(n.id),t.json(n)):t.status(404).json({error:"ticket not found"})});m.get("/api/dismissed",(e,t)=>{let n=String(e.query.q??"").trim().toLowerCase(),r=String(e.query.connectionId??""),s=Math.min(Math.max(Number(e.query.limit)||20,1),100),o=Math.max(Number(e.query.offset)||0,0),i=new Set(b.filter(u=>u.linkStatus==="linked").map(u=>u.id)),c=h.filter(u=>u.dismissedAt&&i.has(u.connectionId)&&(!r||u.connectionId===r)).filter(u=>!n||u.title.toLowerCase().includes(n)||u.externalIdentifier.toLowerCase().includes(n)).sort((u,p)=>u.dismissedAt<p.dismissedAt?1:-1),d=c.slice(o,o+s);t.json({items:d,total:c.length})});m.post("/api/board/:connectionId/clear-done",async(e,t)=>{let n=e.params.connectionId,r=h.filter(s=>s.connectionId===n&&rt(s.id)==="done").map(s=>s.id);for(let s of r){let o=h.findIndex(d=>d.id===s),i=o!==-1?h[o].externalIdentifier:null;o!==-1&&h.splice(o,1);let c=T[s];c&&(delete x[c],delete T[s]),delete S[s],i&&await _n(i),await tn(s),await mn(s)}return t.json({removed:r.length})});m.get("/api/conversations",(e,t)=>{let n=ce.map(r=>W[r]).filter(Boolean).sort((r,s)=>r.updatedAt<s.updatedAt?1:-1).map(r=>({id:r.id,title:r.title,updatedAt:r.updatedAt,workspaceId:r.workspaceId,workspaceName:r.workspaceName,tier:r.tier,preview:r.messages.length?r.messages[r.messages.length-1].text:"",messageCount:r.messages.length,starred:r.starred}));t.json(n)});m.get("/api/conversations/:id",(e,t)=>{let n=W[e.params.id];return n?t.json(n):t.status(404).json({error:"conversation not found"})});var tu={Instant:"haiku",Medium:"sonnet",High:"opus"},nu=["low","medium","high","xhigh","max"],Yi=e=>{let t=String(e??"").trim();return nu.includes(t)?t:"low"};async function Vi(e,t,n,r=[]){if(E())return au(t,e.workspaceName,e.tier);let o=(t||(r.length?"Please look at the attached image(s).":""))+(r.length?`
|
|
127
127
|
|
|
128
128
|
Attached files (read these):
|
|
129
129
|
${r.map(d=>`- ${d}`).join(`
|
|
130
|
-
`)}`:""),i=n?`${
|
|
130
|
+
`)}`:""),i=n?`${Un}
|
|
131
131
|
|
|
132
|
-
${o}`:o,c=await Ye({prompt:i,workspace:e.workspaceId,workingDir:e.workingDir,model:
|
|
132
|
+
${o}`:o,c=await Ye({prompt:i,workspace:e.workspaceId,workingDir:e.workingDir,model:tu[e.tier],effort:e.effort,sessionId:n?e.sessionId:null,resume:n?null:e.sessionId,pipelinesTools:!0,pipelinesToolsMode:"restricted",sandbox:!0,sandboxRepos:await qn(e.workingDir)});return c.code===0?c.stdout.trim()||"(the agent returned no output)":`\u26A0\uFE0F The run failed (exit ${c.code}).
|
|
133
133
|
|
|
134
|
-
${(c.stderr||c.stdout).trim().slice(0,500)}`}async function
|
|
134
|
+
${(c.stderr||c.stdout).trim().slice(0,500)}`}async function Zi(e,t){if(E()||!t.trim())return null;let n=`Write a concise title (3\u20136 words, Title Case, no surrounding quotes, no trailing punctuation) that captures the conversation below. Reply with ONLY the title.
|
|
135
135
|
|
|
136
136
|
"""
|
|
137
137
|
${t.slice(0,4e3)}
|
|
138
138
|
"""`;try{let r=await Ye({prompt:n,workspace:e.workspaceId,workingDir:e.workingDir,model:"haiku",effort:"low",noCapture:!0});if(r.code!==0)return null;let o=r.stdout.split(`
|
|
139
|
-
`).map(i=>i.trim()).find(Boolean)?.replace(/^["'“”\s]+|["'“”.\s]+$/g,"").trim();return o?o.length>48?`${o.slice(0,48).trimEnd()}\u2026`:o:null}catch{return null}}function
|
|
139
|
+
`).map(i=>i.trim()).find(Boolean)?.replace(/^["'“”\s]+|["'“”.\s]+$/g,"").trim();return o?o.length>48?`${o.slice(0,48).trimEnd()}\u2026`:o:null}catch{return null}}function ea(e){return e.messages.map(t=>`${t.role==="assistant"?"Assistant":"User"}: ${t.text}`).join(`
|
|
140
140
|
|
|
141
|
-
`).slice(0,8e3)}async function
|
|
141
|
+
`).slice(0,8e3)}async function ru(e){if(E())return null;let t=ea(e);if(!t.trim())return null;let n=`Summarize the conversation below in 2\u20134 short sentences (or a few concise bullet points): what the user wanted and the key points or outcome. Reply with ONLY the summary.
|
|
142
142
|
|
|
143
143
|
"""
|
|
144
144
|
${t}
|
|
145
|
-
"""`;try{let r=await Ye({prompt:n,workspace:e.workspaceId,workingDir:e.workingDir,model:"haiku",effort:"low",noCapture:!0});return r.code!==0?null:r.stdout.trim()||null}catch{return null}}var
|
|
146
|
-
`),o=D.filter(g=>g.ticketId===e.id).sort((g,
|
|
145
|
+
"""`;try{let r=await Ye({prompt:n,workspace:e.workspaceId,workingDir:e.workingDir,model:"haiku",effort:"low",noCapture:!0});return r.code!==0?null:r.stdout.trim()||null}catch{return null}}var su={backlog:"Backlog",in_progress:"In Progress",needs_input:"Blocked",in_review:"In Review",done:"Done"};function ou(e){let t=(g,I)=>g&&g.trim()?g.trim():I,n=T[e.id]?x[T[e.id]]:void 0,r=["No priority","Urgent","High","Medium","Low"][e.priority??0]??"No priority",s=[`Task: ${e.externalIdentifier} \u2014 ${e.title} (source: ${e.provider})`,`Board stage: ${su[rt(e.id)]??rt(e.id)}`,`Source status: ${t(e.statusRaw,"\u2014")}`,`Priority: ${r}`,`Assignee: ${e.assignee?.name??"unassigned"} | Reporter: ${e.reporter?.name??"\u2014"}`,`Project: ${e.project??"\u2014"}${e.parent?` | Parent: ${e.parent.identifier} \xB7 ${e.parent.title}`:""}`,`Due date: ${e.dueDate??"\u2014"}`,e.labels.length?`Labels: ${e.labels.map(g=>g.name).join(", ")}`:"Labels: none",`Run: ${n?`${n.status}${n.stopReason?` (${n.stopReason})`:""}`:"not started"}`,e.blockedReason?`BLOCKED \u2014 reason: ${e.blockedReason}`:null].filter(Boolean).join(`
|
|
146
|
+
`),o=D.filter(g=>g.ticketId===e.id).sort((g,I)=>g.position-I.position),i=o.length?o.map(g=>`- Q: ${g.question}
|
|
147
147
|
A: ${t(g.answer,"(unanswered)")} [${g.resolved?"resolved":"open"}]`).join(`
|
|
148
148
|
`):"No open questions.",c=j.filter(g=>g.ticketId===e.id),d=c.length?c.map(g=>`- [${g.author==="assistant"?"AI":"Me"}] ${g.body.slice(0,500)}`).join(`
|
|
149
|
-
`):"No notes.",u=X.filter(g=>g.ticketId===e.id).slice(-8),p=u.length?u.map((g,
|
|
149
|
+
`):"No notes.",u=X.filter(g=>g.ticketId===e.id).slice(-8),p=u.length?u.map((g,I)=>`Turn ${I+1} [${g.status}${g.model?`, ${g.model}`:""}]:
|
|
150
150
|
Request: ${String(g.prompt??"").slice(0,800)}
|
|
151
151
|
Response: ${String(g.response??"").slice(0,1500)}`).join(`
|
|
152
152
|
|
|
@@ -165,12 +165,12 @@ ${d}`,`
|
|
|
165
165
|
${t(e.conclusion,"No conclusion yet.")}`,`
|
|
166
166
|
=== Run history (oldest \u2192 newest) ===
|
|
167
167
|
${p}`].join(`
|
|
168
|
-
`).slice(0,14e3)}async function
|
|
168
|
+
`).slice(0,14e3)}async function iu(e){if(E())return null;let t=`You are briefing a teammate on the current state of a task. Using the task data below, write a concise summary: what the task is about, what's been done so far, what's still pending or not started yet (explicitly call out empty parts \u2014 no description, no plan, no runs, unanswered questions, etc.), and any blockers. A few short paragraphs or bullet points. Reply with ONLY the summary.
|
|
169
169
|
|
|
170
170
|
"""
|
|
171
|
-
${
|
|
172
|
-
"""`;try{let n=await Ye({prompt:t,workspace:e.workspaceId,workingDir:e.workingDir,model:"haiku",effort:"low",noCapture:!0});return n.code!==0?null:n.stdout.trim()||null}catch{return null}}m.post("/api/conversations",async(e,t)=>{let n=String(e.body?.text??"").trim(),r=ns(e.body);if(!n&&r.length===0)return t.status(400).json({error:"empty message"});let s
|
|
171
|
+
${ou(e)}
|
|
172
|
+
"""`;try{let n=await Ye({prompt:t,workspace:e.workspaceId,workingDir:e.workingDir,model:"haiku",effort:"low",noCapture:!0});return n.code!==0?null:n.stdout.trim()||null}catch{return null}}m.post("/api/conversations",async(e,t)=>{let n=String(e.body?.text??"").trim(),r=ns(e.body);if(!n&&r.length===0)return t.status(400).json({error:"empty message"});let s=$.find(g=>g.id===e.body?.workspaceId)??$[0];if(!s)return t.status(409).json({error:"no workspace available"});let o=String(e.body?.tier??"Instant"),i=Yi(e.body?.effort),c=String(e.body?.workingDir??"").trim(),d=`conv_${++G.convSeq}`,u=na(n,r),p={id:d,title:ta(u),workspaceId:s.id,workspaceName:s.name,tier:o,effort:i,workingDir:c||void 0,sessionId:ot(),messages:[{id:"m1",role:"user",text:u,ts:be(),...r.length?{attachments:r.map(Xi)}:{}}],starred:!1,createdAt:new Date().toISOString(),updatedAt:new Date().toISOString()};W[d]=p,ce.push(d),await Xe(d),t.status(201).json(p),(async()=>{let g=Zi(p,n),I;try{I=await Vi(p,n,!0,r)}catch(w){I=`\u26A0\uFE0F The run failed.
|
|
173
173
|
|
|
174
|
-
${String(w).slice(0,500)}`}let f=await Promise.race([g,new Promise(w=>setTimeout(()=>w(null),3e3))]);f&&(p.title=f),p.messages.push({id:"m2",role:"assistant",text:
|
|
175
|
-
`)[0].trim();return t.length>48?`${t.slice(0,48).trimEnd()}\u2026`:t}function ns(e){let t=e?.attachments;return Array.isArray(t)?t.map(n=>String(n)).filter(Boolean):[]}function
|
|
176
|
-
`)}m.get("/api/schedules",(e,t)=>{t.json(
|
|
174
|
+
${String(w).slice(0,500)}`}let f=await Promise.race([g,new Promise(w=>setTimeout(()=>w(null),3e3))]);f&&(p.title=f),p.messages.push({id:"m2",role:"assistant",text:I,ts:be()}),p.updatedAt=new Date().toISOString(),await Xe(d),f||g.then(w=>{w&&(p.title=w,Xe(d))})})()});m.post("/api/conversations/:id/messages",async(e,t)=>{let n=W[e.params.id];if(!n)return t.status(404).json({error:"conversation not found"});let r=String(e.body?.text??"").trim(),s=ns(e.body);if(!r&&s.length===0)return t.status(400).json({error:"empty message"});n.messages.push({id:`m${n.messages.length+1}`,role:"user",text:na(r,s),ts:be(),...s.length?{attachments:s.map(Xi)}:{}});let o=await Vi(n,r,!1,s);return n.messages.push({id:`m${n.messages.length+1}`,role:"assistant",text:o,ts:be()}),n.updatedAt=new Date().toISOString(),await Xe(n.id),t.json(n)});m.patch("/api/conversations/:id",async(e,t)=>{let n=W[e.params.id];if(!n)return t.status(404).json({error:"conversation not found"});let r=String(e.body?.title??"").trim();r&&(n.title=r),typeof e.body?.starred=="boolean"&&(n.starred=e.body.starred);let s=String(e.body?.tier??"").trim();return s&&(n.tier=s),"effort"in(e.body??{})&&(n.effort=Yi(e.body.effort)),await Xe(n.id),t.json(n)});m.post("/api/conversations/:id/retitle",async(e,t)=>{let n=W[e.params.id];if(!n)return t.status(404).json({error:"conversation not found"});let r=await Zi(n,ea(n));return r&&(n.title=r,await Xe(n.id)),t.json(n)});m.post("/api/conversations/:id/summarize",async(e,t)=>{let n=W[e.params.id];if(!n)return t.status(404).json({error:"conversation not found"});let r=await ru(n);return r?t.json({summary:r}):t.status(502).json({error:"could not generate a summary"})});m.delete("/api/conversations/:id",async(e,t)=>{let n=e.params.id;if(!W[n])return t.status(404).json({error:"conversation not found"});delete W[n];let r=ce.indexOf(n);return r!==-1&&ce.splice(r,1),await ws(n),t.status(204).end()});function ta(e){let t=e.split(`
|
|
175
|
+
`)[0].trim();return t.length>48?`${t.slice(0,48).trimEnd()}\u2026`:t}function ns(e){let t=e?.attachments;return Array.isArray(t)?t.map(n=>String(n)).filter(Boolean):[]}function na(e,t){if(e)return e;let n=t.length;return n?`(${n} image${n>1?"s":""})`:""}function au(e,t,n){let r=ta(e);return[`Running under ${t} (${n}) \u2014 your team, guardrails and memory are in context.`,"",`Here's how I'd approach "${r}":`,"\u2022 Start with the smallest shippable change.","\u2022 Reuse what's already in the repo before adding anything new.","\u2022 Verify with the existing tests, then tighten from there.","","(Mocked reply \u2014 the real chat drives your own claude on the same owned runtime.)"].join(`
|
|
176
|
+
`)}m.get("/api/schedules",(e,t)=>{t.json(se.map(n=>O[n]).filter(Boolean))});m.get("/api/schedules/:id",(e,t)=>{let n=O[e.params.id];return n?t.json(n):t.status(404).json({error:"schedule not found"})});m.get("/api/schedules/:id/runs",async(e,t)=>{if(!O[e.params.id])return t.status(404).json({error:"schedule not found"});let n=Math.max(0,Number(e.query.offset??0)),r=Math.min(Math.max(1,Number(e.query.limit??6)),100),{runs:s,total:o}=await Ss(e.params.id,r,n),i=n+r<o?n+r:null;return t.json({runs:s,nextOffset:i,total:o})});m.post("/api/schedules",async(e,t)=>{let n=String(e.body?.name??"").trim()||"Untitled schedule",r=String(e.body?.prompt??"").trim(),s=$.find(g=>g.id===e.body?.workspaceId)??$[0],o=["team","agent","anyone"].includes(e.body?.assignmentKind)?e.body.assignmentKind:"anyone",i=o==="team"?Se.find(g=>g.id===e.body?.teamId):void 0,c=o==="agent"?xe.find(g=>g.id===e.body?.agentId):void 0,d=String(e.body?.cadence??"Every 1 hour").trim(),u=`sched_${++G.schedSeq}`,p={id:u,name:n,prompt:r,workspaceId:s.id,workspaceName:s.name,assignmentKind:o,teamId:i?.id,teamName:i?.name,agentId:c?.id,agentName:c?.name,cadence:d,workingDir:String(e.body?.workingDir??"").trim()||Yn(s.id),model:Re(e.body?.model??l.defaultModel),effort:Te(e.body?.effort??l.defaultEffort),enabled:!0,starred:!1,createdAt:new Date().toISOString(),lastRunAt:null,nextRunAt:sn(Date.now(),d)};return O[u]=p,se.unshift(u),z[u]=[],await zt(u),t.status(201).json(p)});m.post("/api/schedules/:id/run",async(e,t)=>{let n=O[e.params.id];if(!n)return t.status(404).json({error:"schedule not found"});let r=await Pr(n.id,!1);return r?t.status(201).json(r):t.status(409).json({error:"schedule is already running"})});m.patch("/api/schedules/:id",async(e,t)=>{let n=O[e.params.id];if(!n)return t.status(404).json({error:"schedule not found"});let r=e.body??{};if(typeof r.enabled=="boolean"&&(n.enabled=r.enabled,n.nextRunAt=r.enabled?sn(Date.now(),n.cadence):null),typeof r.starred=="boolean"&&(n.starred=r.starred),typeof r.name=="string"&&r.name.trim()&&(n.name=r.name.trim()),typeof r.prompt=="string"&&(n.prompt=r.prompt),typeof r.workingDir=="string"&&(n.workingDir=r.workingDir.trim()||void 0),r.model!==void 0&&(n.model=Re(r.model,n.model||ke)),r.effort!==void 0&&(n.effort=Te(r.effort,n.effort||ye)),typeof r.cadence=="string"&&r.cadence.trim()&&(n.cadence=r.cadence.trim()),typeof r.workspaceId=="string"){let s=$.find(o=>o.id===r.workspaceId);s&&(n.workspaceId=s.id,n.workspaceName=s.name)}if(["team","agent","anyone"].includes(r.assignmentKind)){n.assignmentKind=r.assignmentKind;let s=r.assignmentKind==="team"?Se.find(i=>i.id===r.teamId):void 0,o=r.assignmentKind==="agent"?xe.find(i=>i.id===r.agentId):void 0;n.teamId=s?.id,n.teamName=s?.name,n.agentId=o?.id,n.agentName=o?.name}return await zt(n.id),t.json(n)});m.delete("/api/schedules/:id",async(e,t)=>{let n=e.params.id;if(!O[n])return t.status(404).json({error:"schedule not found"});delete O[n],delete z[n];let r=se.indexOf(n);return r!==-1&&se.splice(r,1),await ys(n),t.status(204).end()});m.get("/api/batches",(e,t)=>t.json(Lt()));m.get("/api/batches/:id",(e,t)=>{try{return t.json(Gt(e.params.id))}catch(n){return it(t,n)}});m.post("/api/batches",async(e,t)=>{try{let n=await Ze(e.body??{});return t.status(201).json(n)}catch(n){return it(t,n)}});m.patch("/api/batches/:id",async(e,t)=>{try{return t.json(await oo(e.params.id,e.body??{}))}catch(n){return it(t,n)}});m.delete("/api/batches/:id",async(e,t)=>{try{return await Dn(e.params.id),t.status(204).end()}catch(n){return it(t,n)}});m.post("/api/batches/:id/start",async(e,t)=>{try{return t.json(await Ut(e.params.id))}catch(n){return it(t,n)}});m.post("/api/batches/:id/pause",async(e,t)=>{try{return t.json(await qt(e.params.id))}catch(n){return it(t,n)}});m.post("/api/batches/:id/stop",async(e,t)=>{try{return t.json(await Ft(e.params.id))}catch(n){return it(t,n)}});var Vr=["publicApiEnabled","apiKeyHash","apiKeyLast4","apiKeyCreatedAt","webhookEnabled","webhookUrl","webhookHeaders","webhookEvents","webhookSecret"];m.get("/api/settings",(e,t)=>{let n={...l};for(let r of Vr)delete n[r];t.json(n)});m.put("/api/settings",async(e,t)=>{let n={...e.body??{}};for(let s of Vr)delete n[s];Object.assign(l,n),"defaultModel"in n&&(l.defaultModel=Re(l.defaultModel)),"defaultEffort"in n&&(l.defaultEffort=Te(l.defaultEffort)),"autoAssignImports"in n&&(l.autoAssignImports=!!n.autoAssignImports),"importDelegate"in n&&(l.importDelegate=Fd(n.importDelegate)),await te();let r={...l};for(let s of Vr)delete r[s];t.json(r)});m.get("/api/public-api",(e,t)=>t.json(rn()));m.post("/api/public-api/enable",async(e,t)=>{t.json(await bo(e.body?.enabled===!0))});m.post("/api/public-api/key",async(e,t)=>{t.status(201).json(await Io())});m.delete("/api/public-api/key",async(e,t)=>{t.json(await So())});m.put("/api/public-api/webhook",async(e,t)=>{try{t.json(await xo(e.body??{}))}catch(n){if(n instanceof Ee)return t.status(n.status).json({error:n.message});throw n}});m.post("/api/public-api/webhook/secret",async(e,t)=>{t.status(201).json(await vo())});m.post("/api/public-api/webhook/test",async(e,t)=>{t.json(await Ro())});function be(){let e=new Date;return`${String(e.getHours()).padStart(2,"0")}:${String(e.getMinutes()).padStart(2,"0")}`}Co(m,dn);var Yr=re.join(re.dirname(Gd(import.meta.url)),"..","dist");dn===Zr&&Ce.existsSync(re.join(Yr,"index.html"))&&(m.use(Xn.static(Yr)),m.get("*",(e,t,n)=>{if(e.path.startsWith("/api"))return n();t.sendFile(re.join(Yr,"index.html"))}));process.env.PIPELINES_TEST||Ms().then(e=>{console.log(e.loggedIn?`[pipelines] authenticated as ${e.email??e.userId}`:`[pipelines] not authenticated: ${e.error}`)}).then(()=>us()).then(()=>ms()).then(()=>qd()).then(()=>to()).then(()=>Oo()).then(()=>po()).then(()=>Po()).then(()=>Pi({listConnections:()=>b,connectedWorkspaces:()=>l.connectedWorkspaces,syncOne:ts,isMock:E})).then(()=>eu()).then(()=>m.listen(dn,()=>{console.log(`[pipelines] ready http://localhost:${dn} (sqlite)`)})).then(()=>{l.publicApiEnabled&&$n(Ln)}).catch(e=>{console.error("[pipelines] startup failed:",e),process.exit(1)});export{m as app};
|