impactus-swarm 0.1.2 → 0.1.4

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.

Potentially problematic release.


This version of impactus-swarm might be problematic. Click here for more details.

package/bin/cli.js CHANGED
@@ -20,7 +20,7 @@ if (major < 22 || (major === 22 && minor < 5)) {
20
20
 
21
21
  // Parse CLI args
22
22
  const args = process.argv.slice(2);
23
- let port = 3773;
23
+ let port = 7773;
24
24
  let noOpen = false;
25
25
  let relayUrl = null;
26
26
  let allowedIps = [];
@@ -62,7 +62,7 @@ impactus-swarm - AI-powered terminal swarm orchestration platform
62
62
  Usage: impactus-swarm [options]
63
63
 
64
64
  Options:
65
- --port <number> Port to run the server on (default: 3773)
65
+ --port <number> Port to run the server on (default: 7773)
66
66
  --relay <url> Connect to a relay server for remote access
67
67
  --ip <address> Allow only this IP to connect via relay (can be repeated)
68
68
  --no-open Don't open the browser automatically
@@ -99,11 +99,7 @@ if (!existsSync(webDir)) {
99
99
  process.exit(1);
100
100
  }
101
101
 
102
- // Set env vars
103
- process.env.IMPACTUS_WEB_DIR = webDir;
104
- process.env.PORT = String(port);
105
-
106
- // Fork the server
102
+ // Fork the server — PORT and IMPACTUS_WEB_DIR are passed via env below (not on process.env to avoid polluting parent)
107
103
  const serverArgs = [];
108
104
  if (!noOpen) serverArgs.push("--open");
109
105
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "impactus-swarm",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "AI-powered terminal swarm orchestration platform",
5
5
  "type": "module",
6
6
  "bin": {
package/server/main.js CHANGED
@@ -78,7 +78,7 @@ You can document your own discoveries for teammates to benefit from.`),r.push(""
78
78
  VALUES (?, ?, ?, ?, ?, ?, 'idle', ?, ?, ?, ?, ?, ?)`).run(n,e,s,t.name,t.role,t.cli,t.autoApprove?1:0,t.systemPrompt??null,JSON.stringify(t.skills??[]),JSON.stringify(t.mcps??[]),t.workScope?JSON.stringify(t.workScope):null,r),this.rowToAgent({id:n,swarmId:e,index:s,name:t.name,role:t.role,cli:t.cli,status:"idle",autoApprove:t.autoApprove?1:0,systemPrompt:t.systemPrompt??null,skills:JSON.stringify(t.skills??[]),mcps:JSON.stringify(t.mcps??[]),workScope:t.workScope?JSON.stringify(t.workScope):null,worktreeBranch:null,worktreePath:null,createdAt:r})}async launch(e){let t=this.buildSwarm(e);if(!t)throw new Error(`Swarm ${e} not found`);let s=new Date().toISOString();this.db.prepare("UPDATE swarms SET status = 'active', launchedAt = ? WHERE id = ?").run(s,e);let r=O(t.directory,".swarm");G(O(r,"agents"),{recursive:!0}),G(O(r,"registry"),{recursive:!0}),G(O(r,"progress"),{recursive:!0});let n=this.settingsManager?.getEnvVarsForWorkspace(t.workspaceId)??{};for(let c of t.agents){this.db.prepare("UPDATE swarm_agents SET status = 'booting' WHERE id = ?").run(c.id);let d=this.buildSwarm(e),l=await this.agentSession.boot(c,d,this.terminalManager,Object.keys(n).length>0?n:void 0,this.worktreeManager),{terminalId:u,worktreePath:p,worktreeBranch:m}=l,f=this.agentTerminals.get(c.id)??[];f.push(u),this.agentTerminals.set(c.id,f),this.db.prepare("UPDATE swarm_agents SET status = 'running', worktreePath = ?, worktreeBranch = ? WHERE id = ?").run(p??null,m??null,c.id),this.consoleManager.addMessage({swarmId:e,agentId:c.id,agentName:c.name,agentRole:c.role,message:`Agent "${c.name}" (${c.role}) started on terminal ${u}`,source:"system"});let w=this.coordinatorLoops.get(e);if(w?.isRunning&&w.registerAgentTerminal(c.id,u,c),c.role==="coordinator"){let g=new Ee;g.start(c.id,e,this,this.terminalManager,this.broadcast,this.taskManager??void 0),this.coordinatorLoops.set(e,g);for(let h of t.agents){let T=this.agentTerminals.get(h.id)??[];for(let E of T)g.registerAgentTerminal(h.id,E,h)}}}let o=this.buildSwarm(e),i={swarmId:e,status:o.status,launchedAt:o.launchedAt,agents:o.agents.map(c=>({id:c.id,name:c.name,role:c.role,status:c.status}))};if(sr(O(r,"progress","status.json"),JSON.stringify(i,null,2)),this.taskManager){let c=this.taskManager.list({swarmId:e}),d=o.agents.filter(u=>u.role!=="coordinator"),l=0;for(let u of c)if(u.status==="todo"&&!u.assignedAgentId&&d.length>0){let p=d[l%d.length];this.taskManager.assign(u.id,p.id),l++}}this.broadcast(`swarm.status.${e}`,{swarmId:e,status:"active"})}stop(e){let t=this.buildSwarm(e);if(!t)throw new Error(`Swarm ${e} not found`);let s=this.orchestrators.get(e);s&&(s.stop(),this.orchestrators.delete(e));let r=this.coordinatorLoops.get(e);r&&(r.stop(),this.coordinatorLoops.delete(e));for(let o of t.agents){let i=this.agentTerminals.get(o.id)??[];for(let c of i)this.terminalManager.close(c);this.agentTerminals.delete(o.id),this.db.prepare("UPDATE swarm_agents SET status = 'idle' WHERE id = ?").run(o.id)}if(t.useWorktrees&&t.directory)try{this.worktreeManager.removeAllForSwarm(t.directory,e)}catch(o){console.warn(`[WorktreeManager] Cleanup failed for swarm ${e}:`,o)}let n=new Date().toISOString();this.db.prepare("UPDATE swarms SET status = 'completed', stoppedAt = ? WHERE id = ?").run(n,e),this.consoleManager.addMessage({swarmId:e,message:"Swarm stopped",source:"system"}),this.broadcast(`swarm.status.${e}`,{swarmId:e,status:"completed"})}pause(e){this.db.prepare("UPDATE swarms SET status = 'paused' WHERE id = ?").run(e),this.broadcast(`swarm.status.${e}`,{swarmId:e,status:"paused"})}resume(e){this.db.prepare("UPDATE swarms SET status = 'active' WHERE id = ?").run(e),this.broadcast(`swarm.status.${e}`,{swarmId:e,status:"active"})}list(e){return this.db.prepare("SELECT id FROM swarms WHERE workspaceId = ? ORDER BY createdAt DESC").all(e).map(s=>this.buildSwarm(s.id))}get(e){return this.db.prepare("SELECT id FROM swarms WHERE id = ?").get(e)?this.buildSwarm(e):null}delete(e){let t=this.get(e);return t&&t.status==="active"&&this.stop(e),this.db.prepare("DELETE FROM swarms WHERE id = ?").run(e).changes>0}message(e,t,s){if(t){let r=this.agentTerminals.get(t)??[];for(let n of r)this.terminalManager.write(n,s+"\r")}else{let r=this.buildSwarm(e);for(let n of r.agents){let o=this.agentTerminals.get(n.id)??[];for(let i of o)this.terminalManager.write(i,s+"\r")}}this.consoleManager.addMessage({swarmId:e,agentId:t,message:s,source:"user"})}addAgent(e){let t=new Date().toISOString(),r=(this.db.prepare('SELECT MAX("index") as maxIdx FROM swarm_agents WHERE swarmId = ?').get(e.swarmId)?.maxIdx??-1)+1;return this.insertAgent(e.swarmId,e.agent,r,t)}removeAgent(e){let t=this.agentTerminals.get(e.agentId)??[];for(let r of t)this.terminalManager.close(r);return this.agentTerminals.delete(e.agentId),this.db.prepare("DELETE FROM swarm_agents WHERE id = ? AND swarmId = ?").run(e.agentId,e.swarmId).changes>0}updateAgent(e){let{updates:t}=e,s=[],r=[];if(t.name!==void 0&&(s.push("name = ?"),r.push(t.name)),t.role!==void 0&&(s.push("role = ?"),r.push(t.role)),t.cli!==void 0&&(s.push("cli = ?"),r.push(t.cli)),t.autoApprove!==void 0&&(s.push("autoApprove = ?"),r.push(t.autoApprove?1:0)),t.systemPrompt!==void 0&&(s.push("systemPrompt = ?"),r.push(t.systemPrompt)),t.skills!==void 0&&(s.push("skills = ?"),r.push(JSON.stringify(t.skills))),t.mcps!==void 0&&(s.push("mcps = ?"),r.push(JSON.stringify(t.mcps))),t.workScope!==void 0&&(s.push("workScope = ?"),r.push(JSON.stringify(t.workScope))),s.length===0)return null;r.push(e.agentId,e.swarmId),this.db.prepare(`UPDATE swarm_agents SET ${s.join(", ")} WHERE id = ? AND swarmId = ?`).run(...r);let n=this.db.prepare("SELECT * FROM swarm_agents WHERE id = ?").get(e.agentId);return n?this.rowToAgent(n):null}getStats(e){let t=this.getAgents(e),s=t.filter(c=>c.status==="running"||c.status==="booting").length,r=t.filter(c=>c.status==="done").length,n=t.filter(c=>c.status==="error").length,o=this.db.prepare("SELECT launchedAt FROM swarms WHERE id = ?").get(e),i=0;return o?.launchedAt&&(i=Date.now()-new Date(o.launchedAt).getTime()),{activeAgents:s,completedTasks:r,errors:n,escalations:0,reviewsPending:0,elapsedTime:i}}setTopology(e,t){this.topologies.set(e,t),this.db.prepare("UPDATE swarms SET knowledge = json_set(knowledge, '$.topology', ?) WHERE id = ?").run(JSON.stringify(t),e)}launchOrchestrated(e){let t=this.buildSwarm(e);if(!t)throw new Error(`Swarm ${e} not found`);let s=new Date().toISOString();this.db.prepare("UPDATE swarms SET status = 'active', launchedAt = ? WHERE id = ?").run(s,e);let r=O(t.directory,".swarm");G(O(r,"agents"),{recursive:!0}),G(O(r,"registry"),{recursive:!0}),G(O(r,"progress"),{recursive:!0});let n=this.settingsManager?.getEnvVarsForWorkspace(t.workspaceId)??{},o=this.topologies.get(e)??t.topology,i=this.settingsManager?.get("providers.claude_binary_path")?.value??this.providerRegistry?.getClaudeBinaryPath()??"claude",c=new Te({swarm:t,topology:o,claudeBinaryPath:i,env:Object.keys(n).length>0?n:void 0,onAgentOutput:d=>{this.broadcast(`orchestrator.agent.output.${e}`,d),this.consoleManager.addMessage({swarmId:e,agentId:d.agentId,agentName:d.agentName,message:`[${d.type}] ${d.content.slice(0,500)}`,source:"agent"})},onCLISpawned:d=>{this.broadcast(`orchestrator.cli.spawned.${e}`,d),this.consoleManager.addMessage({swarmId:e,agentId:d.agentId,message:`Spawned ${d.cli} CLI: ${d.prompt.slice(0,200)}`,source:"system"})},onCLICompleted:d=>{this.broadcast(`orchestrator.cli.completed.${e}`,d)},onEscalation:d=>{this.broadcast(`orchestrator.escalation.${e}`,d),this.broadcast(`swarm.escalation.${e}`,{swarmId:e,agentId:d.escalation.agentId,agentName:d.escalation.agentName,message:d.escalation.reason,requiresResponse:!0,escalation:d.escalation})},onAgentCompleted:d=>{this.broadcast(`swarm.agent.status.${e}`,{swarmId:e,agentId:d.agentId,status:d.error?"error":"done"}),this.db.prepare("UPDATE swarm_agents SET status = ? WHERE id = ?").run(d.error?"error":"done",d.agentId)},onSwarmCompleted:d=>{this.db.prepare("UPDATE swarms SET status = 'completed', stoppedAt = ? WHERE id = ?").run(new Date().toISOString(),e),this.broadcast(`swarm.status.${e}`,{swarmId:e,status:"completed"}),this.consoleManager.addMessage({swarmId:e,message:"Swarm orchestration completed",source:"system"}),this.orchestrators.delete(e)}});this.orchestrators.set(e,c);for(let d of t.agents)this.db.prepare("UPDATE swarm_agents SET status = 'running' WHERE id = ?").run(d.id);this.consoleManager.addMessage({swarmId:e,message:`Swarm "${t.name}" launched in orchestrated mode with ${t.agents.length} agents`,source:"system"}),this.broadcast(`swarm.status.${e}`,{swarmId:e,status:"active"}),c.start().catch(d=>{console.error(`[AgentOrchestrator] Swarm ${e} failed:`,d),this.db.prepare("UPDATE swarms SET status = 'error', stoppedAt = ? WHERE id = ?").run(new Date().toISOString(),e),this.broadcast(`swarm.status.${e}`,{swarmId:e,status:"error"})})}async spawnCLI(e,t,s){let r=this.orchestrators.get(e);if(!r)throw new Error(`No active orchestrator for swarm ${e}`);return{sessionId:await r.getCLIProvider().spawn(t,s)}}respondToEscalation(e,t){for(let s of this.orchestrators.values())s.respondToEscalation(e,t)}getOrchestrator(e){return this.orchestrators.get(e)}getAgents(e){return this.db.prepare('SELECT * FROM swarm_agents WHERE swarmId = ? ORDER BY "index" ASC').all(e).map(s=>this.rowToAgent(s))}buildSwarm(e){let t=this.db.prepare("SELECT * FROM swarms WHERE id = ?").get(e);if(!t)throw new Error(`Swarm ${e} not found`);let s=this.getAgents(e),r=this.getStats(e);return{id:t.id,workspaceId:t.workspaceId,name:t.name,mission:t.mission,directory:t.directory,knowledge:JSON.parse(t.knowledge),agents:s,status:t.status,presetSize:t.presetSize??void 0,cliForAll:t.cliForAll??void 0,autoApprove:t.autoApprove===1,useWorktrees:t.useWorktrees===1,stats:r,createdAt:t.createdAt,launchedAt:t.launchedAt??void 0,stoppedAt:t.stoppedAt??void 0}}rowToAgent(e){let t=this.agentTerminals.get(e.id)??[];return{id:e.id,swarmId:e.swarmId,index:e.index,name:e.name,role:e.role,cli:e.cli,status:e.status,autoApprove:e.autoApprove===1,terminalIds:t,systemPrompt:e.systemPrompt??void 0,skills:JSON.parse(e.skills),mcps:JSON.parse(e.mcps),workScope:e.workScope?JSON.parse(e.workScope):void 0,worktreeBranch:e.worktreeBranch??void 0,worktreePath:e.worktreePath??void 0,elapsedTime:0}}};import{randomUUID as Lt}from"node:crypto";import D from"node:fs";import _ from"node:path";import Nt from"node:os";var Ot={"frontend-design":{description:"Frontend design patterns and best practices"},shadcn:{description:"shadcn/ui component library integration"},"next-best-practices":{description:"Next.js best practices and patterns"},"systematic-debugging":{description:"Systematic debugging methodology"},"web-design-guidelines":{description:"Web design guidelines and principles"},"vercel-react-best-practices":{description:"Vercel + React best practices"},"supabase-postgres-best-practices":{description:"Supabase + Postgres best practices"},"expo-tailwind-setup":{description:"Expo + Tailwind CSS setup guide"},"nodejs-backend-patterns":{description:"Node.js backend architecture patterns"},"api-design-principles":{description:"API design principles and REST patterns"},"database-schema-design":{description:"Database schema design best practices"},"better-auth":{description:"Authentication best practices with Better Auth"},"skill-creator":{description:"Create custom skills for agents"},"building-native-ui":{description:"Building native UI components"},"expo-deployment":{description:"Expo deployment and distribution"}},Dt=[{id:"nextjs-convex-clerk",name:"Next.js + Convex + Clerk",description:"Full-stack Next.js with Convex backend and Clerk auth",skills:["next-best-practices","frontend-design","shadcn","better-auth"],mcps:[],stack:"nextjs"},{id:"react-native-expo",name:"React Native + Expo",description:"Mobile development with React Native and Expo",skills:["expo-tailwind-setup","expo-deployment","building-native-ui"],mcps:[],stack:"mobile"},{id:"fullstack-supabase",name:"Full Stack Supabase",description:"Full-stack app with Supabase backend and Postgres",skills:["supabase-postgres-best-practices","database-schema-design","api-design-principles","better-auth"],mcps:[],stack:"supabase"},{id:"vibe-coding",name:"Vibe Coding",description:"Generic vibe coding setup for rapid prototyping",skills:["frontend-design","web-design-guidelines","systematic-debugging","shadcn"],mcps:[],stack:"generic"},{id:"api-nodejs",name:"API + Node.js",description:"Backend API development with Node.js",skills:["nodejs-backend-patterns","api-design-principles","database-schema-design","systematic-debugging"],mcps:[],stack:"backend"}],Ie=class{projectSkillsDir(e){return _.join(e,".swarm","registry","skills")}projectMcpsDir(e){return _.join(e,".swarm","registry","mcps")}globalSkillsDir(){return _.join(Nt.homedir(),".swarm","registry","skills")}globalMcpsDir(){return _.join(Nt.homedir(),".swarm","registry","mcps")}readJsonFiles(e){return D.existsSync(e)?D.readdirSync(e).filter(s=>s.endsWith(".json")).map(s=>JSON.parse(D.readFileSync(_.join(e,s),"utf-8"))):[]}listSkills(e){let t=this.readJsonFiles(this.globalSkillsDir());return e?[...this.readJsonFiles(this.projectSkillsDir(e)),...t]:t}searchSkills(e){let t=e.toLowerCase();return Object.entries(Ot).filter(([s,r])=>s.includes(t)||r.description.toLowerCase().includes(t)).map(([s,r])=>({name:s,description:r.description,source:"skills.sh"}))}installSkill(e,t){let s=this.projectSkillsDir(t);D.mkdirSync(s,{recursive:!0});let r=Ot[e],n={id:Lt(),name:e,description:r?.description??`Custom skill: ${e}`,source:r?"skills.sh":"custom",installedAt:new Date().toISOString()};return D.writeFileSync(_.join(s,`${e}.json`),JSON.stringify(n,null,2)),n}removeSkill(e,t){let s=_.join(this.projectSkillsDir(t),`${e}.json`);return D.existsSync(s)?(D.unlinkSync(s),!0):!1}listBundles(){return Dt}installBundle(e,t){let s=Dt.find(r=>r.id===e);if(!s)throw new Error(`Bundle "${e}" not found`);for(let r of s.skills)this.installSkill(r,t)}listMcps(e){let t=this.readJsonFiles(this.globalMcpsDir());return e?[...this.readJsonFiles(this.projectMcpsDir(e)),...t]:t}configureMcp(e){let t=this.projectMcpsDir(e.projectDir);D.mkdirSync(t,{recursive:!0});let s={id:Lt(),name:e.name,command:e.command,args:e.args,env:e.env,enabled:!0};return D.writeFileSync(_.join(t,`${e.name}.json`),JSON.stringify(s,null,2)),s}};import{randomUUID as Pt}from"node:crypto";import{execSync as xt}from"node:child_process";var Re=class{db;broadcast;constructor(e,t){this.db=e,this.broadcast=t}create(e){let t=Pt(),s=new Date().toISOString();if(this.db.prepare(`INSERT INTO tasks (id, swarmId, workspaceId, title, description, assignedAgentId, useWorktree, status, approvalMode, createdAt, updatedAt)
79
79
  VALUES (?, ?, ?, ?, ?, ?, ?, 'todo', ?, ?, ?)`).run(t,e.swarmId??null,e.workspaceId,e.title,e.description??"",e.assignedAgentId??null,e.useWorktree?1:0,e.approvalMode??"auto",s,s),e.attachments&&e.attachments.length>0){let n=this.db.prepare(`INSERT INTO task_attachments (id, taskId, type, data, width, height, label, createdAt)
80
80
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);for(let o of e.attachments)n.run(Pt(),t,o.type,o.data,o.width,o.height,o.label??null,s)}if(e.dependencies&&e.dependencies.length>0){let n=this.db.prepare("INSERT INTO task_dependencies (taskId, dependsOnTaskId) VALUES (?, ?)");for(let o of e.dependencies)n.run(t,o)}let r=this.getById(t);return this.broadcast("task.created",{task:r}),r}update(e,t){let s=[],r=[];if(t.title!==void 0&&(s.push("title = ?"),r.push(t.title)),t.description!==void 0&&(s.push("description = ?"),r.push(t.description)),t.assignedAgentId!==void 0&&(s.push("assignedAgentId = ?"),r.push(t.assignedAgentId)),t.useWorktree!==void 0&&(s.push("useWorktree = ?"),r.push(t.useWorktree?1:0)),t.approvalMode!==void 0&&(s.push("approvalMode = ?"),r.push(t.approvalMode)),t.output!==void 0&&(s.push("output = ?"),r.push(t.output)),t.filesChanged!==void 0&&(s.push("filesChanged = ?"),r.push(JSON.stringify(t.filesChanged))),s.length===0)return this.getById(e);let n=new Date().toISOString();s.push("updatedAt = ?"),r.push(n),r.push(e),this.db.prepare(`UPDATE tasks SET ${s.join(", ")} WHERE id = ?`).run(...r);let o=this.getById(e);return this.broadcast("task.updated",{task:o}),o}get(e){return this.getById(e)}delete(e){return this.db.prepare("DELETE FROM tasks WHERE id = ?").run(e).changes>0}list(e){let t="SELECT * FROM tasks WHERE 1=1",s=[];return e.workspaceId&&(t+=" AND workspaceId = ?",s.push(e.workspaceId)),e.swarmId&&(t+=" AND swarmId = ?",s.push(e.swarmId)),t+=" ORDER BY createdAt ASC",this.db.prepare(t).all(...s).map(n=>this.rowToTask(n))}move(e,t){if(t==="running"&&!this.checkDependencies(e))throw new Error(`Task ${e} is blocked by unfinished dependencies`);let s=this.getById(e);if(!s)throw new Error(`Task ${e} not found`);let r=s.status,n=new Date().toISOString();if(t==="running"&&s.useWorktree&&s.swarmId){let i=this.getSwarmDirectory(s.swarmId);if(i){let c=`.swarm/worktrees/${s.id}`;try{xt(`git worktree add ${c} -b swarm/${s.id}`,{cwd:i,stdio:"pipe"}),this.db.prepare("UPDATE tasks SET worktreePath = ?, status = ?, updatedAt = ? WHERE id = ?").run(c,t,n,e);let d=this.getById(e);return this.broadcast("task.moved",{taskId:e,from:r,to:t}),d}catch{}}}if(t==="done"&&s.worktreePath&&s.swarmId){let i=this.getSwarmDirectory(s.swarmId);if(i)try{xt(`git worktree remove ${s.worktreePath}`,{cwd:i,stdio:"pipe"})}catch{}}this.db.prepare("UPDATE tasks SET status = ?, updatedAt = ? WHERE id = ?").run(t,n,e);let o=this.getById(e);return this.broadcast("task.moved",{taskId:e,from:r,to:t}),o}getSwarmDirectory(e){return this.db.prepare("SELECT directory FROM swarms WHERE id = ?").get(e)?.directory??null}updateTerminalId(e,t){let s=new Date().toISOString();this.db.prepare("UPDATE tasks SET terminalId = ?, updatedAt = ? WHERE id = ?").run(t,s,e)}assign(e,t){let s=new Date().toISOString();this.db.prepare("UPDATE tasks SET assignedAgentId = ?, updatedAt = ? WHERE id = ?").run(t,s,e);let r=this.getById(e);return this.broadcast("task.assigned",{taskId:e,agentId:t}),r}complete(e,t,s){let r=new Date().toISOString();this.db.prepare("UPDATE tasks SET status = 'done', output = ?, filesChanged = ?, updatedAt = ?, completedAt = ? WHERE id = ?").run(t??null,JSON.stringify(s??[]),r,r,e);let n=this.getById(e);return this.broadcast("task.completed",{task:n}),n}checkDependencies(e){let t=this.getBlockedBy(e);if(t.length===0)return!0;for(let s of t){let r=this.getById(s);if(!r||r.status!=="done")return!1}return!0}getBlockedBy(e){return this.db.prepare("SELECT dependsOnTaskId FROM task_dependencies WHERE taskId = ?").all(e).map(s=>s.dependsOnTaskId)}getById(e){let t=this.db.prepare("SELECT * FROM tasks WHERE id = ?").get(e);return t?this.rowToTask(t):null}getAttachments(e){return this.db.prepare("SELECT * FROM task_attachments WHERE taskId = ?").all(e).map(s=>({id:s.id,type:s.type,data:s.data,width:s.width,height:s.height,label:s.label??void 0,createdAt:s.createdAt}))}rowToTask(e){let t=this.getDependencies(e.id),s=this.getAllTerminalIds(e.id),r=this.getAttachments(e.id);return{id:e.id,swarmId:e.swarmId??void 0,workspaceId:e.workspaceId,title:e.title,description:e.description,dependencies:t,assignedAgentId:e.assignedAgentId??void 0,useWorktree:e.useWorktree===1,worktreePath:e.worktreePath??void 0,status:e.status,approvalMode:e.approvalMode,terminalId:e.terminalId??void 0,terminalIds:s,createdAt:e.createdAt,updatedAt:e.updatedAt,completedAt:e.completedAt??void 0,output:e.output??void 0,filesChanged:JSON.parse(e.filesChanged),attachments:r.length>0?r:void 0}}getDependencies(e){return this.db.prepare("SELECT dependsOnTaskId FROM task_dependencies WHERE taskId = ?").all(e).map(s=>s.dependsOnTaskId)}getTerminalIds(e){return this.db.prepare("SELECT terminalId FROM task_terminals WHERE taskId = ? AND status = 'running'").all(e).map(s=>s.terminalId)}getAllTerminalIds(e){return this.db.prepare("SELECT terminalId FROM task_terminals WHERE taskId = ?").all(e).map(s=>s.terminalId)}addTerminal(e,t,s,r,n){let o=new Date().toISOString();this.db.prepare(`INSERT INTO task_terminals (taskId, terminalId, cli, prompt, autoApprove, status, startedAt)
81
- VALUES (?, ?, ?, ?, ?, 'running', ?)`).run(e,t,s,r,n?1:0,o)}getTaskTerminals(e){return this.db.prepare("SELECT * FROM task_terminals WHERE taskId = ?").all(e).map(s=>({taskId:s.taskId,terminalId:s.terminalId,cli:s.cli,prompt:s.prompt,autoApprove:s.autoApprove===1,status:s.status,exitCode:s.exitCode??void 0,startedAt:s.startedAt,exitedAt:s.exitedAt??void 0}))}getTaskByTerminalId(e){let t=this.db.prepare("SELECT taskId FROM task_terminals WHERE terminalId = ?").get(e);return t?this.getById(t.taskId):null}markTerminalExited(e,t,s){let r=new Date().toISOString(),n=s===0?"exited":"error";this.db.prepare("UPDATE task_terminals SET status = ?, exitCode = ?, exitedAt = ? WHERE taskId = ? AND terminalId = ?").run(n,s,r,e,t);let i=this.db.prepare("SELECT COUNT(*) as cnt FROM task_terminals WHERE taskId = ? AND status = 'running'").get(e).cnt===0,c=!1;return i&&(c=this.db.prepare("SELECT COUNT(*) as cnt FROM task_terminals WHERE taskId = ? AND status = 'error'").get(e).cnt>0),{allDone:i,hasError:c}}removeTaskTerminals(e){this.db.prepare("DELETE FROM task_terminals WHERE taskId = ?").run(e)}};import{execSync as ct}from"node:child_process";var Ut={id:"claude",name:"Claude Code",command:"claude",buildCommand({mission:a,autoApprove:e,claudeMdPath:t}){let s=a.replace(/'/g,"'\\''"),r=["claude"];return e&&r.push("--dangerously-skip-permissions"),t&&r.push(`--project ${t}`),r.push(`'${s}'`),r.join(" ")}};var _t={id:"codex",name:"Codex",command:"codex",buildCommand({mission:a,autoApprove:e}){let t=a.replace(/'/g,"'\\''"),s=["codex"];return e&&s.push("--full-auto"),s.push(`'${t}'`),s.join(" ")}};var Ft={id:"gemini",name:"Gemini",command:"gemini",buildCommand({mission:a}){return`gemini '${a.replace(/'/g,"'\\''")}'`}};var Wt={id:"opencode",name:"OpenCode",command:"opencode",buildCommand(){return"opencode"}};var Bt={id:"cursor",name:"Cursor",command:"cursor",buildCommand(){return"cursor"}};var Ae=class{providers=new Map;constructor(){this.register(Ut),this.register(_t),this.register(Ft),this.register(Wt),this.register(Bt)}register(e){this.providers.set(e.id,e)}get(e){return this.providers.get(e)}listRegistered(){return Array.from(this.providers.keys())}async detectInstalled(){let e=[];for(let[t,s]of this.providers)try{let r=ct(`which ${s.command}`,{encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim(),n={cli:t,installed:!0,path:r};t==="claude"&&(n.authenticated=this.checkClaudeAuth(r)),e.push(n)}catch{e.push({cli:t,installed:!1})}return e}checkClaudeAuth(e="claude"){try{let s=ct(`${e} auth status`,{encoding:"utf8",timeout:5e3,stdio:["pipe","pipe","pipe"]}).toLowerCase();return!s.includes("not logged in")&&!s.includes("login required")}catch{return!1}}getClaudeBinaryPath(e){if(e&&e!=="claude")return e;try{return ct("which claude",{encoding:"utf8"}).trim()}catch{return"claude"}}};import{randomUUID as Kt}from"node:crypto";import{mkdirSync as nr,writeFileSync as Le}from"node:fs";import{join as ne}from"node:path";var Ce=[{id:"nextjs",name:"Next.js",category:"frontend"},{id:"react",name:"React",category:"frontend"},{id:"react-native",name:"React Native",category:"frontend"},{id:"expo",name:"Expo",category:"frontend"},{id:"vite",name:"Vite",category:"frontend"},{id:"nodejs",name:"Node.js",category:"backend"},{id:"express",name:"Express",category:"backend"},{id:"fastify",name:"Fastify",category:"backend"},{id:"clerk",name:"Clerk",category:"auth",requiredEnvVars:["CLERK_PUBLISHABLE_KEY","CLERK_SECRET_KEY"]},{id:"better-auth",name:"Better Auth",category:"auth"},{id:"convex",name:"Convex",category:"database",requiredEnvVars:["CONVEX_DEPLOYMENT_URL"]},{id:"supabase",name:"Supabase",category:"database",requiredEnvVars:["SUPABASE_URL","SUPABASE_ANON_KEY"]},{id:"prisma",name:"Prisma",category:"database"},{id:"drizzle",name:"Drizzle",category:"database"},{id:"stripe",name:"Stripe",category:"other",requiredEnvVars:["STRIPE_SECRET_KEY","STRIPE_WEBHOOK_SECRET"]},{id:"vercel",name:"Vercel",category:"deployment",requiredEnvVars:["VERCEL_TOKEN"]},{id:"tailwind",name:"Tailwind CSS",category:"frontend"},{id:"shadcn",name:"shadcn/ui",category:"frontend"}];var re=[{provider:"anthropic",envVar:"ANTHROPIC_API_KEY",label:"Anthropic API Key",required:!1,docsUrl:"https://console.anthropic.com/settings/keys",settingKey:"api_key.anthropic",description:"Optional \u2014 if not set, the system uses the Claude CLI's existing authentication (claude auth login)"},{provider:"openai",envVar:"OPENAI_API_KEY",label:"OpenAI API Key",required:!1,docsUrl:"https://platform.openai.com/api-keys",settingKey:"api_key.openai"},{provider:"google",envVar:"GOOGLE_API_KEY",label:"Google API Key",required:!1,docsUrl:"https://aistudio.google.com/apikey",settingKey:"api_key.google"},{provider:"clerk",envVar:"CLERK_SECRET_KEY",label:"Clerk Secret Key",required:!1,docsUrl:"https://dashboard.clerk.com",settingKey:"api_key.clerk"},{provider:"convex",envVar:"CONVEX_DEPLOYMENT_URL",label:"Convex Deployment URL",required:!1,docsUrl:"https://dashboard.convex.dev",settingKey:"api_key.convex"},{provider:"supabase",envVar:"SUPABASE_ANON_KEY",label:"Supabase Anon Key",required:!1,docsUrl:"https://supabase.com/dashboard",settingKey:"api_key.supabase"},{provider:"stripe",envVar:"STRIPE_SECRET_KEY",label:"Stripe Secret Key",required:!1,docsUrl:"https://dashboard.stripe.com/apikeys",settingKey:"api_key.stripe"},{provider:"vercel",envVar:"VERCEL_TOKEN",label:"Vercel Token",required:!1,docsUrl:"https://vercel.com/account/tokens",settingKey:"api_key.vercel"},{provider:"cloudflare",envVar:"CF_TUNNEL_TOKEN",label:"Cloudflare Tunnel Token",required:!1,docsUrl:"https://dash.cloudflare.com",settingKey:"api_key.cloudflare"}],$t=[{key:"general.theme",value:"system",category:"general",label:"Tema",description:"Aparencia do app (system/dark/light)"},{key:"general.default_cli",value:"claude",category:"general",label:"CLI Padrao",description:"CLI agent padrao para novos swarms"},{key:"general.auto_approve",value:"false",category:"general",label:"Auto-Approve",description:"Pular confirmacoes de permissao por padrao"},{key:"general.language",value:"pt",category:"general",label:"Idioma",description:"Idioma da interface (pt/en)"},{key:"providers.claude_binary_path",value:"claude",category:"providers",label:"Claude CLI Path",description:"Caminho para o binario do Claude CLI (usado para autenticacao via Agent SDK)"},{key:"advanced.server_port",value:"3773",category:"advanced",label:"Porta do Servidor",description:"Porta HTTP/WebSocket do servidor"}];var qt=[{name:"Claude Code",category:"ai_coding",command:"claude",healthCommand:"claude --version",healthSuccessKeywords:["claude"],versionCommand:"claude --version",authCommand:"claude auth status",authSuccessKeywords:["logged in","authenticated","isloggedin"],docsUrl:"https://docs.anthropic.com/en/docs/claude-code",enabled:!0,isBuiltIn:!0},{name:"Codex",category:"ai_coding",command:"codex",healthCommand:"codex --version",healthSuccessKeywords:["codex"],versionCommand:"codex --version",authCommand:"codex login status",authSuccessKeywords:["logged in"],docsUrl:"https://github.com/openai/codex",enabled:!0,isBuiltIn:!0},{name:"Gemini CLI",category:"ai_coding",command:"gemini",healthCommand:"gemini --version 2>&1",healthSuccessKeywords:["0.","1.","2."],versionCommand:"gemini --version",authCommand:"gemini --prompt exit 2>&1",authSuccessKeywords:["cached credentials","signed in with google"],docsUrl:"https://github.com/google-gemini/gemini-cli",enabled:!0,isBuiltIn:!0},{name:"OpenCode",category:"ai_coding",command:"opencode",healthCommand:"opencode --version",healthSuccessKeywords:["opencode"],versionCommand:"opencode --version",enabled:!0,isBuiltIn:!0},{name:"Cursor",category:"ai_coding",command:"cursor",healthCommand:"cursor --version",healthSuccessKeywords:["0.","1.","2.","3."],versionCommand:"cursor --version",docsUrl:"https://docs.cursor.com",enabled:!0,isBuiltIn:!0}];function Ht(a){let e=a.split(".").pop()?.toLowerCase()||"";return{ts:"typescript",tsx:"typescript",js:"javascript",jsx:"javascript",mjs:"javascript",py:"python",html:"html",htm:"html",css:"css",scss:"css",json:"json",md:"markdown",yaml:"yaml",yml:"yaml",rs:"rust",go:"go",sh:"shell",bash:"shell",zsh:"shell",sql:"sql",toml:"toml",xml:"xml",svg:"xml",c:"c",cpp:"cpp",h:"c",hpp:"cpp",java:"java",rb:"ruby",php:"php",swift:"swift",kt:"kotlin",lua:"lua",r:"r",dockerfile:"dockerfile",vue:"vue",svelte:"svelte"}[e]||"text"}var dt=[{id:"cursor",label:"Cursor",command:"cursor"},{id:"vscode",label:"VS Code",command:"code"},{id:"vscode-insiders",label:"VS Code Insiders",command:"code-insiders"},{id:"vscodium",label:"VSCodium",command:"codium"},{id:"zed",label:"Zed",command:"zed"},{id:"windsurf",label:"Windsurf",command:"windsurf"},{id:"fleet",label:"Fleet",command:"fleet"},{id:"sublime",label:"Sublime Text",command:"subl"},{id:"terminal",label:"Terminal",command:null},{id:"finder",label:"Finder",command:null}];var Xt={engine:"Qual motor de PRD voce gostaria de usar? (impactus-native / bmad / aiox / custom)",overview:"Descreva seu projeto em 2-3 frases. O que ele faz? Qual problema resolve?",features:"Liste as funcionalidades principais do seu projeto (uma por linha):",stack:"Selecione as tecnologias que serao usadas no projeto. (Use o seletor de stack no frontend)",screens:"Descreva as telas principais do seu aplicativo (nome e breve descricao de cada uma):",review:"",done:"PRD gerado com sucesso! Os arquivos foram salvos no diretorio .swarm/prd/"};function Y(a,e){return a==="review"&&e?rr(e):Xt[a]}function Me(a){let e=["engine","overview","features","stack","screens","review","done"],t=e.indexOf(a);return t===-1||t===e.length-1?"done":e[t+1]}function rr(a){let e=V(a,"overview"),t=V(a,"features"),s=V(a,"screens"),r=a.stack.map(n=>Ce.find(o=>o.id===n)?.name??n).join(", ");return["--- Resumo do PRD ---","",`**Visao Geral:** ${e||"(nao informado)"}`,"","**Funcionalidades:**",t||"(nao informado)","",`**Stack:** ${r||"(nao selecionado)"}`,"","**Telas:**",s||"(nao informado)","","Tudo certo? Digite 'sim' para gerar o PRD ou descreva o que deseja alterar."].join(`
81
+ VALUES (?, ?, ?, ?, ?, 'running', ?)`).run(e,t,s,r,n?1:0,o)}getTaskTerminals(e){return this.db.prepare("SELECT * FROM task_terminals WHERE taskId = ?").all(e).map(s=>({taskId:s.taskId,terminalId:s.terminalId,cli:s.cli,prompt:s.prompt,autoApprove:s.autoApprove===1,status:s.status,exitCode:s.exitCode??void 0,startedAt:s.startedAt,exitedAt:s.exitedAt??void 0}))}getTaskByTerminalId(e){let t=this.db.prepare("SELECT taskId FROM task_terminals WHERE terminalId = ?").get(e);return t?this.getById(t.taskId):null}markTerminalExited(e,t,s){let r=new Date().toISOString(),n=s===0?"exited":"error";this.db.prepare("UPDATE task_terminals SET status = ?, exitCode = ?, exitedAt = ? WHERE taskId = ? AND terminalId = ?").run(n,s,r,e,t);let i=this.db.prepare("SELECT COUNT(*) as cnt FROM task_terminals WHERE taskId = ? AND status = 'running'").get(e).cnt===0,c=!1;return i&&(c=this.db.prepare("SELECT COUNT(*) as cnt FROM task_terminals WHERE taskId = ? AND status = 'error'").get(e).cnt>0),{allDone:i,hasError:c}}removeTaskTerminals(e){this.db.prepare("DELETE FROM task_terminals WHERE taskId = ?").run(e)}};import{execSync as ct}from"node:child_process";var Ut={id:"claude",name:"Claude Code",command:"claude",buildCommand({mission:a,autoApprove:e,claudeMdPath:t}){let s=a.replace(/'/g,"'\\''"),r=["claude"];return e&&r.push("--dangerously-skip-permissions"),t&&r.push(`--project ${t}`),r.push(`'${s}'`),r.join(" ")}};var _t={id:"codex",name:"Codex",command:"codex",buildCommand({mission:a,autoApprove:e}){let t=a.replace(/'/g,"'\\''"),s=["codex"];return e&&s.push("--full-auto"),s.push(`'${t}'`),s.join(" ")}};var Ft={id:"gemini",name:"Gemini",command:"gemini",buildCommand({mission:a}){return`gemini '${a.replace(/'/g,"'\\''")}'`}};var Wt={id:"opencode",name:"OpenCode",command:"opencode",buildCommand(){return"opencode"}};var Bt={id:"cursor",name:"Cursor",command:"cursor",buildCommand(){return"cursor"}};var Ae=class{providers=new Map;constructor(){this.register(Ut),this.register(_t),this.register(Ft),this.register(Wt),this.register(Bt)}register(e){this.providers.set(e.id,e)}get(e){return this.providers.get(e)}listRegistered(){return Array.from(this.providers.keys())}async detectInstalled(){let e=[];for(let[t,s]of this.providers)try{let r=ct(`which ${s.command}`,{encoding:"utf8",stdio:["pipe","pipe","pipe"]}).trim(),n={cli:t,installed:!0,path:r};t==="claude"&&(n.authenticated=this.checkClaudeAuth(r)),e.push(n)}catch{e.push({cli:t,installed:!1})}return e}checkClaudeAuth(e="claude"){try{let s=ct(`${e} auth status`,{encoding:"utf8",timeout:5e3,stdio:["pipe","pipe","pipe"]}).toLowerCase();return!s.includes("not logged in")&&!s.includes("login required")}catch{return!1}}getClaudeBinaryPath(e){if(e&&e!=="claude")return e;try{return ct("which claude",{encoding:"utf8"}).trim()}catch{return"claude"}}};import{randomUUID as Kt}from"node:crypto";import{mkdirSync as nr,writeFileSync as Le}from"node:fs";import{join as ne}from"node:path";var Ce=[{id:"nextjs",name:"Next.js",category:"frontend"},{id:"react",name:"React",category:"frontend"},{id:"react-native",name:"React Native",category:"frontend"},{id:"expo",name:"Expo",category:"frontend"},{id:"vite",name:"Vite",category:"frontend"},{id:"nodejs",name:"Node.js",category:"backend"},{id:"express",name:"Express",category:"backend"},{id:"fastify",name:"Fastify",category:"backend"},{id:"clerk",name:"Clerk",category:"auth",requiredEnvVars:["CLERK_PUBLISHABLE_KEY","CLERK_SECRET_KEY"]},{id:"better-auth",name:"Better Auth",category:"auth"},{id:"convex",name:"Convex",category:"database",requiredEnvVars:["CONVEX_DEPLOYMENT_URL"]},{id:"supabase",name:"Supabase",category:"database",requiredEnvVars:["SUPABASE_URL","SUPABASE_ANON_KEY"]},{id:"prisma",name:"Prisma",category:"database"},{id:"drizzle",name:"Drizzle",category:"database"},{id:"stripe",name:"Stripe",category:"other",requiredEnvVars:["STRIPE_SECRET_KEY","STRIPE_WEBHOOK_SECRET"]},{id:"vercel",name:"Vercel",category:"deployment",requiredEnvVars:["VERCEL_TOKEN"]},{id:"tailwind",name:"Tailwind CSS",category:"frontend"},{id:"shadcn",name:"shadcn/ui",category:"frontend"}];var re=[{provider:"anthropic",envVar:"ANTHROPIC_API_KEY",label:"Anthropic API Key",required:!1,docsUrl:"https://console.anthropic.com/settings/keys",settingKey:"api_key.anthropic",description:"Optional \u2014 if not set, the system uses the Claude CLI's existing authentication (claude auth login)"},{provider:"openai",envVar:"OPENAI_API_KEY",label:"OpenAI API Key",required:!1,docsUrl:"https://platform.openai.com/api-keys",settingKey:"api_key.openai"},{provider:"google",envVar:"GOOGLE_API_KEY",label:"Google API Key",required:!1,docsUrl:"https://aistudio.google.com/apikey",settingKey:"api_key.google"},{provider:"clerk",envVar:"CLERK_SECRET_KEY",label:"Clerk Secret Key",required:!1,docsUrl:"https://dashboard.clerk.com",settingKey:"api_key.clerk"},{provider:"convex",envVar:"CONVEX_DEPLOYMENT_URL",label:"Convex Deployment URL",required:!1,docsUrl:"https://dashboard.convex.dev",settingKey:"api_key.convex"},{provider:"supabase",envVar:"SUPABASE_ANON_KEY",label:"Supabase Anon Key",required:!1,docsUrl:"https://supabase.com/dashboard",settingKey:"api_key.supabase"},{provider:"stripe",envVar:"STRIPE_SECRET_KEY",label:"Stripe Secret Key",required:!1,docsUrl:"https://dashboard.stripe.com/apikeys",settingKey:"api_key.stripe"},{provider:"vercel",envVar:"VERCEL_TOKEN",label:"Vercel Token",required:!1,docsUrl:"https://vercel.com/account/tokens",settingKey:"api_key.vercel"},{provider:"cloudflare",envVar:"CF_TUNNEL_TOKEN",label:"Cloudflare Tunnel Token",required:!1,docsUrl:"https://dash.cloudflare.com",settingKey:"api_key.cloudflare"}],$t=[{key:"general.theme",value:"system",category:"general",label:"Tema",description:"Aparencia do app (system/dark/light)"},{key:"general.default_cli",value:"claude",category:"general",label:"CLI Padrao",description:"CLI agent padrao para novos swarms"},{key:"general.auto_approve",value:"false",category:"general",label:"Auto-Approve",description:"Pular confirmacoes de permissao por padrao"},{key:"general.language",value:"pt",category:"general",label:"Idioma",description:"Idioma da interface (pt/en)"},{key:"providers.claude_binary_path",value:"claude",category:"providers",label:"Claude CLI Path",description:"Caminho para o binario do Claude CLI (usado para autenticacao via Agent SDK)"},{key:"advanced.server_port",value:"7773",category:"advanced",label:"Porta do Servidor",description:"Porta HTTP/WebSocket do servidor"}];var qt=[{name:"Claude Code",category:"ai_coding",command:"claude",healthCommand:"claude --version",healthSuccessKeywords:["claude"],versionCommand:"claude --version",authCommand:"claude auth status",authSuccessKeywords:["logged in","authenticated","isloggedin"],docsUrl:"https://docs.anthropic.com/en/docs/claude-code",enabled:!0,isBuiltIn:!0},{name:"Codex",category:"ai_coding",command:"codex",healthCommand:"codex --version",healthSuccessKeywords:["codex"],versionCommand:"codex --version",authCommand:"codex login status",authSuccessKeywords:["logged in"],docsUrl:"https://github.com/openai/codex",enabled:!0,isBuiltIn:!0},{name:"Gemini CLI",category:"ai_coding",command:"gemini",healthCommand:"gemini --version 2>&1",healthSuccessKeywords:["0.","1.","2."],versionCommand:"gemini --version",authCommand:"gemini --prompt exit 2>&1",authSuccessKeywords:["cached credentials","signed in with google"],docsUrl:"https://github.com/google-gemini/gemini-cli",enabled:!0,isBuiltIn:!0},{name:"OpenCode",category:"ai_coding",command:"opencode",healthCommand:"opencode --version",healthSuccessKeywords:["opencode"],versionCommand:"opencode --version",enabled:!0,isBuiltIn:!0},{name:"Cursor",category:"ai_coding",command:"cursor",healthCommand:"cursor --version",healthSuccessKeywords:["0.","1.","2.","3."],versionCommand:"cursor --version",docsUrl:"https://docs.cursor.com",enabled:!0,isBuiltIn:!0}];function Ht(a){let e=a.split(".").pop()?.toLowerCase()||"";return{ts:"typescript",tsx:"typescript",js:"javascript",jsx:"javascript",mjs:"javascript",py:"python",html:"html",htm:"html",css:"css",scss:"css",json:"json",md:"markdown",yaml:"yaml",yml:"yaml",rs:"rust",go:"go",sh:"shell",bash:"shell",zsh:"shell",sql:"sql",toml:"toml",xml:"xml",svg:"xml",c:"c",cpp:"cpp",h:"c",hpp:"cpp",java:"java",rb:"ruby",php:"php",swift:"swift",kt:"kotlin",lua:"lua",r:"r",dockerfile:"dockerfile",vue:"vue",svelte:"svelte"}[e]||"text"}var dt=[{id:"cursor",label:"Cursor",command:"cursor"},{id:"vscode",label:"VS Code",command:"code"},{id:"vscode-insiders",label:"VS Code Insiders",command:"code-insiders"},{id:"vscodium",label:"VSCodium",command:"codium"},{id:"zed",label:"Zed",command:"zed"},{id:"windsurf",label:"Windsurf",command:"windsurf"},{id:"fleet",label:"Fleet",command:"fleet"},{id:"sublime",label:"Sublime Text",command:"subl"},{id:"terminal",label:"Terminal",command:null},{id:"finder",label:"Finder",command:null}];var Xt={engine:"Qual motor de PRD voce gostaria de usar? (impactus-native / bmad / aiox / custom)",overview:"Descreva seu projeto em 2-3 frases. O que ele faz? Qual problema resolve?",features:"Liste as funcionalidades principais do seu projeto (uma por linha):",stack:"Selecione as tecnologias que serao usadas no projeto. (Use o seletor de stack no frontend)",screens:"Descreva as telas principais do seu aplicativo (nome e breve descricao de cada uma):",review:"",done:"PRD gerado com sucesso! Os arquivos foram salvos no diretorio .swarm/prd/"};function Y(a,e){return a==="review"&&e?rr(e):Xt[a]}function Me(a){let e=["engine","overview","features","stack","screens","review","done"],t=e.indexOf(a);return t===-1||t===e.length-1?"done":e[t+1]}function rr(a){let e=V(a,"overview"),t=V(a,"features"),s=V(a,"screens"),r=a.stack.map(n=>Ce.find(o=>o.id===n)?.name??n).join(", ");return["--- Resumo do PRD ---","",`**Visao Geral:** ${e||"(nao informado)"}`,"","**Funcionalidades:**",t||"(nao informado)","",`**Stack:** ${r||"(nao selecionado)"}`,"","**Telas:**",s||"(nao informado)","","Tudo certo? Digite 'sim' para gerar o PRD ou descreva o que deseja alterar."].join(`
82
82
  `)}function V(a,e){let s=["engine","overview","features","stack","screens","review","done"].indexOf(e),r=Xt[e],n=!1;for(let o of a.conversation){if(o.role==="system"&&o.content===r){n=!0;continue}if(n&&o.role==="user")return o.content}}function jt(a){let e=V(a,"overview")??"",t=V(a,"features")??"",s=V(a,"screens")??"",r=a.stack.map(g=>Ce.find(h=>h.id===g)?.name??g),n=a.stack.map(g=>Ce.find(h=>h.id===g)).filter(Boolean),o=["# PRD - Product Requirements Document","","## Visao Geral","",e,"","## Funcionalidades","",...t.split(`
