sootsim 0.0.4 → 0.1.37

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.
Files changed (165) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +24 -9
  3. package/dist-cli/bin.js +15 -20
  4. package/dist-cli/chunks/{agent-PJAOF4JS.js → agent-EQRQGSBL.js} +4 -4
  5. package/dist-cli/chunks/agent-wrapper-AWKZ67GN.js +15 -0
  6. package/dist-cli/chunks/{assert-P47NW4AF.js → assert-ZVGELUZB.js} +2 -2
  7. package/dist-cli/chunks/auto-bootstrap-UEOLNAWJ.js +2 -0
  8. package/dist-cli/chunks/beta-4MD7WSI4.js +2 -0
  9. package/dist-cli/chunks/chunk-2ZPJHSIJ.js +11 -0
  10. package/dist-cli/chunks/chunk-4IO3D5XG.js +2 -0
  11. package/dist-cli/chunks/chunk-4OHVCGMF.js +2 -0
  12. package/dist-cli/chunks/chunk-56BIMCDH.js +2 -0
  13. package/dist-cli/chunks/chunk-5FLDI6CV.js +66 -0
  14. package/dist-cli/chunks/{chunk-WWDJCKMI.js → chunk-B3RAGRK6.js} +1 -1
  15. package/dist-cli/chunks/chunk-BGAPLYMS.js +61 -0
  16. package/dist-cli/chunks/chunk-CX3ZIPD3.js +3 -0
  17. package/dist-cli/chunks/{chunk-I6XGFZPA.js → chunk-DSTV2VJT.js} +2 -2
  18. package/dist-cli/chunks/chunk-EDBFYOQB.js +2 -0
  19. package/dist-cli/chunks/chunk-ERLA3F77.js +1 -0
  20. package/dist-cli/chunks/chunk-FCQLQ7NA.js +117 -0
  21. package/dist-cli/chunks/chunk-H2HSOHXN.js +7 -0
  22. package/dist-cli/chunks/chunk-HYYMBXIX.js +2 -0
  23. package/dist-cli/chunks/chunk-JMGDVXAV.js +3 -0
  24. package/dist-cli/chunks/chunk-JMU5IGIU.js +1 -0
  25. package/dist-cli/chunks/chunk-KA5JJCWL.js +1 -0
  26. package/dist-cli/chunks/chunk-L4F4JRKJ.js +348 -0
  27. package/dist-cli/chunks/chunk-LDWXH43L.js +4 -0
  28. package/dist-cli/chunks/chunk-PERKPZ7T.js +4 -0
  29. package/dist-cli/chunks/chunk-PN6FWLD4.js +5 -0
  30. package/dist-cli/chunks/chunk-QD7YIVPS.js +64 -0
  31. package/dist-cli/chunks/chunk-QWKO62QM.js +2 -0
  32. package/dist-cli/chunks/{chunk-6SZMLFCR.js → chunk-QXMZNJV5.js} +1 -1
  33. package/dist-cli/chunks/chunk-R77F5J3X.js +4 -0
  34. package/dist-cli/chunks/chunk-RLNIKWFO.js +27 -0
  35. package/dist-cli/chunks/chunk-RX6RHGSI.js +2 -0
  36. package/dist-cli/chunks/chunk-S74RCIVB.js +2 -0
  37. package/dist-cli/chunks/chunk-SK4SOISL.js +1 -0
  38. package/dist-cli/chunks/{chunk-AFQBSK2J.js → chunk-T5L73GJB.js} +1 -1
  39. package/dist-cli/chunks/{chunk-432TMHBG.js → chunk-UIQ3536J.js} +1 -1
  40. package/dist-cli/chunks/chunk-URSEYCC5.js +16 -0
  41. package/dist-cli/chunks/chunk-WFXYY3DU.js +3 -0
  42. package/dist-cli/chunks/{chunk-DQKQYPIG.js → chunk-WHLHA5R5.js} +4 -4
  43. package/dist-cli/chunks/chunk-WLIVBPPY.js +3 -0
  44. package/dist-cli/chunks/{chunk-UQ3N6FZF.js → chunk-X6BP5JFC.js} +4 -4
  45. package/dist-cli/chunks/chunk-YFXTO4QX.js +5 -0
  46. package/dist-cli/chunks/{chunk-4XBPZQLW.js → chunk-Z5SVSAZO.js} +2 -2
  47. package/dist-cli/chunks/{chunk-5TTQKPGH.js → chunk-Z5X3PITK.js} +3 -3
  48. package/dist-cli/chunks/chunk-ZBOIGEGO.js +5 -0
  49. package/dist-cli/chunks/chunk-ZERYEI3L.js +17 -0
  50. package/dist-cli/chunks/{compat-ILLJ7VDL.js → compat-QQ3OJDBI.js} +2 -2
  51. package/dist-cli/chunks/{config-CDIAJIIT.js → config-LT27SC25.js} +2 -2
  52. package/dist-cli/chunks/control-3BO54QMO.js +2 -0
  53. package/dist-cli/chunks/cpu-profile-XEO3JCVB.js +22 -0
  54. package/dist-cli/chunks/daemon-3J2SAVQZ.js +83 -0
  55. package/dist-cli/chunks/{debug-6SMCTPMC.js → debug-OGQLIH4U.js} +4 -4
  56. package/dist-cli/chunks/demo-app-registry-5RZCXLWB.js +2 -0
  57. package/dist-cli/chunks/detox-Z2OSCIQU.js +49 -0
  58. package/dist-cli/chunks/device-RPTVD25S.js +16 -0
  59. package/dist-cli/chunks/diagnose-LAEXBNOQ.js +41 -0
  60. package/dist-cli/chunks/drivers-PSQUUAYC.js +2 -0
  61. package/dist-cli/chunks/electron-S2463O3P.js +18 -0
  62. package/dist-cli/chunks/flow-34YCVQDB.js +2 -0
  63. package/dist-cli/chunks/hints-E5PXPWFT.js +2 -0
  64. package/dist-cli/chunks/home-paths-F5SGBTRZ.js +2 -0
  65. package/dist-cli/chunks/inspect-EVGMEZ3G.js +1101 -0
  66. package/dist-cli/chunks/install-AM5PTJT3.js +2 -0
  67. package/dist-cli/chunks/install-desktop-ZNWYKTWQ.js +23 -0
  68. package/dist-cli/chunks/{keys-OWQ7SOTM.js → keys-5ETF6DYO.js} +2 -2
  69. package/dist-cli/chunks/{launch-WUEDHSO5.js → launch-DHUCNFX6.js} +3 -3
  70. package/dist-cli/chunks/login-KDR34JIP.js +26 -0
  71. package/dist-cli/chunks/logout-R6WIJYCW.js +2 -0
  72. package/dist-cli/chunks/maestro-ZOOJ2YVH.js +80 -0
  73. package/dist-cli/chunks/{preview-4RVHA2PP.js → preview-YFADHNBD.js} +2 -2
  74. package/dist-cli/chunks/profile-CQSC32HB.js +22 -0
  75. package/dist-cli/chunks/react-QSQD6CJE.js +30 -0
  76. package/dist-cli/chunks/{record-KEWLM5JR.js → record-IWLEYATN.js} +5 -5
  77. package/dist-cli/chunks/runtime-WKMNKYTN.js +25 -0
  78. package/dist-cli/chunks/screenshot-VJXHV57I.js +28 -0
  79. package/dist-cli/chunks/screenshot-mode-FA4VQ76K.js +17 -0
  80. package/dist-cli/chunks/screenshots-U4FQXHVK.js +70 -0
  81. package/dist-cli/chunks/server-7WZLM5NQ.js +35 -0
  82. package/dist-cli/chunks/setup-repo-3BXLAX5E.js +2 -0
  83. package/dist-cli/chunks/{skills-DJA6QEVR.js → skills-KO7RCY24.js} +2 -2
  84. package/dist-cli/chunks/start-EBD7T2GW.js +23 -0
  85. package/dist-cli/chunks/store-ONX3EBS4.js +2 -0
  86. package/dist-cli/chunks/telemetry-MFR7TUW7.js +2 -0
  87. package/dist-cli/chunks/{test-IWUHNFXV.js → test-OSVUG54G.js} +3 -3
  88. package/dist-cli/chunks/three-mode-MDBXZQG4.js +39 -0
  89. package/dist-cli/chunks/timeline-UJOKZKQR.js +22 -0
  90. package/dist-cli/chunks/upload-H2SMWP6T.js +2 -0
  91. package/dist-cli/chunks/what-happened-LFWH74FR.js +15 -0
  92. package/dist-cli/chunks/whoami-CUF56TLP.js +2 -0
  93. package/dist-lib/agent-daemon-client.cjs +6 -1
  94. package/dist-lib/agent-events.cjs +1 -1
  95. package/dist-lib/agent-sessions.cjs +42 -39
  96. package/dist-lib/attached-projects.cjs +30 -28
  97. package/dist-lib/auth/shared-session.cjs +35 -27
  98. package/dist-lib/backend-origin.cjs +1 -1
  99. package/dist-lib/bridge-constants.cjs +1 -1
  100. package/dist-lib/cli-constants.cjs +1 -1
  101. package/dist-lib/config.cjs +6 -2
  102. package/dist-lib/dev-bundle-resolution.cjs +7 -21
  103. package/dist-lib/home-paths.cjs +112 -30
  104. package/dist-lib/host/bridge-host.cjs +1817 -579
  105. package/dist-lib/host/fetch-proxy-handler.cjs +248 -0
  106. package/dist-lib/index.cjs +22 -22
  107. package/dist-lib/metro.cjs +22 -22
  108. package/dist-lib/profiles.cjs +246 -0
  109. package/dist-lib/render-mode.cjs +1 -1
  110. package/dist-lib/vite-base.cjs +3224 -764
  111. package/dist-lib/vite.cjs +1 -1
  112. package/package.json +11 -3
  113. package/dist-cli/chunks/agent-wrapper-STO7PLQD.js +0 -15
  114. package/dist-cli/chunks/auto-bootstrap-SC2LMI2H.js +0 -2
  115. package/dist-cli/chunks/chunk-47S5DXXX.js +0 -11
  116. package/dist-cli/chunks/chunk-4VXB2DBA.js +0 -119
  117. package/dist-cli/chunks/chunk-AUR2LTNX.js +0 -3
  118. package/dist-cli/chunks/chunk-BQRM4E66.js +0 -4
  119. package/dist-cli/chunks/chunk-C3QLIYCS.js +0 -16
  120. package/dist-cli/chunks/chunk-EHMSE3Q3.js +0 -2
  121. package/dist-cli/chunks/chunk-F4ARVCRR.js +0 -1
  122. package/dist-cli/chunks/chunk-HAKR72LJ.js +0 -2
  123. package/dist-cli/chunks/chunk-HGFIS26A.js +0 -2
  124. package/dist-cli/chunks/chunk-MQDPKSCK.js +0 -308
  125. package/dist-cli/chunks/chunk-MZPAJ5PQ.js +0 -1
  126. package/dist-cli/chunks/chunk-OAHMYSMD.js +0 -2
  127. package/dist-cli/chunks/chunk-QIP7LYQI.js +0 -5
  128. package/dist-cli/chunks/chunk-QQOBLF7O.js +0 -22
  129. package/dist-cli/chunks/chunk-SY74J6F4.js +0 -5
  130. package/dist-cli/chunks/chunk-UKYK63H6.js +0 -3
  131. package/dist-cli/chunks/chunk-UNFERMZ3.js +0 -27
  132. package/dist-cli/chunks/chunk-VGXARPIH.js +0 -3
  133. package/dist-cli/chunks/chunk-W3TYN64D.js +0 -62
  134. package/dist-cli/chunks/chunk-W7CYWXRZ.js +0 -4
  135. package/dist-cli/chunks/chunk-WRF43M33.js +0 -4
  136. package/dist-cli/chunks/chunk-WVBPATRA.js +0 -2
  137. package/dist-cli/chunks/chunk-XJF46GU2.js +0 -2
  138. package/dist-cli/chunks/chunk-ZF5FCFLD.js +0 -2
  139. package/dist-cli/chunks/chunk-ZKNI5MRD.js +0 -1
  140. package/dist-cli/chunks/control-7QGKUCAX.js +0 -2
  141. package/dist-cli/chunks/daemon-4BLYGM5N.js +0 -49
  142. package/dist-cli/chunks/demo-app-registry-HLI5UGGI.js +0 -2
  143. package/dist-cli/chunks/detox-R4G5INNB.js +0 -49
  144. package/dist-cli/chunks/device-YSLCWS4E.js +0 -16
  145. package/dist-cli/chunks/drivers-YIXRFFBQ.js +0 -2
  146. package/dist-cli/chunks/electron-JZOFO37G.js +0 -15
  147. package/dist-cli/chunks/flow-L7X5FGIN.js +0 -2
  148. package/dist-cli/chunks/hints-O4QR6UGI.js +0 -2
  149. package/dist-cli/chunks/home-paths-4YJJYGR6.js +0 -2
  150. package/dist-cli/chunks/inspect-DRFAUJUH.js +0 -1030
  151. package/dist-cli/chunks/install-BATRTWRI.js +0 -65
  152. package/dist-cli/chunks/install-desktop-6X474IQ3.js +0 -23
  153. package/dist-cli/chunks/install-dev-desktop-CAJHPRNP.js +0 -100
  154. package/dist-cli/chunks/login-54YJ2KH6.js +0 -26
  155. package/dist-cli/chunks/logout-XECXLEXW.js +0 -2
  156. package/dist-cli/chunks/maestro-PMHK6EHI.js +0 -75
  157. package/dist-cli/chunks/profile-3IVNHUS6.js +0 -22
  158. package/dist-cli/chunks/runtime-PJKHEB36.js +0 -25
  159. package/dist-cli/chunks/screenshot-BXRAQERZ.js +0 -26
  160. package/dist-cli/chunks/screenshot-mode-5IXEDIUS.js +0 -17
  161. package/dist-cli/chunks/screenshots-T4MQF3TB.js +0 -70
  162. package/dist-cli/chunks/server-CIP3LH45.js +0 -29
  163. package/dist-cli/chunks/store-SPC247DB.js +0 -2
  164. package/dist-cli/chunks/upload-UPD2RSYF.js +0 -2
  165. package/dist-cli/chunks/whoami-MCXFWKIH.js +0 -2