83
83
  `).map(g=>{let h=g.trim();return h?h.startsWith("-")?h:`- ${h}`:""}),"","## Stack Tecnologica","",...r.map(g=>`- ${g}`),"","## Telas","",s,"","---",`Gerado em ${new Date().toISOString()}`].join(`
84
84
  `),i=n.filter(g=>g.category==="frontend").map(g=>g.name),c=n.filter(g=>g.category==="backend").map(g=>g.name),d=n.filter(g=>g.category==="database").map(g=>g.name),l=n.filter(g=>g.category==="auth").map(g=>g.name),u=n.filter(g=>g.category==="deployment").map(g=>g.name),p=["# Arquitetura","","## Frontend",i.length>0?i.map(g=>`- ${g}`).join(`
@@ -109,7 +109,7 @@ You can document your own discoveries for teammates to benefit from.`),r.push(""
109
109
  enabled, isBuiltIn, createdAt, updatedAt)
110
110
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?)`).run(s,e.name,e.logo??null,e.category,e.command,e.healthCommand,JSON.stringify(e.healthSuccessKeywords??[]),e.versionCommand??null,e.authCommand??null,e.authSuccessKeywords?JSON.stringify(e.authSuccessKeywords):null,e.docsUrl??null,JSON.stringify(e.envVars??{}),e.notes??null,e.enabled!==!1?1:0,t,t),this.get(s)}update(e){let t=this.get(e.id);if(!t)throw new Error(`CLI tool not found: ${e.id}`);let s=new Date().toISOString(),r=[],n=[];return e.name!==void 0&&(r.push("name = ?"),n.push(e.name)),e.logo!==void 0&&(r.push("logo = ?"),n.push(e.logo)),e.category!==void 0&&(r.push("category = ?"),n.push(e.category)),e.command!==void 0&&(r.push("command = ?"),n.push(e.command)),e.healthCommand!==void 0&&(r.push("healthCommand = ?"),n.push(e.healthCommand)),e.healthSuccessKeywords!==void 0&&(r.push("healthSuccessKeywords = ?"),n.push(JSON.stringify(e.healthSuccessKeywords))),e.versionCommand!==void 0&&(r.push("versionCommand = ?"),n.push(e.versionCommand)),e.authCommand!==void 0&&(r.push("authCommand = ?"),n.push(e.authCommand)),e.authSuccessKeywords!==void 0&&(r.push("authSuccessKeywords = ?"),n.push(JSON.stringify(e.authSuccessKeywords))),e.docsUrl!==void 0&&(r.push("docsUrl = ?"),n.push(e.docsUrl)),e.envVars!==void 0&&(r.push("envVars = ?"),n.push(JSON.stringify(e.envVars))),e.notes!==void 0&&(r.push("notes = ?"),n.push(e.notes)),e.enabled!==void 0&&(r.push("enabled = ?"),n.push(e.enabled?1:0)),r.length===0?t:(r.push("updatedAt = ?"),n.push(s),n.push(e.id),this.db.prepare(`UPDATE cli_tools SET ${r.join(", ")} WHERE id = ?`).run(...n),this.get(e.id))}delete(e){let t=this.get(e);if(!t)return!1;if(t.isBuiltIn)throw new Error("Cannot delete built-in CLI tools");return this.db.prepare("DELETE FROM cli_tools WHERE id = ?").run(e),!0}async healthCheck(e){let t=this.get(e);if(!t)throw new Error(`CLI tool not found: ${e}`);let s=new Date().toISOString(),r={id:t.id,name:t.name,installed:!1,healthy:!1,checkedAt:s};try{let n=ut(t.healthCommand,{timeout:1e4,encoding:"utf-8",stdio:"pipe",shell:"/bin/sh"}).toLowerCase();r.installed=!0,t.healthSuccessKeywords.length>0?r.healthy=t.healthSuccessKeywords.some(o=>n.includes(o.toLowerCase())):r.healthy=!0}catch(n){return r.installed=!1,r.healthy=!1,r.error=n instanceof Error?n.message:String(n),r}if(t.versionCommand)try{let n=ut(t.versionCommand,{timeout:5e3,encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim();r.version=n}catch{}if(t.authCommand){let n="";try{n=ut(t.authCommand,{timeout:15e3,encoding:"utf-8",stdio:"pipe",shell:"/bin/sh"}).toLowerCase()}catch(o){if(o&&typeof o=="object"){let i=o;n=((i.stdout??"")+" "+(i.stderr??"")).toLowerCase()}}t.authSuccessKeywords&&t.authSuccessKeywords.length>0?r.authenticated=t.authSuccessKeywords.some(o=>n.includes(o.toLowerCase())):r.authenticated=n.length>0}return r}async healthCheckAll(e){let t=this.list(e).filter(r=>r.enabled),s=[];for(let r of t)s.push(await this.healthCheck(r.id));return s}};import{randomUUID as xe}from"node:crypto";var Ue=class{browser=null;sessions=new Map;broadcast;playwright=null;constructor(e){this.broadcast=e}async ensurePlaywright(){if(this.playwright)return this.playwright;try{return this.playwright=await import("playwright"),this.playwright}catch{throw new Error("Playwright not installed. Run: pnpm add playwright")}}async ensureBrowser(){if(this.browser)return this.browser;let e=await this.ensurePlaywright();return this.browser=await e.chromium.launch({headless:!0}),this.browser}getSession(e){let t=this.sessions.get(e);if(!t)throw new Error(`Browser session ${e} not found`);return t}getTab(e,t){let s=e.tabs.get(t);if(!s)throw new Error(`Browser tab ${t} not found in session ${e.id}`);return s}async getNavigationState(e){try{let t=await e.send("Page.getNavigationHistory");return{canGoBack:t.currentIndex>0,canGoForward:t.currentIndex<t.entries.length-1}}catch{return{canGoBack:!1,canGoForward:!1}}}async startScreencast(e,t,s){await e.send("Page.startScreencast",{format:"jpeg",quality:70,maxWidth:1920,maxHeight:1080,everyNthFrame:1}),e.on("Page.screencastFrame",r=>{this.broadcast("browser.frame",{sessionId:t,tabId:s,data:r.data,frameNumber:r.sessionId,metadata:r.metadata})})}async stopScreencast(e){try{await e.send("Page.stopScreencast")}catch{}}async setupPageEvents(e,t,s){let r=t.id,n=t.tabs.get(s),o=n.cdpSession;await o.send("Runtime.enable"),o.on("Runtime.consoleAPICalled",i=>{let c=i.args?.map(d=>d.type==="string"?d.value:d.type==="number"||d.type==="boolean"?String(d.value):d.type==="undefined"?"undefined":d.value===null?"null":d.preview?.description?d.preview.description:d.description?d.description:d.type)??[];this.broadcast("browser.console",{sessionId:r,tabId:s,type:i.type,args:c,text:c.join(" "),timestamp:Date.now()})}),o.on("Runtime.exceptionThrown",i=>{let c=i.exceptionDetails?.text||i.exceptionDetails?.exception?.description||"Unknown error";this.broadcast("browser.console",{sessionId:r,tabId:s,type:"error",args:[c],text:c,timestamp:Date.now()})}),e.on("framenavigated",async i=>{if(i===e.mainFrame())try{let c=e.url(),d=await e.title().catch(()=>"");n.url=c,n.title=d;let l=await this.getNavigationState(n.cdpSession);this.broadcast("browser.tab.updated",{sessionId:r,tabId:s,url:c,title:d,loading:!0,...l})}catch{}}),e.on("load",async()=>{try{let i=await e.title().catch(()=>"");n.title=i;let c=await this.getNavigationState(n.cdpSession);this.broadcast("browser.tab.updated",{sessionId:r,tabId:s,title:i,loading:!1,...c})}catch{}}),e.on("popup",async i=>{try{let c=xe(),d=await i.context().newCDPSession(i),l=i.url(),u=await i.title().catch(()=>""),p={page:i,cdpSession:d,url:l,title:u};t.tabs.set(c,p),this.setupPageEvents(i,t,c),this.broadcast("browser.tab.created",{sessionId:r,tabId:c,url:l,title:u})}catch{}})}async openSession(e){let s=await(await this.ensureBrowser()).newContext({viewport:{width:1280,height:720}}),r=xe(),n=xe(),o=await s.newPage(),i=await s.newCDPSession(o);e&&await o.goto(e);let c=o.url(),d=await o.title().catch(()=>""),l={page:o,cdpSession:i,url:c,title:d},u={id:r,context:s,tabs:new Map([[n,l]]),activeTabId:n};return this.sessions.set(r,u),await this.setupPageEvents(o,u,n),await this.startScreencast(i,r,n),{sessionId:r,tabId:n}}async closeSession(e){let t=this.getSession(e);for(let[,s]of t.tabs){await this.stopScreencast(s.cdpSession);try{await s.cdpSession.detach()}catch{}}try{await t.context.close()}catch{}this.sessions.delete(e)}async openTab(e,t){let s=this.getSession(e),r=xe(),n=await s.context.newPage(),o=await s.context.newCDPSession(n);t&&await n.goto(t);let i=n.url(),c=await n.title().catch(()=>""),d={page:n,cdpSession:o,url:i,title:c};return s.tabs.set(r,d),await this.setupPageEvents(n,s,r),this.broadcast("browser.tab.created",{sessionId:e,tabId:r,url:i,title:c}),{tabId:r}}async closeTab(e,t){let s=this.getSession(e),r=this.getTab(s,t);s.activeTabId===t&&await this.stopScreencast(r.cdpSession);try{await r.cdpSession.detach()}catch{}try{await r.page.close()}catch{}if(s.tabs.delete(t),this.broadcast("browser.tab.closed",{sessionId:e,tabId:t}),s.activeTabId===t&&s.tabs.size>0){let n=s.tabs.keys().next().value;await this.activateTab(e,n)}}async navigateTab(e,t,s){let r=this.getSession(e),n=this.getTab(r,t),o=s.trim();o&&!o.startsWith("http://")&&!o.startsWith("https://")&&!o.startsWith("about:")&&(o="https://"+o),await n.page.goto(o).catch(i=>{this.broadcast("browser.console",{sessionId:e,tabId:t,type:"error",args:[`Navigation failed: ${i.message}`],text:`Navigation failed: ${i.message}`,timestamp:Date.now()})})}async activateTab(e,t){let s=this.getSession(e),r=this.getTab(s,t);if(s.activeTabId!==t){let o=s.tabs.get(s.activeTabId);o&&await this.stopScreencast(o.cdpSession)}s.activeTabId=t,await this.startScreencast(r.cdpSession,e,t);let n=await this.getNavigationState(r.cdpSession);this.broadcast("browser.tab.updated",{sessionId:e,tabId:t,url:r.url,title:r.title,loading:!1,...n})}async goBack(e,t){let s=this.getSession(e);await this.getTab(s,t).page.goBack()}async goForward(e,t){let s=this.getSession(e);await this.getTab(s,t).page.goForward()}async refresh(e,t){let s=this.getSession(e);await this.getTab(s,t).page.reload()}async sendInput(e,t,s){let r=this.getSession(e),o=this.getTab(r,t).cdpSession,i=["mousePressed","mouseReleased","mouseMoved","mouseWheel"].includes(s.type),c=["keyDown","keyUp","char"].includes(s.type);i?await o.send("Input.dispatchMouseEvent",{type:s.type,x:s.x??0,y:s.y??0,button:s.button||"left",buttons:s.buttons??0,clickCount:s.clickCount||1,deltaX:s.deltaX??0,deltaY:s.deltaY??0,modifiers:s.modifiers||0}):c&&await o.send("Input.dispatchKeyEvent",{type:s.type,key:s.key,code:s.code,text:s.text,windowsVirtualKeyCode:s.windowsVirtualKeyCode||0,nativeVirtualKeyCode:s.windowsVirtualKeyCode||0,modifiers:s.modifiers||0})}async ackFrame(e,t,s){let r=this.getSession(e),n=this.getTab(r,t);try{await n.cdpSession.send("Page.screencastFrameAck",{sessionId:s})}catch{}}async inspectElementAt(e,t,s,r){let n=this.getSession(e),i=this.getTab(n,t).cdpSession;try{await i.send("DOM.enable");let{nodeId:c}=await i.send("DOM.getNodeForLocation",{x:s,y:r,includeUserAgentShadowDOM:!1});if(!c)return{found:!1};let{model:d}=await i.send("DOM.getBoxModel",{nodeId:c}),l=d.border,u={x:l[0],y:l[1],width:l[2]-l[0],height:l[5]-l[1]},{node:p}=await i.send("DOM.describeNode",{nodeId:c}),m=(p.attributes||[]).reduce((w,g,h,T)=>g==="class"&&T[h+1]?T[h+1]:w,""),f=(p.attributes||[]).reduce((w,g,h,T)=>g==="id"&&T[h+1]?T[h+1]:w,"");return{found:!0,boundingBox:u,tagName:p.localName||p.nodeName?.toLowerCase(),className:m,id:f,textContent:(p.nodeValue||"").slice(0,200)}}catch{return{found:!1}}}async highlightElementAt(e,t,s,r,n){let o=this.getSession(e),c=this.getTab(o,t).cdpSession;try{if(!n){await c.send("DOM.hideHighlight");return}await c.send("DOM.enable");let{nodeId:d}=await c.send("DOM.getNodeForLocation",{x:s,y:r,includeUserAgentShadowDOM:!1});if(!d)return;await c.send("DOM.highlightNode",{nodeId:d,highlightConfig:{contentColor:{r:111,g:168,b:220,a:.3},paddingColor:{r:147,g:196,b:125,a:.3},borderColor:{r:255,g:229,b:153,a:.7},marginColor:{r:246,g:178,b:107,a:.3}}})}catch{}}async screenshotRegion(e,t,s){let r=this.getSession(e),o=this.getTab(r,t).cdpSession;try{await o.send("DOM.hideHighlight")}catch{}let i={format:"png"};return s&&(i.clip={x:s.x,y:s.y,width:s.width,height:s.height,scale:1}),{data:(await o.send("Page.captureScreenshot",i)).data,width:s?.width??1280,height:s?.height??720}}async stopAll(){let e=[...this.sessions.keys()];if(await Promise.all(e.map(t=>this.closeSession(t))),this.browser){try{await this.browser.close()}catch{}this.browser=null}}};import{exec as ur}from"node:child_process";import{promisify as pr}from"node:util";var Vt=pr(ur),_e=class{monitors=new Map;portCache=new Map;workspaceTerminals=new Map;broadcast;resolvePid;scanIntervalMs=3e3;constructor(e,t){this.broadcast=e,this.resolvePid=t}async startMonitoring(e,t){this.stopMonitoring(e),this.workspaceTerminals.set(e,t),await this.scan(e);let s=setInterval(()=>{this.scan(e).catch(console.error)},this.scanIntervalMs);this.monitors.set(e,s)}stopMonitoring(e){let t=this.monitors.get(e);t&&(clearInterval(t),this.monitors.delete(e)),this.portCache.delete(e),this.workspaceTerminals.delete(e)}listPorts(e){return this.portCache.get(e)||[]}listAllWorkspacePorts(){let e=new Set,t=[];for(let s of this.portCache.values())for(let r of s){let n=`${r.port}:${r.pid}`;e.has(n)||(e.add(n),t.push(r))}return t.sort((s,r)=>s.port-r.port),t}async listSystemPorts(){return this.detectListeningPorts()}async killPort(e,t){try{process.kill(t,"SIGTERM"),await new Promise(s=>setTimeout(s,2e3));try{process.kill(t,0),process.kill(t,"SIGKILL")}catch{}return{success:!0}}catch(s){return{success:!1,error:s instanceof Error?s.message:String(s)}}}async scan(e){try{let t=this.workspaceTerminals.get(e)||[],s=[];for(let l of t){let u=this.resolvePid(l);u&&u>0&&s.push(u)}if(s.length===0){(this.portCache.get(e)||[]).length>0&&(this.portCache.set(e,[]),this.broadcast("ports.update",{workspaceId:e,ports:[],timestamp:Date.now()}));return}let{allDescendantPids:r,pidToTerminal:n}=await this.buildProcessTree(s,t),i=(await this.detectListeningPorts()).filter(l=>r.has(l.pid));for(let l of i){let u=n.get(l.pid);u&&(l.terminalId=u)}let c=this.portCache.get(e)||[],d=this.portsChanged(c,i);if(this.portCache.set(e,i),d){let l={workspaceId:e,ports:i,timestamp:Date.now()};this.broadcast("ports.update",l)}}catch(t){console.error("[PortMonitor] scan error:",t)}}async buildProcessTree(e,t){let s=new Set(e),r=new Map;try{let{stdout:n}=await Vt("ps -eo ppid=,pid= 2>/dev/null || true",{timeout:3e3}),o=new Map;for(let i of n.trim().split(`
111
111
  `)){let c=i.trim().split(/\s+/);if(c.length<2)continue;let d=parseInt(c[0],10),l=parseInt(c[1],10);if(isNaN(d)||isNaN(l))continue;let u=o.get(d);u||(u=[],o.set(d,u)),u.push(l)}for(let i of t){let c=this.resolvePid(i);if(!c||c<=0)continue;let d=[c];for(r.set(c,i);d.length>0;){let l=d.pop(),u=o.get(l);if(u)for(let p of u)s.has(p)||(s.add(p),r.set(p,i),d.push(p))}}}catch{}return{allDescendantPids:s,pidToTerminal:r}}async detectListeningPorts(){let e=process.platform;try{if(e==="darwin"||e==="linux"){let{stdout:t}=await Vt("lsof -i -P -n -sTCP:LISTEN 2>/dev/null || true",{timeout:5e3});return this.parseLsofOutput(t)}return[]}catch{return[]}}parseLsofOutput(e){let t=e.trim().split(`
112
- `);if(t.length<2)return[];let s=[],r=new Set;for(let n=1;n<t.length;n++){let o=t[n].split(/\s+/);if(o.length<9)continue;let i=o[0],c=parseInt(o[1],10),d=o[8];if(i==="launchd"||i==="systemd")continue;let l=d.lastIndexOf(":");if(l===-1)continue;let u=d.substring(0,l),p=parseInt(d.substring(l+1),10);if(isNaN(p)||p<=0)continue;let m=`${p}:${c}`;if(r.has(m))continue;r.add(m);let f=u==="*"?"0.0.0.0":u;s.push({port:p,protocol:"tcp",pid:c,process:i,localAddress:f,detectedAt:Date.now()})}return s.sort((n,o)=>n.port-o.port),s}portsChanged(e,t){if(e.length!==t.length)return!0;let s=new Set(e.map(r=>`${r.port}:${r.pid}`));return t.some(r=>!s.has(`${r.port}:${r.pid}`))}dispose(){for(let[e]of this.monitors)this.stopMonitoring(e)}};import{spawn as mr}from"node:child_process";import{exec as gr}from"node:child_process";import{promisify as hr}from"node:util";import{randomBytes as fr}from"node:crypto";var wr=hr(gr),Tr=/https:\/\/[a-zA-Z0-9-]+\.trycloudflare\.com/,Fe=class{process=null;url=null;active=!1;startedAt=null;lastError=null;authToken=null;broadcast;constructor(e){this.broadcast=e}async isCloudflaredInstalled(){try{return await wr("which cloudflared"),!0}catch{return!1}}async startTunnel(e){if(this.active)return;if(!await this.isCloudflaredInstalled()){let o="cloudflared is not installed. Install it from https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/";throw this.lastError=o,this.broadcast("tunnel.status",{active:!1,error:o}),new Error(o)}let s=e?.port??3773,r=["tunnel","--url",`${e?.protocol??"http"}://localhost:${s}`];e?.hostname&&r.push("--hostname",e.hostname),this.process=mr("cloudflared",r,{stdio:["ignore","pipe","pipe"]}),this.active=!0,this.startedAt=new Date().toISOString(),this.lastError=null,this.authToken=fr(32).toString("hex");let n=o=>{let c=o.toString().match(Tr);c&&!this.url&&(this.url=c[0],this.broadcast("tunnel.url",{url:this.url}),this.broadcast("tunnel.status",{active:!0,url:this.url}))};this.process.stdout?.on("data",n),this.process.stderr?.on("data",n),this.process.on("exit",o=>{let i=this.active;if(this.active=!1,this.url=null,this.startedAt=null,this.process=null,i){let c=o!==0?`cloudflared exited with code ${o}`:void 0;c&&(this.lastError=c),this.broadcast("tunnel.status",{active:!1,error:c})}}),this.process.on("error",o=>{this.active=!1,this.url=null,this.startedAt=null,this.process=null,this.lastError=o.message,this.broadcast("tunnel.status",{active:!1,error:o.message})}),this.broadcast("tunnel.status",{active:!0})}stopTunnel(){this.process&&(this.process.kill(),this.active=!1,this.url=null,this.startedAt=null,this.process=null,this.authToken=null,this.broadcast("tunnel.status",{active:!1}))}async getStatus(){let e=await this.isCloudflaredInstalled();return{active:this.active,url:this.url??void 0,pid:this.process?.pid,startedAt:this.startedAt??void 0,error:this.lastError??void 0,isInstalled:e,token:this.authToken??void 0}}getUrl(){return this.url}getToken(){return this.authToken}isActive(){return this.active}validateToken(e){return!this.active||!this.authToken?!0:e===this.authToken}};import{spawn as Er}from"node:child_process";import{exec as yr}from"node:child_process";import{promisify as Sr}from"node:util";var Yt=Sr(yr),vr=/https:\/\/[a-zA-Z0-9-]+\.trycloudflare\.com/,Jt=10,We=class{tunnels=new Map;broadcast;constructor(e){this.broadcast=e}async startTunnel(e,t){if(this.tunnels.has(e))return;if(this.tunnels.size>=Jt)throw new Error(`Maximum of ${Jt} port tunnels reached`);try{await Yt("which cloudflared")}catch{throw new Error("cloudflared is not installed. Install it from https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/")}let s=`${t??"http"}://localhost:${e}`,r=Er("cloudflared",["tunnel","--url",s],{stdio:["ignore","pipe","pipe"]}),n={process:r,url:null,active:!0,startedAt:new Date().toISOString(),error:null};this.tunnels.set(e,n),this.broadcastUpdate();let o=i=>{let d=i.toString().match(vr);d&&!n.url&&(n.url=d[0],this.broadcastUpdate())};r.stdout?.on("data",o),r.stderr?.on("data",o),r.on("exit",i=>{let c=this.tunnels.get(e);c&&c.process===r&&(i!==0&&(c.error=`cloudflared exited with code ${i}`),c.active=!1,this.tunnels.delete(e),this.broadcastUpdate())}),r.on("error",i=>{let c=this.tunnels.get(e);c&&c.process===r&&(c.error=i.message,c.active=!1,this.tunnels.delete(e),this.broadcastUpdate())})}stopTunnel(e){let t=this.tunnels.get(e);if(t){try{t.process.kill()}catch{}this.tunnels.delete(e),this.broadcastUpdate()}}stopAll(){for(let[,e]of this.tunnels)try{e.process.kill()}catch{}this.tunnels.clear(),this.broadcastUpdate()}async checkInstalled(){try{return await Yt("which cloudflared"),!0}catch{return!1}}getTunnels(){let e=[];for(let[t,s]of this.tunnels)e.push({port:t,url:s.url??void 0,active:s.active,startedAt:s.startedAt,error:s.error??void 0});return e}getTunnelForPort(e){let t=this.tunnels.get(e);if(t)return{port:e,url:t.url??void 0,active:t.active,startedAt:t.startedAt,error:t.error??void 0}}cleanupStaleTunnels(e){let t=new Set(e);for(let s of this.tunnels.keys())t.has(s)||this.stopTunnel(s)}broadcastUpdate(){this.broadcast("portTunnels.update",{tunnels:this.getTunnels()})}};import{readFileSync as kr,writeFileSync as zt,readdirSync as br,unlinkSync as Ir,mkdirSync as Rr,existsSync as Zt,statSync as Ar}from"node:fs";import{join as z,basename as Cr}from"node:path";function Mr(a){return a.toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9\-_]/g,"")}function Be(a){return z(a,".swarm","prd","wireframes")}function $e(a,e){let t=kr(a,"utf-8"),s=Ar(a),r=e.replace(/-/g," ").replace(/\b\w/g,n=>n.toUpperCase());return{id:e,name:r,content:t,updatedAt:s.mtime.toISOString()}}function Lr(a){let e=a.toLowerCase();return/login|signup|register|auth/.test(e)?`\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
112
+ `);if(t.length<2)return[];let s=[],r=new Set;for(let n=1;n<t.length;n++){let o=t[n].split(/\s+/);if(o.length<9)continue;let i=o[0],c=parseInt(o[1],10),d=o[8];if(i==="launchd"||i==="systemd")continue;let l=d.lastIndexOf(":");if(l===-1)continue;let u=d.substring(0,l),p=parseInt(d.substring(l+1),10);if(isNaN(p)||p<=0)continue;let m=`${p}:${c}`;if(r.has(m))continue;r.add(m);let f=u==="*"?"0.0.0.0":u;s.push({port:p,protocol:"tcp",pid:c,process:i,localAddress:f,detectedAt:Date.now()})}return s.sort((n,o)=>n.port-o.port),s}portsChanged(e,t){if(e.length!==t.length)return!0;let s=new Set(e.map(r=>`${r.port}:${r.pid}`));return t.some(r=>!s.has(`${r.port}:${r.pid}`))}dispose(){for(let[e]of this.monitors)this.stopMonitoring(e)}};import{spawn as mr}from"node:child_process";import{exec as gr}from"node:child_process";import{promisify as hr}from"node:util";import{randomBytes as fr}from"node:crypto";var wr=hr(gr),Tr=/https:\/\/[a-zA-Z0-9-]+\.trycloudflare\.com/,Fe=class{process=null;url=null;active=!1;startedAt=null;lastError=null;authToken=null;broadcast;constructor(e){this.broadcast=e}async isCloudflaredInstalled(){try{return await wr("which cloudflared"),!0}catch{return!1}}async startTunnel(e){if(this.active)return;if(!await this.isCloudflaredInstalled()){let o="cloudflared is not installed. Install it from https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/";throw this.lastError=o,this.broadcast("tunnel.status",{active:!1,error:o}),new Error(o)}let s=e?.port??7773,r=["tunnel","--url",`${e?.protocol??"http"}://localhost:${s}`];e?.hostname&&r.push("--hostname",e.hostname),this.process=mr("cloudflared",r,{stdio:["ignore","pipe","pipe"]}),this.active=!0,this.startedAt=new Date().toISOString(),this.lastError=null,this.authToken=fr(32).toString("hex");let n=o=>{let c=o.toString().match(Tr);c&&!this.url&&(this.url=c[0],this.broadcast("tunnel.url",{url:this.url}),this.broadcast("tunnel.status",{active:!0,url:this.url}))};this.process.stdout?.on("data",n),this.process.stderr?.on("data",n),this.process.on("exit",o=>{let i=this.active;if(this.active=!1,this.url=null,this.startedAt=null,this.process=null,i){let c=o!==0?`cloudflared exited with code ${o}`:void 0;c&&(this.lastError=c),this.broadcast("tunnel.status",{active:!1,error:c})}}),this.process.on("error",o=>{this.active=!1,this.url=null,this.startedAt=null,this.process=null,this.lastError=o.message,this.broadcast("tunnel.status",{active:!1,error:o.message})}),this.broadcast("tunnel.status",{active:!0})}stopTunnel(){this.process&&(this.process.kill(),this.active=!1,this.url=null,this.startedAt=null,this.process=null,this.authToken=null,this.broadcast("tunnel.status",{active:!1}))}async getStatus(){let e=await this.isCloudflaredInstalled();return{active:this.active,url:this.url??void 0,pid:this.process?.pid,startedAt:this.startedAt??void 0,error:this.lastError??void 0,isInstalled:e,token:this.authToken??void 0}}getUrl(){return this.url}getToken(){return this.authToken}isActive(){return this.active}validateToken(e){return!this.active||!this.authToken?!0:e===this.authToken}};import{spawn as Er}from"node:child_process";import{exec as yr}from"node:child_process";import{promisify as Sr}from"node:util";var Yt=Sr(yr),vr=/https:\/\/[a-zA-Z0-9-]+\.trycloudflare\.com/,Jt=10,We=class{tunnels=new Map;broadcast;constructor(e){this.broadcast=e}async startTunnel(e,t){if(this.tunnels.has(e))return;if(this.tunnels.size>=Jt)throw new Error(`Maximum of ${Jt} port tunnels reached`);try{await Yt("which cloudflared")}catch{throw new Error("cloudflared is not installed. Install it from https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/")}let s=`${t??"http"}://localhost:${e}`,r=Er("cloudflared",["tunnel","--url",s],{stdio:["ignore","pipe","pipe"]}),n={process:r,url:null,active:!0,startedAt:new Date().toISOString(),error:null};this.tunnels.set(e,n),this.broadcastUpdate();let o=i=>{let d=i.toString().match(vr);d&&!n.url&&(n.url=d[0],this.broadcastUpdate())};r.stdout?.on("data",o),r.stderr?.on("data",o),r.on("exit",i=>{let c=this.tunnels.get(e);c&&c.process===r&&(i!==0&&(c.error=`cloudflared exited with code ${i}`),c.active=!1,this.tunnels.delete(e),this.broadcastUpdate())}),r.on("error",i=>{let c=this.tunnels.get(e);c&&c.process===r&&(c.error=i.message,c.active=!1,this.tunnels.delete(e),this.broadcastUpdate())})}stopTunnel(e){let t=this.tunnels.get(e);if(t){try{t.process.kill()}catch{}this.tunnels.delete(e),this.broadcastUpdate()}}stopAll(){for(let[,e]of this.tunnels)try{e.process.kill()}catch{}this.tunnels.clear(),this.broadcastUpdate()}async checkInstalled(){try{return await Yt("which cloudflared"),!0}catch{return!1}}getTunnels(){let e=[];for(let[t,s]of this.tunnels)e.push({port:t,url:s.url??void 0,active:s.active,startedAt:s.startedAt,error:s.error??void 0});return e}getTunnelForPort(e){let t=this.tunnels.get(e);if(t)return{port:e,url:t.url??void 0,active:t.active,startedAt:t.startedAt,error:t.error??void 0}}cleanupStaleTunnels(e){let t=new Set(e);for(let s of this.tunnels.keys())t.has(s)||this.stopTunnel(s)}broadcastUpdate(){this.broadcast("portTunnels.update",{tunnels:this.getTunnels()})}};import{readFileSync as kr,writeFileSync as zt,readdirSync as br,unlinkSync as Ir,mkdirSync as Rr,existsSync as Zt,statSync as Ar}from"node:fs";import{join as z,basename as Cr}from"node:path";function Mr(a){return a.toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9\-_]/g,"")}function Be(a){return z(a,".swarm","prd","wireframes")}function $e(a,e){let t=kr(a,"utf-8"),s=Ar(a),r=e.replace(/-/g," ").replace(/\b\w/g,n=>n.toUpperCase());return{id:e,name:r,content:t,updatedAt:s.mtime.toISOString()}}function Lr(a){let e=a.toLowerCase();return/login|signup|register|auth/.test(e)?`\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
113
113
  \u2502 LOGIN \u2502
114
114
  \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524
115
115
  \u2502 \u2502
@@ -565,5 +565,5 @@ You can document your own discoveries for teammates to benefit from.`),r.push(""
565
565
  `).all();if(t.length===0)return;let s=new Date().toISOString(),r=a.prepare(`
566
566
  INSERT INTO areas (id, workspaceId, name, color, directory, useWorktree, defaultCommand, terminalCount, layout, "order", isDefault, createdAt, updatedAt)
567
567
  VALUES (?, ?, ?, ?, '', 0, ?, ?, ?, 0, 1, ?, ?)
568
- `),n=a.prepare("INSERT OR IGNORE INTO area_state (areaId) VALUES (?)"),o=a.prepare("UPDATE terminal_configs SET areaId = ? WHERE workspaceId = ? AND (areaId IS NULL OR areaId = '')"),i=a.prepare("UPDATE saved_layouts SET areaId = ? WHERE workspaceId = ? AND (areaId IS NULL OR areaId = '')"),c=a.prepare("UPDATE swarms SET areaId = ? WHERE workspaceId = ? AND (areaId IS NULL OR areaId = '')");for(let d of t){let l=dn();r.run(l,d.id,"Principal",d.color,d.defaultCommand,d.terminalCount,d.layout,s,s),n.run(l),o.run(l,d.id),i.run(l,d.id),c.run(l,d.id)}}import $ from"ws";var pn=3e4,mn=1e4,gn=480*60*1e3,ze=class{ws=null;_code=null;relayUrl;allowedIps;reconnectDelay=1e3;maxReconnectDelay=3e4;shouldReconnect=!0;pingTimer=null;sessionTimer=null;connectedAt=null;onMessage;onCodeChange;constructor(e,t=[]){this.relayUrl=e,this.allowedIps=t}get code(){return this._code}get connected(){return this.ws?.readyState===$.OPEN}getStatus(){return{code:this._code,connected:this.connected,connectedAt:this.connectedAt,relayUrl:this.relayUrl}}connect(){return new Promise((e,t)=>{let s=`${this.relayUrl.replace(/^http/,"ws")}/ws/server`;this.ws=new $(s),this.ws.on("open",()=>{this.reconnectDelay=1e3,this.connectedAt=new Date().toISOString(),this.ws.send(JSON.stringify({type:"register",allowed_ips:this.allowedIps})),this.startPing(),this.startSessionTimer()}),this.ws.on("message",r=>{let n=r.toString();try{let o=JSON.parse(n);if(o.type==="registered"&&o.code){this._code=o.code,this.reconnectDelay=1e3,e(o.code),this.onCodeChange?.(o.code);return}if(o.type==="pong")return}catch{}if(this.onMessage&&this.ws){let o=this.createVirtualWs();this.onMessage(n,o)}}),this.ws.on("pong",()=>{}),this.ws.on("close",()=>{this.handleDisconnect()}),this.ws.on("error",r=>{console.error("[relay] Connection error:",r.message)})})}async reconnect(){if(this.stopPing(),this.stopSessionTimer(),this.ws){this.ws.removeAllListeners();try{this.ws.close()}catch{}this.ws=null}return this._code=null,this.connect()}createVirtualWs(){let e=this;return{readyState:$.OPEN,send(t){e.ws?.readyState===$.OPEN&&e.ws.send(t)}}}send(e){this.ws?.readyState===$.OPEN&&this.ws.send(e)}close(){this.shouldReconnect=!1,this.stopPing(),this.stopSessionTimer(),this.ws?.close()}startPing(){this.stopPing(),this.pingTimer=setInterval(()=>{if(this.ws?.readyState===$.OPEN){try{this.ws.ping()}catch{}try{this.ws.send(JSON.stringify({type:"ping"}))}catch{}let e=setTimeout(()=>{this.ws?.readyState===$.OPEN&&(console.log("[relay] Pong timeout \u2014 forcing reconnect"),this.ws.terminate())},mn);this.ws.once("pong",()=>clearTimeout(e))}},pn)}stopPing(){this.pingTimer&&(clearInterval(this.pingTimer),this.pingTimer=null)}startSessionTimer(){this.stopSessionTimer(),this.sessionTimer=setTimeout(()=>{console.log("[relay] Session expired (8h) \u2014 reconnecting"),this.reconnect().then(e=>console.log(`[relay] Reconnected with new code: ${e}`)).catch(()=>console.error("[relay] Session refresh failed"))},gn)}stopSessionTimer(){this.sessionTimer&&(clearTimeout(this.sessionTimer),this.sessionTimer=null)}handleDisconnect(){this._code=null,this.connectedAt=null,this.stopPing(),this.stopSessionTimer(),this.shouldReconnect&&(console.log(`[relay] Disconnected. Reconnecting in ${this.reconnectDelay}ms...`),setTimeout(async()=>{try{let e=await this.connect();console.log(`[relay] Reconnected. New code: ${e}`)}catch{console.error("[relay] Reconnection failed")}},this.reconnectDelay),this.reconnectDelay=Math.min(this.reconnectDelay*2,this.maxReconnectDelay))}};function Is(){return process.env.PORT?parseInt(process.env.PORT):process.env.ELECTRON_RUN_AS_NODE?process.platform==="win32"?"\\\\.\\pipe\\impactus-swarm":"/tmp/impactus-swarm.sock":3773}var wn={".html":"text/html",".js":"application/javascript",".css":"text/css",".json":"application/json",".png":"image/png",".svg":"image/svg+xml",".ico":"image/x-icon"},ie=Is(),Cs=process.env.IMPACTUS_WEB_DIR||St(import.meta.dirname,"../../web/dist"),vt=hn((a,e)=>{let t=a.url||"/",s=St(Cs,t==="/"?"index.html":t);if(As(s)||(s=St(Cs,"index.html")),!As(s)){e.writeHead(404),e.end("Not found");return}if(s.endsWith("index.html")){try{let o=Rs(s,"utf-8"),i=JSON.stringify({relayUrl:process.env.RELAY_URL||null}),c=o.replace("</head>",`<script>window.__IMPACTUS_CONFIG__=${i}</script>
568
+ `),n=a.prepare("INSERT OR IGNORE INTO area_state (areaId) VALUES (?)"),o=a.prepare("UPDATE terminal_configs SET areaId = ? WHERE workspaceId = ? AND (areaId IS NULL OR areaId = '')"),i=a.prepare("UPDATE saved_layouts SET areaId = ? WHERE workspaceId = ? AND (areaId IS NULL OR areaId = '')"),c=a.prepare("UPDATE swarms SET areaId = ? WHERE workspaceId = ? AND (areaId IS NULL OR areaId = '')");for(let d of t){let l=dn();r.run(l,d.id,"Principal",d.color,d.defaultCommand,d.terminalCount,d.layout,s,s),n.run(l),o.run(l,d.id),i.run(l,d.id),c.run(l,d.id)}}import $ from"ws";var pn=3e4,mn=1e4,gn=480*60*1e3,ze=class{ws=null;_code=null;relayUrl;allowedIps;reconnectDelay=1e3;maxReconnectDelay=3e4;shouldReconnect=!0;pingTimer=null;sessionTimer=null;connectedAt=null;onMessage;onCodeChange;constructor(e,t=[]){this.relayUrl=e,this.allowedIps=t}get code(){return this._code}get connected(){return this.ws?.readyState===$.OPEN}getStatus(){return{code:this._code,connected:this.connected,connectedAt:this.connectedAt,relayUrl:this.relayUrl}}connect(){return new Promise((e,t)=>{let s=`${this.relayUrl.replace(/^http/,"ws")}/ws/server`;this.ws=new $(s),this.ws.on("open",()=>{this.reconnectDelay=1e3,this.connectedAt=new Date().toISOString(),this.ws.send(JSON.stringify({type:"register",allowed_ips:this.allowedIps})),this.startPing(),this.startSessionTimer()}),this.ws.on("message",r=>{let n=r.toString();try{let o=JSON.parse(n);if(o.type==="registered"&&o.code){this._code=o.code,this.reconnectDelay=1e3,e(o.code),this.onCodeChange?.(o.code);return}if(o.type==="pong")return}catch{}if(this.onMessage&&this.ws){let o=this.createVirtualWs();this.onMessage(n,o)}}),this.ws.on("pong",()=>{}),this.ws.on("close",()=>{this.handleDisconnect()}),this.ws.on("error",r=>{console.error("[relay] Connection error:",r.message)})})}async reconnect(){if(this.stopPing(),this.stopSessionTimer(),this.ws){this.ws.removeAllListeners();try{this.ws.close()}catch{}this.ws=null}return this._code=null,this.connect()}createVirtualWs(){let e=this;return{readyState:$.OPEN,send(t){e.ws?.readyState===$.OPEN&&e.ws.send(t)}}}send(e){this.ws?.readyState===$.OPEN&&this.ws.send(e)}close(){this.shouldReconnect=!1,this.stopPing(),this.stopSessionTimer(),this.ws?.close()}startPing(){this.stopPing(),this.pingTimer=setInterval(()=>{if(this.ws?.readyState===$.OPEN){try{this.ws.ping()}catch{}try{this.ws.send(JSON.stringify({type:"ping"}))}catch{}let e=setTimeout(()=>{this.ws?.readyState===$.OPEN&&(console.log("[relay] Pong timeout \u2014 forcing reconnect"),this.ws.terminate())},mn);this.ws.once("pong",()=>clearTimeout(e))}},pn)}stopPing(){this.pingTimer&&(clearInterval(this.pingTimer),this.pingTimer=null)}startSessionTimer(){this.stopSessionTimer(),this.sessionTimer=setTimeout(()=>{console.log("[relay] Session expired (8h) \u2014 reconnecting"),this.reconnect().then(e=>console.log(`[relay] Reconnected with new code: ${e}`)).catch(()=>console.error("[relay] Session refresh failed"))},gn)}stopSessionTimer(){this.sessionTimer&&(clearTimeout(this.sessionTimer),this.sessionTimer=null)}handleDisconnect(){this._code=null,this.connectedAt=null,this.stopPing(),this.stopSessionTimer(),this.shouldReconnect&&(console.log(`[relay] Disconnected. Reconnecting in ${this.reconnectDelay}ms...`),setTimeout(async()=>{try{let e=await this.connect();console.log(`[relay] Reconnected. New code: ${e}`)}catch{console.error("[relay] Reconnection failed")}},this.reconnectDelay),this.reconnectDelay=Math.min(this.reconnectDelay*2,this.maxReconnectDelay))}};function Is(){return process.env.PORT?parseInt(process.env.PORT):process.env.ELECTRON_RUN_AS_NODE?process.platform==="win32"?"\\\\.\\pipe\\impactus-swarm":"/tmp/impactus-swarm.sock":7773}var wn={".html":"text/html",".js":"application/javascript",".css":"text/css",".json":"application/json",".png":"image/png",".svg":"image/svg+xml",".ico":"image/x-icon"},ie=Is(),Cs=process.env.IMPACTUS_WEB_DIR||St(import.meta.dirname,"../../web/dist"),vt=hn((a,e)=>{let t=a.url||"/",s=St(Cs,t==="/"?"index.html":t);if(As(s)||(s=St(Cs,"index.html")),!As(s)){e.writeHead(404),e.end("Not found");return}if(s.endsWith("index.html")){try{let o=Rs(s,"utf-8"),i=JSON.stringify({relayUrl:process.env.RELAY_URL||null}),c=o.replace("</head>",`<script>window.__IMPACTUS_CONFIG__=${i}</script>
569
569
  </head>`);e.writeHead(200,{"Content-Type":"text/html"}),e.end(c)}catch{e.writeHead(500),e.end("Internal server error")}return}let r=fn(s),n=wn[r]||"application/octet-stream";try{let o=Rs(s);e.writeHead(200,{"Content-Type":n}),e.end(o)}catch{e.writeHead(500),e.end("Internal server error")}}),Ns=bs(),{terminalManager:Tn,broadcast:En,externalSenders:yn,services:Ms,routes:Sn}=vs(vt,Ns),Ls=process.env.RELAY_URL,vn=process.env.RELAY_ALLOWED_IPS?process.env.RELAY_ALLOWED_IPS.split(",").map(a=>a.trim()):[],q=null;Ls&&(q=new ze(Ls,vn),Ms.relayClient=q,yn.push(a=>q.send(a)),q.onMessage=(a,e)=>{let t;try{t=JSON.parse(a)}catch{return}Et(t,e,Ms,Sn)},q.onCodeChange=a=>{En("relay.codeChange",{code:a})},q.connect().then(a=>console.log(`[relay] Code: ${a}`)).catch(a=>console.error("[relay] Failed to connect:",a)));function Os(){q?.close(),Tn.closeAll(),Ns.close(),process.exit(0)}process.on("SIGINT",Os);process.on("SIGTERM",Os);var kn=process.argv.includes("--open");typeof ie=="number"?vt.listen(ie,async()=>{let a=`http://localhost:${ie}`;console.log(`Impactus Swarm server running on ${a}`),kn&&(await import("open")).default(a)}):vt.listen(ie,()=>{console.log(`Impactus Swarm server running on ${ie}`)});