@@ -1,29 +0,0 @@
1
- /*! sootsim v0.0.4 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{b as U,c as R,d as K,e as N,f as J,h as q,i as w,j as G,k as V,l as z,p as Y,s as b,t as Z,u as Q,v as X,w as ee}from"./chunk-BQRM4E66.js";import"./chunk-432TMHBG.js";import{a as $}from"./chunk-QIP7LYQI.js";import"./chunk-QQOBLF7O.js";import"./chunk-OAHMYSMD.js";import"./chunk-MZPAJ5PQ.js";import{o as A,p as D,q as _,r as L,t as M,u as W,v as F,w as j,x as H,y as O}from"./chunk-WRF43M33.js";import"./chunk-WWDJCKMI.js";import{spawn as T}from"child_process";import v from"fs";import{createServer as ae}from"http";import y from"path";import{WebSocket as f,WebSocketServer as ce}from"ws";import te from"node:fs";import C from"node:path";var E=1;function ne(){return[Number(process.env.VITE_PORT_WEB||process.env.PORT||3e3),Number(process.env.VITE_PORT_ZERO||7849),Number(process.env.VITE_PORT_POSTGRES||7432),Number(process.env.VITE_PORT_R2||9500)].filter(h=>Number.isFinite(h)&&h>0)}var P=class{subscriptions=new Map;sessionsBySocket=new Map;allSockets=new Set;pendingPromptEchoes=new Map;pendingTurns=new Map;opts;constructor(e={}){this.opts=e}registerSocket(e){this.allSockets.add(e)}unregisterSocket(e){let t=this.sessionsBySocket.get(e);if(t){for(let r of t)this.decrementSubscription(r);this.sessionsBySocket.delete(e)}this.allSockets.delete(e)}async handleMessage(e,t){let r=t?.type;if(typeof r!="string"||!r.startsWith("agent:"))return!1;let o=t.id;try{let s=await this.dispatch(e,r,t);this.respond(e,o,s)}catch(s){s instanceof b?this.respondError(e,o,s.message,s.code):this.respondError(e,o,s instanceof Error?s.message:String(s))}return!0}async seedOnBoot(){try{await z()}catch(e){process.stderr.write(`[sootsim-agent] seedFromDemoAppRegistry failed: ${e instanceof Error?e.message:String(e)}
3
- `)}}close(){for(let e of this.subscriptions.values())try{e.unsubscribe()}catch{}this.subscriptions.clear(),this.sessionsBySocket.clear(),this.allSockets.clear()}async dispatch(e,t,r){switch(t){case"agent:list-projects":return N();case"agent:upsert-project":return R(r.input??{});case"agent:delete-project":return q(String(r.projectId)),{ok:!0};case"agent:auto-attach-for-url":return this.autoAttachForUrl(r.input??{});case"agent:list-sessions":return G(r.projectId?String(r.projectId):void 0);case"agent:start-session":return this.doStartSession(r.input??{});case"agent:send-prompt":{let s=String(r.sessionId),n=w(s);if(!n)throw new b("NO_SESSION",`no session: ${s}`);let i=this.normalizePromptEnvelope(r);return await Q(s,i),this.notePromptAccepted(s,i,n.status==="working")}case"agent:end-session":this.dropSessionFanout(String(r.sessionId)),await X(String(r.sessionId));let o=w(String(r.sessionId));return o&&this.broadcastSessionStatus(o),{ok:!0};case"agent:get-transcript":return this.getTranscript(String(r.sessionId));case"agent:get-paths":return this.getPaths();case"agent:subscribe-events":return this.subscribeSocket(e,String(r.sessionId));case"agent:unsubscribe-events":return this.unsubscribeSocket(e,String(r.sessionId));default:throw new b("UNKNOWN_AGENT_MSG",`unknown agent message: ${t}`)}}async doStartSession(e){if(!K(e.projectId))throw new b("NO_PROJECT",`no project: ${e.projectId}`);let r=await Z(e);return this.broadcastSessionStatus(r.session),r}async autoAttachForUrl(e){let t=e.bundleUrl??"",r=(()=>{try{return new URL(t).port||null}catch{return null}})();if(!r)return{project:null};let o=this.opts.getExcludePorts?.()??ne(),n=(await $({excludePorts:o})).find(d=>String(d.port)===r);if(!n||!n.cwd)return{project:null};let i=N().find(d=>d.cwd===n.cwd)??null,a=Array.from(new Set([...i?.knownBundleUrls??[],n.bundleUrl,t]));return{project:R({cwd:n.cwd,name:n.projectName??C.basename(n.cwd),preferredProvider:e.provider??i?.preferredProvider,sourceRoots:i?.sourceRoots??[n.cwd],knownBundleUrls:a,framework:i?.framework??oe(n.framework),bundleId:n.bundleId??i?.bundleId})}}getTranscript(e){let t=Y(e);return te.existsSync(t)?te.readFileSync(t,"utf8"):{error:"transcript not found",code:"NO_TRANSCRIPT"}}getPaths(){let e=U();return{userDataDir:e,storeFile:C.join(e,"attached-projects.json"),sessionsDir:C.join(e,"sessions"),transcriptsDir:C.join(e,"transcripts")}}subscribeSocket(e,t){let r=this.sessionsBySocket.get(e);if(r||(r=new Set,this.sessionsBySocket.set(e,r)),r.has(t))return{ok:!0,refCount:this.subscriptions.get(t)?.refCount??1};r.add(t);let o=this.subscriptions.get(t);if(o)return o.refCount++,{ok:!0,refCount:o.refCount};let s=ee(t,n=>{let i=this.coalescePromptEcho(t,n);if(i&&(this.applySessionEvent(t,i),this.fanOutEvent(t,i)),n.type==="turn-completed"){let a=w(t);if(a)try{J(a.projectId,{usd:n.costUsd,ts:n.ts})}catch(c){process.stderr.write(`[sootsim-agent] recordTurnTelemetry failed: ${c instanceof Error?c.message:String(c)}
4
- `)}}});return this.subscriptions.set(t,{unsubscribe:s,refCount:1}),{ok:!0,refCount:1}}unsubscribeSocket(e,t){let r=this.sessionsBySocket.get(e);return!r||!r.has(t)?{ok:!0,refCount:0}:(r.delete(t),this.decrementSubscription(t))}decrementSubscription(e){let t=this.subscriptions.get(e);if(!t)return{ok:!0,refCount:0};if(t.refCount--,t.refCount<=0){try{t.unsubscribe()}catch{}return this.subscriptions.delete(e),{ok:!0,refCount:0}}return{ok:!0,refCount:t.refCount}}dropSessionFanout(e){let t=this.subscriptions.get(e);if(t){try{t.unsubscribe()}catch{}this.subscriptions.delete(e)}for(let r of this.sessionsBySocket.values())r.delete(e);this.clearPromptTracking(e)}normalizePromptEnvelope(e){if(e?.prompt&&typeof e.prompt=="object"){let t=e.prompt;return{text:String(t.text??""),...typeof t.displayText=="string"?{displayText:t.displayText}:{},...typeof t.inspectSummary=="string"?{inspectSummary:t.inspectSummary}:{},...typeof t.inspectTrace=="string"?{inspectTrace:t.inspectTrace}:{}}}return{text:String(e?.text??""),...typeof e?.displayText=="string"?{displayText:e.displayText}:{},...typeof e?.inspectSummary=="string"?{inspectSummary:e.inspectSummary}:{},...typeof e?.inspectTrace=="string"?{inspectTrace:e.inspectTrace}:{}}}notePromptAccepted(e,t,r){let o=Date.now(),s=this.pendingPromptEchoes.get(e)??[];s.push({sentAt:o}),this.pendingPromptEchoes.set(e,s);let n=Math.max(this.pendingTurns.get(e)??0,r?1:0)+1;this.pendingTurns.set(e,n);let i=t.displayText??t.text;return this.patchSession(e,{lastPrompt:i,status:"working",needsAttention:!1}),this.fanOutEvent(e,{type:"prompt-received",text:i,...t.inspectSummary?{inspectSummary:t.inspectSummary}:{},...t.inspectTrace?{inspectTrace:t.inspectTrace}:{},ts:o}),{ok:!0,queued:n>1,pendingTurns:n,queueDepth:Math.max(0,n-1)}}applySessionEvent(e,t){switch(t.type){case"prompt-received":case"turn-started":this.patchSession(e,{status:"working",needsAttention:!1});return;case"turn-completed":{let r=this.consumeSettledTurn(e);this.patchSession(e,{status:r>0?"working":"idle",needsAttention:!1,lastTurnFiles:t.filesTouched,currentlyEditing:void 0});return}case"approval-needed":this.patchSession(e,{status:"needs-attention",needsAttention:!0});return;case"error":{let r=this.consumeSettledTurn(e);this.patchSession(e,{status:r>0?"working":"needs-attention",needsAttention:r<=0,currentlyEditing:void 0});return}case"exited":this.clearPromptTracking(e),this.patchSession(e,{status:"ended",needsAttention:!1,wrapperPid:void 0,currentlyEditing:void 0});return;case"ready":case"turn-reasoning":case"turn-message":case"turn-plan":case"tool-call":case"file-edited":case"file-diff-delta":return}}patchSession(e,t){V(e,t);let r=w(e);r&&this.broadcastSessionStatus(r)}coalescePromptEcho(e,t){if(t.type!=="prompt-received")return t;let r=this.pendingPromptEchoes.get(e);if(!r||r.length===0)return t;for(;r.length>0&&Date.now()-r[0].sentAt>15e3;)r.shift();return r.length===0?(this.pendingPromptEchoes.delete(e),t):(r.shift(),r.length===0?this.pendingPromptEchoes.delete(e):this.pendingPromptEchoes.set(e,r),null)}consumeSettledTurn(e){let t=Math.max(0,(this.pendingTurns.get(e)??1)-1);return t>0?this.pendingTurns.set(e,t):this.pendingTurns.delete(e),t}clearPromptTracking(e){this.pendingPromptEchoes.delete(e),this.pendingTurns.delete(e)}fanOutEvent(e,t){let r=JSON.stringify({type:"agent:event",sessionId:e,event:t});for(let[o,s]of this.sessionsBySocket)if(s.has(e)&&o.readyState===E)try{o.send(r)}catch{}}broadcastSessionStatus(e){let t=JSON.stringify({type:"agent:session-status",session:e});for(let r of this.allSockets)if(r.readyState===E)try{r.send(t)}catch{}}respond(e,t,r){if(e.readyState===E)try{e.send(JSON.stringify({id:t,result:r}))}catch{}}respondError(e,t,r,o){if(e.readyState===E)try{e.send(JSON.stringify({id:t,error:r,...o?{code:o}:{}}))}catch{}}};function oe(h){return h==="expo"?"expo":h==="one"||h==="vxrn"?"one":"unknown"}var de=new Set(["tap","keyboard","close"]);function le(h){return!h||typeof h.type!="string"?!1:h.acquireLock===!0?!0:h.readOnly===!0?!1:de.has(h.type)}var ue=5e3,pe={".html":"text/html; charset=utf-8",".js":"application/javascript",".cjs":"application/javascript",".mjs":"application/javascript",".css":"text/css; charset=utf-8",".json":"application/json; charset=utf-8",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".svg":"image/svg+xml",".webp":"image/webp",".avif":"image/avif",".ico":"image/x-icon",".wasm":"application/wasm",".ttf":"font/ttf",".otf":"font/otf",".woff":"font/woff",".woff2":"font/woff2",".map":"application/json",".txt":"text/plain; charset=utf-8"},I=class h{port;openUrlHandler;httpServer=null;wss=null;nextCommandId=1;nextBrowserId=1;browsers=new Map;primaryBrowserId=null;pendingCommands=new Map;cliBySentId=new Map;cliBrowserBySocket=new Map;cliLastCommandAt=new Map;cliSessionKeyBySocket=new Map;cliLabelBySocket=new Map;restorableBrowsers=new Map;nextCliFallbackId=1;cliIdleTimer=null;agentHost;static CLI_IDLE_TIMEOUT_MS=6e4;static CLI_LEASE_TTL_MS=6e5;static USER_ACTIVE_LEASE_TTL_MS=8e3;static BROWSER_RECONNECT_TTL_MS=3e4;preferredPort;portFallbackCount;shouldWriteLockfile;effectivePort=0;startedAt=0;heartbeatTimer=null;activeRuntimeVersion=null;activeRuntimeDirPath=null;constructor(e={}){this.preferredPort=e.port||7668,this.port=this.preferredPort,this.shouldWriteLockfile=e.writeLockfile===!0;let t=this.shouldWriteLockfile?1:10;this.portFallbackCount=Math.max(1,e.portFallbackCount??t),this.openUrlHandler=e.openUrl,this.agentHost=new P({getExcludePorts:e.agentScanExcludes})}getAgentHost(){return this.agentHost}start(e){this.startAsync(e)}async startAsync(e){if(this.httpServer||this.wss)return this.effectivePort;this.refreshActiveRuntime();for(let t=0;t<this.portFallbackCount;t++){let r=this.preferredPort+t;try{return await this.bindOnce(r,e?.silent===!0),this.effectivePort=r,this.port=r,this.startedAt=Date.now(),t>0&&!e?.silent&&process.stderr.write(`ws bridge bound to port ${r} (preferred ${this.preferredPort} was taken)
5
- `),this.afterBind(),r}catch(o){if(o?.code!=="EADDRINUSE")throw o;e?.silent||process.stderr.write(`ws bridge port ${r} already in use, trying ${r+1}
6
- `)}}throw new Error(`could not bind ws bridge after ${this.portFallbackCount} attempts starting at ${this.preferredPort}`)}bindOnce(e,t){return new Promise((r,o)=>{let s=ae((a,c)=>this.handleHttpRequest(a,c)),n=!1,i=a=>{if(!n){n=!0;try{s.close()}catch{}this.httpServer=null,this.wss=null,o(a)}};s.once("error",i),s.listen(e,"127.0.0.1",()=>{n||(n=!0,s.removeListener("error",i),s.on("error",a=>{process.stderr.write(`ws bridge http error: ${String(a)}
7
- `)}),this.httpServer=s,this.wss=new ce({server:s}),this.wireWebSocketServer(),r())})})}wireWebSocketServer(){this.wss&&this.wss.on("connection",(e,t)=>{let r=t.headers.origin,o=r?"browser":"cli",s=null;if(this.agentHost.registerSocket(e),o==="browser")s={id:`tab-${this.nextBrowserId++}`,ws:e,origin:r,connectedAt:Date.now(),lastSeenAt:Date.now(),lastActiveAt:0,recentActions:[]},this.browsers.set(s.id,s),this.shouldPromoteBrowser(s)&&(this.primaryBrowserId=s.id),this.broadcastBrowserAssignments(),this.broadcastBrowserClientStates();else{let n=`ws-${this.nextCliFallbackId++}`;this.cliSessionKeyBySocket.set(e,n)}e.on("message",n=>{let i;try{i=JSON.parse(n.toString())}catch{return}if(!(!i||typeof i!="object")){if(typeof i.type=="string"&&i.type.startsWith("agent:")){this.agentHost.handleMessage(e,i);return}if(i.type==="runtime:list"){let a=L(),c=this.getActiveRuntime(),d={type:"runtime:list:ok",id:i.id,installed:a,active:c.version,activeRuntimeDir:c.runtimeDir};try{e.send(JSON.stringify(d))}catch{}return}if(i.type==="runtime:use"){let a=typeof i.version=="string"?i.version:"";if(!L().includes(a)){try{e.send(JSON.stringify({type:"runtime:use:error",id:i.id,error:`runtime ${a||"(missing)"} is not installed`}))}catch{}return}let d=this.setActiveRuntime(a);try{e.send(JSON.stringify({type:"runtime:use:ok",id:i.id,version:d.version,runtimeDir:d.runtimeDir}))}catch{}return}if(i.type==="runtime:get"){let a=this.getActiveRuntime();try{e.send(JSON.stringify({type:"runtime:get:ok",id:i.id,active:a.version,activeRuntimeDir:a.runtimeDir}))}catch{}return}if(o==="browser"){if(s&&(s.lastSeenAt=Date.now()),i.type==="bridge:register"&&s){let d=i,l=this.tryRestoreBrowserId(s,d.browserId);s.url=d.url,s.title=d.title,s.userAgent=d.userAgent,l&&(this.broadcastBrowserAssignments(),this.broadcastBrowserClientStates());return}if(i.type==="bridge:user-focus-state"&&s){let d=i;this.updateUserFocusLease(s,d.focused===!0);return}if(i.type==="bridge:user-interact"&&s){this.updateUserActivity(s);return}if(i.type==="bridge:open-path"){let d=typeof i.path=="string"?i.path:"",l=typeof i.line=="number"&&Number.isFinite(i.line)?i.line:void 0,p=typeof i.column=="number"&&Number.isFinite(i.column)?i.column:void 0;d&&this.openPathInEditor(d,l,p);return}if(i.type==="bridge:boot-clients"&&s){let d=[];for(let[p,u]of this.cliBrowserBySocket)u===s.id&&d.push(p);for(let p of d){this.cliBrowserBySocket.delete(p);try{p.close(1e3,"booted by browser")}catch{}}let l=!!s.cliLease;s.cliLease=void 0,(d.length>0||l)&&(process.stderr.write(`sootsim booted ${d.length} cli client(s)${l?" + cleared lease":""} from [${s.id}]
8
- `),this.recordBrowserAction(s.id,"browser booted cli clients"),this.broadcastBrowserClientStates());return}let a=this.pendingCommands.get(i.id);if(a){this.pendingCommands.delete(i.id),i.error?a.reject(new Error(i.error)):a.resolve(i.result);return}let c=this.cliBySentId.get(i.id);if(c&&(this.cliBySentId.delete(i.id),c.ws.readyState===f.OPEN)){let d=this.getOtherCliSessionCount(c.ws,c.browserId),l=d>0?{...i,id:c.originalId,i:d}:{...i,id:c.originalId};c.ws.send(JSON.stringify(l))}return}(async()=>{this.cliLastCommandAt.set(e,Date.now());try{if(i.type==="bridge:bye"){let p=this.cliBrowserBySocket.delete(e);this.cliLastCommandAt.delete(e),this.cliSessionKeyBySocket.delete(e),this.cliLabelBySocket.delete(e);for(let[u,m]of this.cliBySentId)m.ws===e&&this.cliBySentId.delete(u);p&&this.broadcastBrowserClientStates();return}if(i.type==="bridge:hello"){let p=typeof i.cliSessionKey=="string"&&i.cliSessionKey.trim()?i.cliSessionKey.trim():this.cliSessionKeyBySocket.get(e)||`ws-${this.nextCliFallbackId++}`;this.cliSessionKeyBySocket.set(e,p),typeof i.cliLabel=="string"&&i.cliLabel.trim()&&this.cliLabelBySocket.set(e,i.cliLabel.trim()),e.readyState===f.OPEN&&e.send(JSON.stringify({id:i.id,result:{cliSessionKey:p,leaseTtlMs:h.CLI_LEASE_TTL_MS,leasing:!0}}));return}if(i.type==="bridge:list-browsers"){e.readyState===f.OPEN&&e.send(JSON.stringify({id:i.id,result:this.listBrowsers()}));return}if(i.type==="bridge:open"){if(typeof i.url!="string"||!i.url)throw new Error("bridge:open requires a url");await this.openUrl(i.url),e.readyState===f.OPEN&&e.send(JSON.stringify({id:i.id,result:{ok:!0,url:i.url}}));return}if(i.type==="bridge:claim"){let p=await this.waitForBrowser(i.browserId),u=this.tryAcquireLease(e,p,{force:i.force===!0});if(!u.granted){e.readyState===f.OPEN&&e.send(JSON.stringify({id:i.id,error:`tab ${p.id} is locked by another cli`,o:u.lock}));return}this.setCliBrowserTarget(e,p.id),this.recordBrowserAction(p.id,u.bootedCount>0?`cli force-claimed tab (booted ${u.bootedCount})`:"cli claimed tab"),e.readyState===f.OPEN&&e.send(JSON.stringify({id:i.id,result:{browserId:p.id,lockedBy:u.lease.cliSessionKey,lockExpiresAt:u.lease.expiresAt,bootedCount:u.bootedCount}}));return}let a=await this.waitForBrowser(i.browserId);if(le(i)){let p=this.tryAcquireLease(e,a);if(!p.granted){e.readyState===f.OPEN&&e.send(JSON.stringify({id:i.id,error:`tab ${a.id} is locked by another cli \u2014 use \`sootsim claim ${a.id} --force\` or \`sootsim open --new\``,o:p.lock}));return}}else this.ensureCliSessionKey(e);this.setCliBrowserTarget(e,a.id),this.recordBrowserAction(a.id,this.describeForwardedCommand(i));let c=this.nextCommandId++;this.cliBySentId.set(c,{browserId:a.id,ws:e,originalId:i.id});let{browserId:d,...l}=i;a.ws.send(JSON.stringify({...l,id:c}))}catch(a){e.readyState===f.OPEN&&e.send(JSON.stringify({id:i.id,error:a instanceof Error?a.message:String(a)}))}})()}}),e.on("close",()=>{if(this.agentHost.unregisterSocket(e),o==="browser"&&s){this.rememberDisconnectedBrowser(s),this.primaryBrowserId===s.id&&(this.primaryBrowserId=this.getOpenBrowser()?.id??null);for(let[n,i]of this.pendingCommands)i.browserId===s.id&&(i.reject(new Error("browser disconnected")),this.pendingCommands.delete(n));for(let[n,i]of this.cliBySentId)i.browserId===s.id&&(i.ws.readyState===f.OPEN&&i.ws.send(JSON.stringify({id:i.originalId,error:"browser disconnected before responding"})),this.cliBySentId.delete(n));this.broadcastBrowserAssignments(),this.broadcastBrowserClientStates()}else if(o==="cli"){let n=this.cliBrowserBySocket.delete(e);this.cliLastCommandAt.delete(e),this.cliSessionKeyBySocket.delete(e),this.cliLabelBySocket.delete(e);for(let[i,a]of this.cliBySentId)a.ws===e&&this.cliBySentId.delete(i);n&&this.broadcastBrowserClientStates()}})})}afterBind(){if(process.stderr.write(`ws bridge listening on port ${this.port}
9
- `),this.cliIdleTimer=setInterval(()=>this.sweepIdleCliClients(),3e4),this.cliIdleTimer.unref(),this.shouldWriteLockfile){try{if(A(),!H(this.buildLockfileSnapshot()))throw new Error("another sootsim daemon wrote the lockfile during startup \u2014 aborting")}catch(e){throw process.stderr.write(`ws bridge failed to claim daemon lockfile: ${String(e)}
10
- `),e}this.heartbeatTimer=setInterval(()=>{try{this.writeLockfileSnapshot()}catch{}},ue),this.heartbeatTimer.unref()}this.agentHost.seedOnBoot()}buildLockfileSnapshot(){return{schema:1,pid:process.pid,platform:process.platform,bridgePort:this.effectivePort,runtimePort:this.effectivePort,activeRuntime:this.activeRuntimeVersion,activeRuntimeDir:this.activeRuntimeDirPath,startedAt:this.startedAt,heartbeatAt:Date.now()}}writeLockfileSnapshot(){j(this.buildLockfileSnapshot())}refreshActiveRuntime(){this.activeRuntimeVersion=D(),this.activeRuntimeDirPath=M()}setActiveRuntime(e){if(_(e),this.refreshActiveRuntime(),this.shouldWriteLockfile&&this.httpServer)try{this.writeLockfileSnapshot()}catch{}let t=JSON.stringify({type:"runtime:changed",version:e,runtimeDir:this.activeRuntimeDirPath});for(let r of this.browsers.values())if(r.ws.readyState===f.OPEN)try{r.ws.send(t)}catch{}return{version:e,runtimeDir:this.activeRuntimeDirPath}}getActiveRuntime(){return{version:this.activeRuntimeVersion,runtimeDir:this.activeRuntimeDirPath}}removeLockfile(){if(this.shouldWriteLockfile)try{O()}catch{}}handleHttpRequest(e,t){let r=(e.method||"GET").toUpperCase();if(r!=="GET"&&r!=="HEAD"){t.writeHead(405,{Allow:"GET, HEAD"}),t.end("method not allowed");return}let o=new URL(e.url||"/","http://localhost");if(o.pathname==="/__bundle-proxy"){let c=o.searchParams.get("url");if(!c){t.writeHead(400,{"Content-Type":"text/plain"}),t.end("bundle-proxy: missing url query param");return}let d;try{d=new URL(c)}catch{t.writeHead(400,{"Content-Type":"text/plain"}),t.end("bundle-proxy: invalid url");return}let l=d.hostname;if(!(l==="localhost"||l==="127.0.0.1"||l==="::1"||l.endsWith(".localhost"))){t.writeHead(403,{"Content-Type":"text/plain"}),t.end("bundle-proxy: only loopback targets allowed");return}(async()=>{try{let u=await fetch(d.toString(),{redirect:"follow"}),m={},g=u.headers.get("content-type");if(g&&(m["Content-Type"]=g),m["Cache-Control"]="no-store",t.writeHead(u.status,m),!u.body){t.end();return}let S=u.body.getReader();for(;;){let{done:B,value:k}=await S.read();if(B)break;t.write(Buffer.from(k))}t.end()}catch(u){t.writeHead(502,{"Content-Type":"text/plain"}),t.end(`bundle-proxy: upstream fetch failed: ${u instanceof Error?u.message:String(u)}`)}})();return}if(o.pathname==="/healthz"){t.writeHead(200,{"Content-Type":"application/json","Cache-Control":"no-store"}),t.end(JSON.stringify({ok:!0,pid:process.pid,platform:process.platform,bridgePort:this.effectivePort,runtimePort:this.effectivePort,activeRuntime:this.activeRuntimeVersion,startedAt:this.startedAt,uptimeMs:this.startedAt>0?Date.now()-this.startedAt:0}));return}this.refreshActiveRuntime();let s=this.activeRuntimeDirPath;if(!s){t.writeHead(503,{"Content-Type":"text/plain; charset=utf-8"}),t.end("sootsim: no active runtime installed. run `sootsim runtime install` to fetch one.");return}let n=o.pathname;if(n==="/runtime"||n==="/runtime/"?n="/":n.startsWith("/runtime/")&&(n=n.slice(8)),(n===""||n==="/")&&(n="/index.html"),n.includes("\0")){t.writeHead(400),t.end("bad request");return}if(process.platform!=="win32"&&n.includes("\\")){t.writeHead(400),t.end("bad request");return}for(let c of n.split("/"))if(c===".."){t.writeHead(403),t.end("forbidden");return}let i=y.resolve(s,"."+n),a=s.endsWith(y.sep)?s:s+y.sep;if(!i.startsWith(a)&&i!==s){t.writeHead(403),t.end("forbidden");return}v.realpath(i,(c,d)=>{let l=c?i:d,p=l.endsWith(y.sep)?l:l+y.sep;if(!c){let u=(()=>{try{let m=v.realpathSync(s);return m.endsWith(y.sep)?m:m+y.sep}catch{return a}})();if(!p.startsWith(u)&&l+y.sep!==u){t.writeHead(403),t.end("forbidden");return}}v.stat(l,(u,m)=>{if(u||!m?.isFile()){let k=y.extname(n).toLowerCase();if(k&&k!==".html"){t.writeHead(404),t.end("not found");return}let re=y.join(s,"index.html");v.readFile(re,(ie,se)=>{if(ie){t.writeHead(404),t.end("not found");return}if(t.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),r==="HEAD"){t.end();return}t.end(se)});return}let g=y.extname(l).toLowerCase(),S=pe[g]||"application/octet-stream";if(t.writeHead(200,{"Content-Type":S}),r==="HEAD"){t.end();return}let B=v.createReadStream(l);B.pipe(t),B.on("error",()=>{try{t.end()}catch{}})})})}sweepIdleCliClients(){let e=Date.now(),t=!1;for(let[r,o]of this.cliBrowserBySocket){let s=this.cliLastCommandAt.get(r)??0;if(!(e-s<h.CLI_IDLE_TIMEOUT_MS)){this.cliBrowserBySocket.delete(r),this.cliLastCommandAt.delete(r);for(let[n,i]of this.cliBySentId)i.ws===r&&this.cliBySentId.delete(n);try{r.close(1e3,"idle timeout")}catch{}t=!0}}t&&this.broadcastBrowserClientStates(),this.sweepRestorableBrowsers(e)}listBrowsers(){return Array.from(this.browsers.values()).sort((e,t)=>e.id===this.primaryBrowserId?-1:t.id===this.primaryBrowserId?1:e.connectedAt-t.connectedAt).map(e=>this.describeBrowser(e))}async sendCommand(e){let t=await this.waitForBrowser(e.browserId),r=this.nextCommandId++;return new Promise((o,s)=>{let n=setTimeout(()=>{this.pendingCommands.delete(r),this.broadcastBrowserClientStates(),s(new Error("command timed out after 30s"))},3e4);this.pendingCommands.set(r,{browserId:t.id,resolve:c=>{clearTimeout(n),this.pendingCommands.delete(r),this.broadcastBrowserClientStates(),o(c)},reject:c=>{clearTimeout(n),this.pendingCommands.delete(r),this.broadcastBrowserClientStates(),s(c)}}),this.broadcastBrowserClientStates();let{browserId:i,...a}=e;t.ws.send(JSON.stringify({...a,id:r}))})}async evaluate(e,t){return this.sendCommand({type:"evaluate",code:e,browserId:t})}async focusBrowser(e){return this.sendCommand({type:"focus",browserId:e})}async closeBrowser(e){return this.sendCommand({type:"close",browserId:e})}async openPathInEditor(e,t,r){let o=t!=null?`:${t}${r!=null?`:${r}`:""}`:"",s=`${e}${o}`,n=(a,c)=>new Promise(d=>{try{let l=T(a,c,{detached:!0,stdio:"ignore"}),p=!1;l.on("error",()=>{p||(p=!0,d(!1))}),l.on("spawn",()=>{p||(p=!0,l.unref(),d(!0))})}catch{d(!1)}}),i=process.env.REACT_EDITOR||process.env.EDITOR;if(i){let a=i.split(" ").filter(Boolean);if(a.length&&await n(a[0],[...a.slice(1),"-g",s]))return}await n("cursor",["-g",s])||await n("code",["-g",s])||await this.openUrl(e)}async openUrl(e){if(this.openUrlHandler){await this.openUrlHandler(e);return}if(process.platform==="darwin"){T("open",["-g",e],{detached:!0,stdio:"ignore"}).unref();return}if(process.platform==="win32"){T("cmd",["/c","start","",e],{detached:!0,stdio:"ignore"}).unref();return}T("xdg-open",[e],{detached:!0,stdio:"ignore"}).unref()}async close(){if(this.cliIdleTimer&&(clearInterval(this.cliIdleTimer),this.cliIdleTimer=null),this.heartbeatTimer&&(clearInterval(this.heartbeatTimer),this.heartbeatTimer=null),this.shouldWriteLockfile)try{O()}catch{}this.effectivePort=0,this.startedAt=0,this.agentHost.close();for(let[r,o]of this.pendingCommands)o.reject(new Error("server closing")),this.pendingCommands.delete(r);for(let r of this.browsers.values())r.ws.close();this.browsers.clear(),this.primaryBrowserId=null;let e=this.wss,t=this.httpServer;if(this.wss=null,this.httpServer=null,e)try{e.close()}catch{}if(t)try{t.close()}catch{}}describeBrowser(e){let t;try{t=e.ws.readyState}catch{t=f.CLOSED}let r=this.getActiveLease(e);return{id:e.id,origin:e.origin,url:e.url,title:e.title,userAgent:e.userAgent,connectedAt:e.connectedAt,lastSeenAt:e.lastSeenAt,lastActiveAt:e.lastActiveAt||void 0,isPrimary:e.id===this.primaryBrowserId,readyState:t===f.OPEN?"open":t===f.CLOSING?"closing":"closed",attachedCliCount:this.getAttachedCliCount(e.id),lockedBy:r?r.cliLabel||r.cliSessionKey:void 0,lockedByKind:r?r.kind:void 0,lockExpiresAt:r?r.expiresAt:void 0,userFocused:e.userFocused||void 0}}getActiveLease(e){let t=e.cliLease;return t?Date.now()>=t.expiresAt?(e.cliLease=void 0,null):t:null}tryAcquireLease(e,t,r={}){let o=this.cliSessionKeyBySocket.get(e)??(()=>{let l=`ws-${this.nextCliFallbackId++}`;return this.cliSessionKeyBySocket.set(e,l),l})(),s=this.cliLabelBySocket.get(e),n=Date.now(),i=this.getActiveLease(t),a=i&&i.cliSessionKey===o,c=0;if(i&&!a&&!r.force)return{granted:!1,lease:i,lock:{by:i.cliLabel||i.cliSessionKey,expiresInMs:Math.max(0,i.expiresAt-n)},bootedCount:0};if(i&&!a&&r.force)for(let[l,p]of this.cliBrowserBySocket){if(p!==t.id)continue;let u=this.cliSessionKeyBySocket.get(l);if(u&&u!==o){this.cliBrowserBySocket.delete(l);try{l.close(1e3,"lease claimed by another cli")}catch{}c++}}let d={kind:"cli",cliSessionKey:o,cliLabel:s,expiresAt:n+h.CLI_LEASE_TTL_MS};return t.cliLease=d,{granted:!0,lease:d,bootedCount:c}}updateUserFocusLease(e,t){let r=t;e.userFocused!==r&&(e.userFocused=r,this.broadcastBrowserClientStates())}updateUserActivity(e){let t=this.getActiveLease(e);t&&t.kind==="cli"||(e.cliLease={kind:"user-active",cliSessionKey:"__user-active__",cliLabel:"active user",expiresAt:Date.now()+h.USER_ACTIVE_LEASE_TTL_MS},this.broadcastBrowserClientStates())}ensureCliSessionKey(e){let t=this.cliSessionKeyBySocket.get(e);if(t)return t;let r=`ws-${this.nextCliFallbackId++}`;return this.cliSessionKeyBySocket.set(e,r),r}getOpenBrowser(e){if(e){let r=this.browsers.get(e);return r?.ws.readyState===f.OPEN?r:null}let t=this.primaryBrowserId!=null?this.browsers.get(this.primaryBrowserId):null;if(t?.ws.readyState===f.OPEN)return t;for(let r of this.browsers.values())if(r.ws.readyState===f.OPEN)return r;return null}async waitForBrowser(e,t={}){let r=t.attempts??10,o=t.intervalMs??200;for(let s=0;s<r;s++){let n=this.getOpenBrowser(e);if(n)return n;await new Promise(i=>setTimeout(i,o))}throw new Error(e?`no browser connected with id ${e}`:"no browser connected")}shouldPromoteBrowser(e){let t=this.primaryBrowserId?this.browsers.get(this.primaryBrowserId):null,r=e.origin?.includes(":5173"),o=t?.origin?.includes(":5173");return!t||t.ws.readyState!==f.OPEN||!!r||!o}broadcastBrowserAssignments(){for(let e of this.browsers.values())e.ws.readyState===f.OPEN&&e.ws.send(JSON.stringify({type:"bridge:welcome",browserId:e.id,isPrimary:e.id===this.primaryBrowserId}))}broadcastBrowserClientStates(){for(let e of this.browsers.values()){if(e.ws.readyState!==f.OPEN)continue;let t=this.getActiveLease(e),r={type:"bridge:client-state",attachedCliCount:this.getAttachedCliCount(e.id),activeAgentCommandCount:this.getActiveAgentCommandCount(e.id),recentActions:e.recentActions,lockedBy:t?t.cliLabel||t.cliSessionKey:void 0,lockedByKind:t?t.kind:void 0,lockExpiresAt:t?t.expiresAt:void 0,userFocused:e.userFocused||void 0};e.ws.send(JSON.stringify(r))}}setCliBrowserTarget(e,t){let r=this.cliBrowserBySocket.get(e);r!==t&&(this.cliBrowserBySocket.set(e,t),this.recordBrowserAction(t,r?"cli switched tabs":"cli connected",!1),this.broadcastBrowserClientStates())}recordBrowserAction(e,t,r=!0){let o=t?.trim();if(!o)return;let s=this.browsers.get(e);if(!s)return;let n=Date.now();s.lastActiveAt=n,s.recentActions=[{label:o,at:n},...s.recentActions.filter(i=>i.label!==o)].slice(0,4),r&&this.broadcastBrowserClientStates()}describeForwardedCommand(e){switch(e?.type){case"evaluate":return"evaluated page state";case"screenshot":return"captured screenshot";case"tap":return"sent tap event";case"keyboard":return e?.action==="type"?"typed text":"used keyboard";case"tree":return"dumped tree";case"focus":return"focused tab";case"close":return"requested close";default:return typeof e?.type=="string"?e.type:null}}getAttachedCliCount(e){let t=new Set;for(let[r,o]of this.cliBrowserBySocket){if(o!==e||r.readyState!==f.OPEN)continue;let s=this.cliSessionKeyBySocket.get(r);t.add(s??`ws-unknown-${t.size}`)}return t.size}getOtherCliSessionCount(e,t){let r=this.cliSessionKeyBySocket.get(e),o=new Set;for(let[s,n]of this.cliBrowserBySocket){if(n!==t||s.readyState!==f.OPEN)continue;let i=this.cliSessionKeyBySocket.get(s);i&&i===r||o.add(i??`ws-unknown-${o.size}`)}return o.size}getActiveAgentCommandCount(e){let t=0;for(let r of this.pendingCommands.values())r.browserId===e&&t++;return t}tryRestoreBrowserId(e,t){let r=t?.trim();if(!r||r===e.id)return!1;let o=this.browsers.get(r);if(o&&o!==e&&o.ws.readyState===f.OPEN)return!1;let s=this.getRestorableBrowserState(r),n=e.id;this.browsers.delete(n),e.id=r,s&&(e.recentActions=s.recentActions.map(i=>({...i})),e.lastActiveAt=s.lastActiveAt,e.cliLease=s.cliLease?{...s.cliLease}:void 0,this.restorableBrowsers.delete(r)),this.browsers.set(e.id,e),this.primaryBrowserId===n&&(this.primaryBrowserId=e.id);for(let[i,a]of this.cliBrowserBySocket)a===n&&this.cliBrowserBySocket.set(i,e.id);return!0}rememberDisconnectedBrowser(e){let t=this.getActiveLease(e);this.restorableBrowsers.set(e.id,{recentActions:e.recentActions.map(r=>({...r})),lastActiveAt:e.lastActiveAt,cliLease:t&&t.kind==="cli"?{...t}:void 0,expiresAt:Date.now()+h.BROWSER_RECONNECT_TTL_MS}),this.browsers.delete(e.id)}getRestorableBrowserState(e){let t=this.restorableBrowsers.get(e);return t?t.expiresAt<=Date.now()?(this.restorableBrowsers.delete(e),null):(t.cliLease&&t.cliLease.expiresAt<=Date.now()&&(t.cliLease=void 0),t):null}sweepRestorableBrowsers(e=Date.now()){for(let[t,r]of this.restorableBrowsers)if(!(r.expiresAt>e)){this.restorableBrowsers.delete(t);for(let[o,s]of this.cliBrowserBySocket)s===t&&this.cliBrowserBySocket.delete(o)}}resetServerState(){this.cliIdleTimer&&(clearInterval(this.cliIdleTimer),this.cliIdleTimer=null);let e=this.wss,t=this.httpServer;if(this.wss=null,this.httpServer=null,e)try{e.close()}catch{}if(t)try{t.close()}catch{}}};async function Le(h,e={}){(h.includes("--help")||h.includes("-h"))&&(console.log(`
11
- sootsim server \u2014 run the sootsim bridge daemon in the foreground
12
-
13
- hosts the WS bridge that CLI commands talk to. once running, any sootsim
14
- tab (browser, electron, headless playwright) that connects to port 7668
15
- becomes drivable from 'sootsim describe', 'sootsim tap', etc.
16
-
17
- usage:
18
- sootsim server [options]
19
-
20
- options:
21
- --port <n> bridge port (defaults to ${7668})
22
- --quiet suppress per-connection logging
23
-
24
- examples:
25
- sootsim server
26
- sootsim server --port 7668 --quiet
27
- `),process.exit(0));let t=h.indexOf("--port"),r=t>=0&&h[t+1]?Number(h[t+1]):e.port??7668;Number.isNaN(r)&&(console.error(` invalid --port value: ${h[t+1]}`),process.exit(1));let o=h.includes("--quiet")||h.includes("-q"),s=W();s&&F(s)&&(console.error(` a sootsim daemon is already running (pid ${s.pid}, port ${s.bridgePort})`),console.error(" stop it with 'sootsim daemon stop' first"),process.exit(1)),A();let n=new I({port:r,writeLockfile:!0}),i=await n.startAsync({silent:o}),a=Date.now(),c=u=>{o||process.stdout.write(`${u}
28
- `)},d=new Set,l=setInterval(()=>{let u=n.listBrowsers(),m=new Set(u.map(g=>g.id));for(let g of u)if(!d.has(g.id)){let S=g.title||g.url||g.origin||"(unknown)";c(` + ${g.id} ${S}`)}for(let g of d)m.has(g)||c(` - ${g}`);d.clear();for(let g of m)d.add(g)},500);c(`sootsim bridge listening on ws://localhost:${i} (runtime http on same port)`),i!==r&&c(` (preferred port ${r} was taken \u2014 fell back to ${i})`),c(" ready for browser tabs, electron, or headless playwright to connect"),c(" (ctrl-c to stop)");let p=async u=>{clearInterval(l),c(`
29
- ${u} received \u2014 shutting down after ${Math.round((Date.now()-a)/1e3)}s`);try{await n.close()}catch{}process.exit(0)};process.on("SIGINT",()=>p("SIGINT")),process.on("SIGTERM",()=>p("SIGTERM")),process.on("SIGHUP",()=>p("SIGHUP")),process.on("exit",()=>{try{n.removeLockfile()}catch{}}),await new Promise(()=>{})}export{Le as runServer};
@@ -1,2 +0,0 @@
1
- /*! sootsim v0.0.4 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{a}from"./chunk-XJF46GU2.js";import"./chunk-EHMSE3Q3.js";import"./chunk-WWDJCKMI.js";export{a as settingsStore};
@@ -1,2 +0,0 @@
1
- /*! sootsim v0.0.4 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{c as a,d as b}from"./chunk-W3TYN64D.js";import"./chunk-AUR2LTNX.js";import"./chunk-UKYK63H6.js";import"./chunk-WVBPATRA.js";import"./chunk-C3QLIYCS.js";import"./chunk-W7CYWXRZ.js";import"./chunk-MZPAJ5PQ.js";import"./chunk-WRF43M33.js";import"./chunk-WWDJCKMI.js";export{a as resolveDefaultUploadOrigin,b as runUpload};
@@ -1,2 +0,0 @@
1
- /*! sootsim v0.0.4 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
- import{d as n}from"./chunk-UKYK63H6.js";import"./chunk-WWDJCKMI.js";async function s(){let o=await n();o?.token||(console.log(" not signed in"),process.exit(1));let e=o.user;console.log(` ${e?.email||e?.name||e?.id||"signed in"}`),console.log(` origin: ${o.origin}`),console.log(` updated: ${o.updatedAt}`)}export{s as runWhoami};