@sma1lboy/rove 0.9.258 → 0.9.262

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 (139) hide show
  1. package/dist/cli/{chunk-rx2604ry.js → chunk-06wk3r66.js} +1 -1
  2. package/dist/cli/chunk-0wy0w6jd.js +3 -0
  3. package/dist/cli/{chunk-wyt2vgjv.js → chunk-1bfcy0n9.js} +1 -1
  4. package/dist/cli/chunk-1dzr3fba.js +2 -0
  5. package/dist/cli/{chunk-mmc662x2.js → chunk-1pfsfpcg.js} +1 -1
  6. package/dist/cli/{chunk-jtpjdmb6.js → chunk-1x6yahf1.js} +1 -1
  7. package/dist/cli/{chunk-tq6pq1ws.js → chunk-2676cb62.js} +1 -1
  8. package/dist/cli/{chunk-ehb0hbrg.js → chunk-2cwzyxcs.js} +2 -2
  9. package/dist/cli/{chunk-r6f8h111.js → chunk-31h3d4re.js} +2 -2
  10. package/dist/cli/{chunk-68113ts1.js → chunk-36y93k92.js} +1 -1
  11. package/dist/cli/chunk-37v3752z.js +2 -0
  12. package/dist/cli/{chunk-0604fza5.js → chunk-3ds88mym.js} +1 -1
  13. package/dist/cli/{chunk-cewyvr3c.js → chunk-4ntrjm11.js} +1 -1
  14. package/dist/cli/{chunk-kgddz8pw.js → chunk-4t1dvw2h.js} +1 -1
  15. package/dist/cli/chunk-4tgz542w.js +3 -0
  16. package/dist/cli/chunk-53p99fe7.js +66 -0
  17. package/dist/cli/{chunk-sabpcket.js → chunk-5a3c9fnd.js} +1 -1
  18. package/dist/cli/{chunk-ke3t032y.js → chunk-5js4agdd.js} +1 -1
  19. package/dist/cli/{chunk-zqq3djrw.js → chunk-65kx9ksj.js} +3 -3
  20. package/dist/cli/{chunk-cb7hc2xm.js → chunk-68xcxsbg.js} +1 -1
  21. package/dist/cli/{chunk-pb5g6161.js → chunk-6an1v8vq.js} +1 -1
  22. package/dist/cli/{chunk-s0jbtyd9.js → chunk-6ks6s8b2.js} +3 -2
  23. package/dist/cli/{chunk-45thgqdr.js → chunk-6nw0s0ab.js} +1 -1
  24. package/dist/cli/{chunk-t25xke74.js → chunk-73fw8277.js} +1 -1
  25. package/dist/cli/{chunk-1xrbb7gf.js → chunk-74e3k701.js} +1 -1
  26. package/dist/cli/{chunk-stcvcsfm.js → chunk-7697syt8.js} +1 -1
  27. package/dist/cli/{chunk-3q1rv3rm.js → chunk-7nqzg7h6.js} +2 -2
  28. package/dist/cli/chunk-7t9q7k4d.js +4 -0
  29. package/dist/cli/{chunk-0d57z680.js → chunk-8e8y2m0b.js} +1 -1
  30. package/dist/cli/chunk-8k04kxqq.js +20 -0
  31. package/dist/cli/chunk-951xzbsm.js +3 -0
  32. package/dist/cli/{chunk-dqprpnd6.js → chunk-95gk3twe.js} +1 -1
  33. package/dist/cli/chunk-b7w3q5f1.js +2 -0
  34. package/dist/cli/chunk-c0gvp57p.js +3 -0
  35. package/dist/cli/{chunk-pwbtezsa.js → chunk-c3g1bfn6.js} +1 -1
  36. package/dist/cli/{chunk-yktnbb14.js → chunk-c7j05xps.js} +37 -37
  37. package/dist/cli/chunk-db61d8dz.js +3 -0
  38. package/dist/cli/{chunk-95jzkqp2.js → chunk-dc3n6nf7.js} +1 -1
  39. package/dist/cli/{chunk-x8rkpfqd.js → chunk-dq3m7rhj.js} +1 -1
  40. package/dist/cli/{chunk-hfvz23r6.js → chunk-edrw4tby.js} +1 -1
  41. package/dist/cli/chunk-ephcyh6v.js +3 -0
  42. package/dist/cli/{chunk-jd02zgwf.js → chunk-esk09jfb.js} +1 -1
  43. package/dist/cli/{chunk-cq5qv93a.js → chunk-fg55dc4s.js} +1 -1
  44. package/dist/cli/{chunk-278sc87n.js → chunk-fq3s9q62.js} +1 -1
  45. package/dist/cli/{chunk-1egwgg6p.js → chunk-gc3f7zbs.js} +1 -1
  46. package/dist/cli/{chunk-y0gybgba.js → chunk-gc78tfn9.js} +1 -1
  47. package/dist/cli/chunk-h0pg8e0g.js +70 -0
  48. package/dist/cli/{chunk-b47k0cnw.js → chunk-h5znx9v9.js} +1 -1
  49. package/dist/cli/chunk-hrgd7vws.js +2 -0
  50. package/dist/cli/chunk-jc2jwy0j.js +2 -0
  51. package/dist/cli/{chunk-8gjpnb83.js → chunk-k21hb0rj.js} +1 -1
  52. package/dist/cli/{chunk-rc2nwsdd.js → chunk-k71h719j.js} +1 -1
  53. package/dist/cli/chunk-karefnj8.js +11 -0
  54. package/dist/cli/chunk-kkzmdwfr.js +7 -0
  55. package/dist/cli/chunk-kqdq5g1x.js +3 -0
  56. package/dist/cli/{chunk-6x0trw81.js → chunk-m2qajv9v.js} +1 -1
  57. package/dist/cli/{chunk-1vwngrmf.js → chunk-m3773n9n.js} +1 -1
  58. package/dist/cli/chunk-m3nfbnxy.js +2 -0
  59. package/dist/cli/{chunk-edpbz0gz.js → chunk-m5kxc6m3.js} +3 -3
  60. package/dist/cli/chunk-mgx6w27b.js +2 -0
  61. package/dist/cli/{chunk-ebea569y.js → chunk-n8p8b9qd.js} +1 -1
  62. package/dist/cli/{chunk-9v18qy3s.js → chunk-nnda6138.js} +1 -1
  63. package/dist/cli/{chunk-edsm15ht.js → chunk-nwevnc6k.js} +1 -1
  64. package/dist/cli/{chunk-7cd1epfw.js → chunk-nxdm60xy.js} +1 -1
  65. package/dist/cli/{chunk-pw87thvf.js → chunk-p0ycfqw2.js} +1 -1
  66. package/dist/cli/{chunk-sma961wh.js → chunk-q07r6hf7.js} +1 -1
  67. package/dist/cli/{chunk-jf46jsdp.js → chunk-q55z079p.js} +1 -1
  68. package/dist/cli/{chunk-xnaehfby.js → chunk-qc376zm1.js} +1 -1
  69. package/dist/cli/chunk-qk9tgve2.js +2 -0
  70. package/dist/cli/{chunk-jv11f2c2.js → chunk-qwhajjyw.js} +3 -3
  71. package/dist/cli/chunk-r03m4san.js +2 -0
  72. package/dist/cli/{chunk-kqf371ff.js → chunk-r1epx453.js} +1 -1
  73. package/dist/cli/chunk-r3zkpbf5.js +9 -0
  74. package/dist/cli/{chunk-6p9yxt3q.js → chunk-r7p1dhrv.js} +1 -1
  75. package/dist/cli/{chunk-y8xb4ys7.js → chunk-rcqckj34.js} +1 -1
  76. package/dist/cli/{chunk-zam29b9x.js → chunk-rh5vxymy.js} +1 -1
  77. package/dist/cli/chunk-rrqjk4rn.js +16 -0
  78. package/dist/cli/chunk-s83qnstd.js +2 -0
  79. package/dist/cli/{chunk-rnxk3587.js → chunk-sp9cmc7p.js} +1 -1
  80. package/dist/cli/{chunk-yjyjg7sk.js → chunk-stng29vp.js} +2 -2
  81. package/dist/cli/{chunk-80zzjcvp.js → chunk-syqbc3a3.js} +1 -1
  82. package/dist/cli/chunk-t228jyth.js +4 -0
  83. package/dist/cli/{chunk-ya0h6hcd.js → chunk-tj1w7jb7.js} +5 -5
  84. package/dist/cli/{chunk-t60qcvcj.js → chunk-traxwyfz.js} +1 -1
  85. package/dist/cli/chunk-tx4b93j7.js +2 -0
  86. package/dist/cli/{chunk-c8anewjn.js → chunk-tykb6n4m.js} +1 -1
  87. package/dist/cli/{chunk-cqychwvw.js → chunk-vznz1yf0.js} +1 -1
  88. package/dist/cli/{chunk-v7prawj1.js → chunk-wqcs5mz0.js} +1 -1
  89. package/dist/cli/chunk-wv6d96t6.js +2 -0
  90. package/dist/cli/{chunk-tpt3fn1q.js → chunk-xwynw0t0.js} +1 -1
  91. package/dist/cli/{chunk-k2cnmb5q.js → chunk-xywr9nj6.js} +1 -1
  92. package/dist/cli/chunk-y5wt6c1s.js +3 -0
  93. package/dist/cli/{chunk-yaqkajw4.js → chunk-y7nkgnzn.js} +17 -17
  94. package/dist/cli/{chunk-qpc8bv02.js → chunk-yxsec1f1.js} +4 -4
  95. package/dist/cli/chunk-z3qhpthz.js +3 -0
  96. package/dist/cli/{chunk-8q1g1xq0.js → chunk-z5evzj1q.js} +1 -1
  97. package/dist/cli/{chunk-eyzjma6q.js → chunk-zqfvxz1g.js} +1 -1
  98. package/dist/cli/{chunk-1zqzqbek.js → chunk-zsth2jk6.js} +1 -1
  99. package/dist/cli/{chunk-hdnqq66w.js → chunk-zt0dmqn5.js} +1 -1
  100. package/dist/cli/index.js +4 -4
  101. package/dist/cli/pty-host-node.mjs +4 -0
  102. package/dist/cli/rove.js +1 -1
  103. package/dist/completions/rove.bash +2 -2
  104. package/dist/completions/rove.fish +2 -2
  105. package/dist/completions/rove.zsh +2 -2
  106. package/dist/skills/rove/SKILL.md +1 -1
  107. package/dist/skills/rove/references/api-flags.md +28 -11
  108. package/dist/web/assets/index-BLCf4p1g.js +109 -0
  109. package/dist/web/assets/index-CdjFB6v7.css +1 -0
  110. package/dist/web/index.html +2 -2
  111. package/package.json +3 -3
  112. package/dist/cli/chunk-1378mh2w.js +0 -3
  113. package/dist/cli/chunk-1qge84t7.js +0 -3
  114. package/dist/cli/chunk-1vxykwb9.js +0 -3
  115. package/dist/cli/chunk-2yjpqb47.js +0 -3
  116. package/dist/cli/chunk-4ab8y1yp.js +0 -15
  117. package/dist/cli/chunk-4d4d59vp.js +0 -20
  118. package/dist/cli/chunk-658859k9.js +0 -31
  119. package/dist/cli/chunk-6t5d02s4.js +0 -2
  120. package/dist/cli/chunk-79r3wbhh.js +0 -2
  121. package/dist/cli/chunk-7nr405tr.js +0 -9
  122. package/dist/cli/chunk-82zpp3yx.js +0 -4
  123. package/dist/cli/chunk-8dggnfaf.js +0 -2
  124. package/dist/cli/chunk-csdne12x.js +0 -3
  125. package/dist/cli/chunk-dd0mtzje.js +0 -3
  126. package/dist/cli/chunk-fehxbwcb.js +0 -6
  127. package/dist/cli/chunk-g846fbp7.js +0 -52
  128. package/dist/cli/chunk-hkv6t5v9.js +0 -2
  129. package/dist/cli/chunk-jetsx9zr.js +0 -3
  130. package/dist/cli/chunk-q301ewyq.js +0 -2
  131. package/dist/cli/chunk-qn04z4zb.js +0 -2
  132. package/dist/cli/chunk-rmk69hvh.js +0 -2
  133. package/dist/cli/chunk-s1rg1zae.js +0 -3
  134. package/dist/cli/chunk-skpkscne.js +0 -11
  135. package/dist/cli/chunk-x7cv6deh.js +0 -2
  136. package/dist/cli/chunk-y85e24s9.js +0 -2
  137. package/dist/cli/chunk-yc9bytrj.js +0 -2
  138. package/dist/web/assets/index-BiUiNIOp.css +0 -1
  139. package/dist/web/assets/index-jy27XaL0.js +0 -109
@@ -1,52 +0,0 @@
1
- // @bun
2
- import{MachineHub}from"./chunk-s1rg1zae.js";import{COLORBLIND_KEY,DEFAULT_BASE_REF,DEFAULT_COLORBLIND,DEFAULT_GLYPHS,DEFAULT_GLYPH_SET,DEFAULT_PREFIX_TAP_PRESENTATION,DEFAULT_RUNNING_TITLE,DEFAULT_SCROLLBACK_ROWS,DEFAULT_TAB_ROW_HEIGHT,DEFAULT_TASK_COLORS,DEFAULT_WORKING_BORDER,GLYPH_SETS,GLYPH_SET_KEY,PREFIX_TAP_PRESENTATIONS,PREFIX_TAP_PRESENTATION_KEY,RAIL_FOLD_STYLE_KEY,RUNNING_TITLE_KEY,SCROLLBACK_ROWS_KEY,SCROLLBACK_ROWS_MAX,SCROLLBACK_ROWS_MIN,SPLIT_STYLE_KEY,TAB_ROW_HEIGHT_KEY,TAB_STRIP_HIDE_SINGLE_KEY,TAB_STRIP_MODES,TAB_STRIP_MODE_KEY,TASK_COLORS_KEY,WORKING_BORDER_KEY,ZEN_ACTIVE_KEY,ZEN_DEFAULT,appearanceChoices,buildCIPromptForWorktree,buildPRPrompt,cloneRepo,createKeybindingsFile,deriveFolderName,detailFragment,detectWorktreeOpener,diffCommentsKey,displayEndpoint,engineIntegrations,findAvailableFolderName,formatDiffComments,getCurrentBranch,issueChatTaskTitle,issueProjectPrompt,issueWorktreePrompt,listLocalBranches,liveSourceProtocol,normalizeColorblind,normalizeGlyphSet,normalizePrefixTapPresentation,normalizeRunningTitle,normalizeScrollbackRows,normalizeSplitStyle,normalizeTabRowHeight,normalizeTaskColors,normalizeWorkingBorder,openExternally,openWorktree,planWorktreeHandoff,prChip,readPluginRows,resolveCloneTarget,resolveTabStripMode,setPluginEnabled,spawnDetached,taskColor,unsentComments,validateCloneTarget,validateGitUrl,validateRepoPath}from"./chunk-qpc8bv02.js";import"./chunk-rnxk3587.js";import"./chunk-1zqzqbek.js";import{startFileWatchTrigger}from"./chunk-r3s2t9af.js";import"./chunk-dqprpnd6.js";import{DEFAULT_SOUND_VOLUME,RoveKeymap,SOUND_VOLUME_KEY,SOUND_VOLUME_STEPS,normalizeSoundVolume,prefixFirstStrokes,readKeybindingsFile,resetKeybindingsFileCache,resolveUserKeybindings}from"./chunk-65v8ga90.js";import{RemoteOrchestrator,liveRowTokens}from"./chunk-dd0mtzje.js";import{invokeVerb}from"./chunk-fehxbwcb.js";import{argvToParams,executeVerb}from"./chunk-9v18qy3s.js";import{buildContext,defaultApiRuntime,worktreeDiff}from"./chunk-4ab8y1yp.js";import{AUTO_ROUTING_TIERS,autoRoutingKey,describeTierBlock,listFiles,loadPreviewData,readAutoRoutingTable,readClassifierConfig,resolveBase,secretStatus,tierBlock}from"./chunk-tpt3fn1q.js";import{ensurePluginEnginesLoaded}from"./chunk-t60qcvcj.js";import"./chunk-6p9yxt3q.js";import{listPaneLaunches}from"./chunk-rc2nwsdd.js";import"./chunk-cbc1h345.js";import{PROJECT_SIBLING_BASE,WORKTREE_BASE_CUSTOM_KEY,worktreeBaseKindOf}from"./chunk-jjv7xem7.js";import"./chunk-17e2tgyg.js";import{detectEngineStatus,detectEngineStatuses,probeableEngineIds,summarizeEngines}from"./chunk-68113ts1.js";import"./chunk-4hestsjp.js";import{WORKTREE_BASE_KEY}from"./chunk-41x1hrqe.js";import"./chunk-5mhvjytn.js";import"./chunk-stcvcsfm.js";import"./chunk-2bgmby1d.js";import{loadPluginRegistry}from"./chunk-edsm15ht.js";import{DEFAULT_EDITOR_KIND,EDITOR_CUSTOM_KEY,EDITOR_KINDS,EDITOR_KIND_KEY,normalizeEditorKind}from"./chunk-j11hzmwy.js";import"./chunk-atn06ncf.js";import"./chunk-cq5qv93a.js";import"./chunk-qkehg2c1.js";import"./chunk-csdne12x.js";import{expandHome,filterSubdirs,initialTabs,listSubdirs,moveTab,splitPathForDirSuggest,stripTrailingSlash,terminalTabsKey}from"./chunk-3q1rv3rm.js";import{AUTO_STATUS_KEY,DISPATCHER_KEY,resolveThemeSlotHex}from"./chunk-sabpcket.js";import"./chunk-ampdybqg.js";import"./chunk-sak1tphp.js";import"./chunk-4250pw6r.js";import{defaultEngineCommand,engineDisplayName,ensurePtyHostReachable,protocolEntry,roveApiInvocation}from"./chunk-82zpp3yx.js";import"./chunk-rxqdw6s5.js";import{DEFAULT_THEME,DEFAULT_THEME_MODE,FOCUS_ACCENT_SLOTS,THEME_MODE_PREFERENCES,defaultTransparentBackground,diffInks,resolveTheme}from"./chunk-jetsx9zr.js";import"./chunk-f5n0tgky.js";import{BUNDLED_THEME_JSONS,loadUserThemes}from"./chunk-z3zaa155.js";import"./chunk-yewmtant.js";import"./chunk-hpay0ax8.js";import"./chunk-catrr0z3.js";import"./chunk-hdnqq66w.js";import{engineEntry}from"./chunk-b47k0cnw.js";import"./chunk-2tfnscxk.js";import{installedEngineIds}from"./chunk-9ecnsh42.js";import"./chunk-w7zv47mp.js";import"./chunk-etfh8f4z.js";import{resolvePreferredVendor}from"./chunk-rwx1y497.js";import"./chunk-55ajzrj6.js";import"./chunk-t7j8g9f6.js";import"./chunk-dmzg9zdc.js";import"./chunk-kg9kmwt9.js";import"./chunk-6xppe9bj.js";import{forwardVerb,invokeRouted,machineDaemonSocket,requireMachine}from"./chunk-2yjpqb47.js";import"./chunk-1vxykwb9.js";import"./chunk-7nr405tr.js";import{localPtySocketPath}from"./chunk-1378mh2w.js";import"./chunk-b7xjhgts.js";import{ApiError,REMOTE_CALLER}from"./chunk-x7r75e3p.js";import"./chunk-zsfyj36g.js";import{ensureDaemonReachable}from"./chunk-kqf371ff.js";import"./chunk-1vwngrmf.js";import"./chunk-yktnbb14.js";import"./chunk-ag3txfg2.js";import"./chunk-2brn8e1y.js";import"./chunk-t25xke74.js";import{roveCliInvocation}from"./chunk-7zyyvc3d.js";import"./chunk-r6k3ve2g.js";import"./chunk-xy8be792.js";import"./chunk-7kysxgbq.js";import{ALL_VENDORS,isBuiltinVendor}from"./chunk-h2m7em1e.js";import"./chunk-7ghwdjdc.js";import"./chunk-tdhk4dz8.js";import{CURRENT_VERSION,checkLatestVersion,fetchReleaseNotes,fetchReleaseNotesRange}from"./chunk-1xrbb7gf.js";import{getSavedRepos,resolveMainRepoRoot}from"./chunk-22d0yc7m.js";import"./chunk-40dgv828.js";import"./chunk-3342qzve.js";import{loadStateFile,patchStateFile,replaceStateFile,updateStateFile}from"./chunk-g4281a0g.js";import"./chunk-zh0c8yzw.js";import"./chunk-wqckm0k6.js";import{defaultUiPrefsStatePath,promptAttachmentsDir,resolveProductHomeDir,roveStateDir}from"./chunk-prqeddy7.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-tq6pq1ws.js";import{DEFAULT_LOCALE,LOCALES,isLocaleId}from"./chunk-sma961wh.js";import"./chunk-cmp64q4j.js";import{RoveDaemonClient,isDaemonVersionStale}from"./chunk-s0jbtyd9.js";import{MOD_INPUT_MAX,MOD_KEY_PATTERN,MOD_PANE_ID_PATTERN,ROVE_API_VERSION}from"./chunk-278sc87n.js";import"./chunk-gskbzgc3.js";import{samePath}from"./chunk-pn5ygqn6.js";import{defaultDaemonSocketPath,defaultPtyHostSocketPath}from"./chunk-n0rtj4z7.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import"./chunk-pxk2c2vj.js";import{__require}from"./chunk-91sykzqq.js";import{spawnSync}from"child_process";import{existsSync as existsSync3,readFileSync as readFileSync2}from"fs";import{join as join4}from"path";var QrCodeDataType=((QrCodeDataType2)=>{return QrCodeDataType2[QrCodeDataType2.Border=-1]="Border",QrCodeDataType2[QrCodeDataType2.Data=0]="Data",QrCodeDataType2[QrCodeDataType2.Function=1]="Function",QrCodeDataType2[QrCodeDataType2.Position=2]="Position",QrCodeDataType2[QrCodeDataType2.Timing=3]="Timing",QrCodeDataType2[QrCodeDataType2.Alignment=4]="Alignment",QrCodeDataType2})(QrCodeDataType||{}),LOW=[0,1],MEDIUM=[1,0],QUARTILE=[2,3],HIGH=[3,2],EccMap={L:LOW,M:MEDIUM,Q:QUARTILE,H:HIGH},NUMERIC_REGEX=/^\d*$/,ALPHANUMERIC_REGEX=/^[A-Z0-9 $%*+./:-]*$/,ALPHANUMERIC_CHARSET="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:",MIN_VERSION=1,MAX_VERSION=40,PENALTY_N1=3,PENALTY_N2=3,PENALTY_N3=40,PENALTY_N4=10,ECC_CODEWORDS_PER_BLOCK=[[-1,7,10,15,20,26,18,20,24,30,18,20,24,26,30,22,24,28,30,28,28,28,28,30,30,26,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,10,16,26,18,24,16,18,22,22,26,30,22,22,24,24,28,28,26,26,26,26,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28],[-1,13,22,18,26,18,24,18,22,20,24,28,26,24,20,30,24,28,28,26,30,28,30,30,30,30,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,17,28,22,16,22,28,26,26,24,28,24,28,22,24,24,30,28,28,26,28,30,24,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30]],NUM_ERROR_CORRECTION_BLOCKS=[[-1,1,1,1,1,1,2,2,2,2,4,4,4,4,4,6,6,6,6,7,8,8,9,9,10,12,12,12,13,14,15,16,17,18,19,19,20,21,22,24,25],[-1,1,1,1,2,2,4,4,4,5,5,5,8,9,9,10,10,11,13,14,16,17,17,18,20,21,23,25,26,28,29,31,33,35,37,38,40,43,45,47,49],[-1,1,1,2,2,4,4,6,6,8,8,8,10,12,16,12,17,16,18,21,20,23,23,25,27,29,34,34,35,38,40,43,45,48,51,53,56,59,62,65,68],[-1,1,1,2,4,4,4,5,6,8,8,11,11,16,16,18,16,19,21,25,25,25,34,30,32,35,37,40,42,45,48,51,54,57,60,63,66,70,74,77,81]];class QrCode{constructor(version,ecc,dataCodewords,msk){if(this.version=version,this.ecc=ecc,version<MIN_VERSION||version>MAX_VERSION)throw RangeError("Version value out of range");if(msk<-1||msk>7)throw RangeError("Mask value out of range");this.size=version*4+17;let row=Array.from({length:this.size}).fill(!1);for(let i=0;i<this.size;i++)this.modules.push(row.slice()),this.types.push(row.map(()=>0));this.drawFunctionPatterns();let allCodewords=this.addEccAndInterleave(dataCodewords);if(this.drawCodewords(allCodewords),msk===-1){let minPenalty=1e9;for(let i=0;i<8;i++){this.applyMask(i),this.drawFormatBits(i);let penalty=this.getPenaltyScore();if(penalty<minPenalty)msk=i,minPenalty=penalty;this.applyMask(i)}}this.mask=msk,this.applyMask(msk),this.drawFormatBits(msk)}size;mask;modules=[];types=[];getModule(x,y){return x>=0&&x<this.size&&y>=0&&y<this.size&&this.modules[y][x]}drawFunctionPatterns(){for(let i=0;i<this.size;i++)this.setFunctionModule(6,i,i%2===0,QrCodeDataType.Timing),this.setFunctionModule(i,6,i%2===0,QrCodeDataType.Timing);this.drawFinderPattern(3,3),this.drawFinderPattern(this.size-4,3),this.drawFinderPattern(3,this.size-4);let alignPatPos=this.getAlignmentPatternPositions(),numAlign=alignPatPos.length;for(let i=0;i<numAlign;i++)for(let j=0;j<numAlign;j++)if(!(i===0&&j===0||i===0&&j===numAlign-1||i===numAlign-1&&j===0))this.drawAlignmentPattern(alignPatPos[i],alignPatPos[j]);this.drawFormatBits(0),this.drawVersion()}drawFormatBits(mask){let data=this.ecc[1]<<3|mask,rem=data;for(let i=0;i<10;i++)rem=rem<<1^(rem>>>9)*1335;let bits=(data<<10|rem)^21522;for(let i=0;i<=5;i++)this.setFunctionModule(8,i,getBit(bits,i));this.setFunctionModule(8,7,getBit(bits,6)),this.setFunctionModule(8,8,getBit(bits,7)),this.setFunctionModule(7,8,getBit(bits,8));for(let i=9;i<15;i++)this.setFunctionModule(14-i,8,getBit(bits,i));for(let i=0;i<8;i++)this.setFunctionModule(this.size-1-i,8,getBit(bits,i));for(let i=8;i<15;i++)this.setFunctionModule(8,this.size-15+i,getBit(bits,i));this.setFunctionModule(8,this.size-8,!0)}drawVersion(){if(this.version<7)return;let rem=this.version;for(let i=0;i<12;i++)rem=rem<<1^(rem>>>11)*7973;let bits=this.version<<12|rem;for(let i=0;i<18;i++){let color=getBit(bits,i),a=this.size-11+i%3,b=Math.floor(i/3);this.setFunctionModule(a,b,color),this.setFunctionModule(b,a,color)}}drawFinderPattern(x,y){for(let dy=-4;dy<=4;dy++)for(let dx=-4;dx<=4;dx++){let dist=Math.max(Math.abs(dx),Math.abs(dy)),xx=x+dx,yy=y+dy;if(xx>=0&&xx<this.size&&yy>=0&&yy<this.size)this.setFunctionModule(xx,yy,dist!==2&&dist!==4,QrCodeDataType.Position)}}drawAlignmentPattern(x,y){for(let dy=-2;dy<=2;dy++)for(let dx=-2;dx<=2;dx++)this.setFunctionModule(x+dx,y+dy,Math.max(Math.abs(dx),Math.abs(dy))!==1,QrCodeDataType.Alignment)}setFunctionModule(x,y,isDark,type=QrCodeDataType.Function){this.modules[y][x]=isDark,this.types[y][x]=type}addEccAndInterleave(data){let ver=this.version,ecl=this.ecc;if(data.length!==getNumDataCodewords(ver,ecl))throw RangeError("Invalid argument");let numBlocks=NUM_ERROR_CORRECTION_BLOCKS[ecl[0]][ver],blockEccLen=ECC_CODEWORDS_PER_BLOCK[ecl[0]][ver],rawCodewords=Math.floor(getNumRawDataModules(ver)/8),numShortBlocks=numBlocks-rawCodewords%numBlocks,shortBlockLen=Math.floor(rawCodewords/numBlocks),blocks=[],rsDiv=reedSolomonComputeDivisor(blockEccLen);for(let i=0,k=0;i<numBlocks;i++){let dat=data.slice(k,k+shortBlockLen-blockEccLen+(i<numShortBlocks?0:1));k+=dat.length;let ecc=reedSolomonComputeRemainder(dat,rsDiv);if(i<numShortBlocks)dat.push(0);blocks.push(dat.concat(ecc))}let result=[];for(let i=0;i<blocks[0].length;i++)blocks.forEach((block,j)=>{if(i!==shortBlockLen-blockEccLen||j>=numShortBlocks)result.push(block[i])});return result}drawCodewords(data){if(data.length!==Math.floor(getNumRawDataModules(this.version)/8))throw RangeError("Invalid argument");let i=0;for(let right=this.size-1;right>=1;right-=2){if(right===6)right=5;for(let vert=0;vert<this.size;vert++)for(let j=0;j<2;j++){let x=right-j,y=(right+1&2)===0?this.size-1-vert:vert;if(!this.types[y][x]&&i<data.length*8)this.modules[y][x]=getBit(data[i>>>3],7-(i&7)),i++}}}applyMask(mask){if(mask<0||mask>7)throw RangeError("Mask value out of range");for(let y=0;y<this.size;y++)for(let x=0;x<this.size;x++){let invert;switch(mask){case 0:invert=(x+y)%2===0;break;case 1:invert=y%2===0;break;case 2:invert=x%3===0;break;case 3:invert=(x+y)%3===0;break;case 4:invert=(Math.floor(x/3)+Math.floor(y/2))%2===0;break;case 5:invert=x*y%2+x*y%3===0;break;case 6:invert=(x*y%2+x*y%3)%2===0;break;case 7:invert=((x+y)%2+x*y%3)%2===0;break;default:throw Error("Unreachable")}if(!this.types[y][x]&&invert)this.modules[y][x]=!this.modules[y][x]}}getPenaltyScore(){let result=0;for(let y=0;y<this.size;y++){let runColor=!1,runX=0,runHistory=[0,0,0,0,0,0,0];for(let x=0;x<this.size;x++)if(this.modules[y][x]===runColor){if(runX++,runX===5)result+=PENALTY_N1;else if(runX>5)result++}else{if(this.finderPenaltyAddHistory(runX,runHistory),!runColor)result+=this.finderPenaltyCountPatterns(runHistory)*PENALTY_N3;runColor=this.modules[y][x],runX=1}result+=this.finderPenaltyTerminateAndCount(runColor,runX,runHistory)*PENALTY_N3}for(let x=0;x<this.size;x++){let runColor=!1,runY=0,runHistory=[0,0,0,0,0,0,0];for(let y=0;y<this.size;y++)if(this.modules[y][x]===runColor){if(runY++,runY===5)result+=PENALTY_N1;else if(runY>5)result++}else{if(this.finderPenaltyAddHistory(runY,runHistory),!runColor)result+=this.finderPenaltyCountPatterns(runHistory)*PENALTY_N3;runColor=this.modules[y][x],runY=1}result+=this.finderPenaltyTerminateAndCount(runColor,runY,runHistory)*PENALTY_N3}for(let y=0;y<this.size-1;y++)for(let x=0;x<this.size-1;x++){let color=this.modules[y][x];if(color===this.modules[y][x+1]&&color===this.modules[y+1][x]&&color===this.modules[y+1][x+1])result+=PENALTY_N2}let dark=0;for(let row of this.modules)dark=row.reduce((sum,color)=>sum+(color?1:0),dark);let total=this.size*this.size,k=Math.ceil(Math.abs(dark*20-total*10)/total)-1;return result+=k*PENALTY_N4,result}getAlignmentPatternPositions(){if(this.version===1)return[];else{let numAlign=Math.floor(this.version/7)+2,step=this.version===32?26:Math.ceil((this.version*4+4)/(numAlign*2-2))*2,result=[6];for(let pos=this.size-7;result.length<numAlign;pos-=step)result.splice(1,0,pos);return result}}finderPenaltyCountPatterns(runHistory){let n=runHistory[1],core=n>0&&runHistory[2]===n&&runHistory[3]===n*3&&runHistory[4]===n&&runHistory[5]===n;return(core&&runHistory[0]>=n*4&&runHistory[6]>=n?1:0)+(core&&runHistory[6]>=n*4&&runHistory[0]>=n?1:0)}finderPenaltyTerminateAndCount(currentRunColor,currentRunLength,runHistory){if(currentRunColor)this.finderPenaltyAddHistory(currentRunLength,runHistory),currentRunLength=0;return currentRunLength+=this.size,this.finderPenaltyAddHistory(currentRunLength,runHistory),this.finderPenaltyCountPatterns(runHistory)}finderPenaltyAddHistory(currentRunLength,runHistory){if(runHistory[0]===0)currentRunLength+=this.size;runHistory.pop(),runHistory.unshift(currentRunLength)}}function appendBits(val,len,bb){if(len<0||len>31||val>>>len!==0)throw RangeError("Value out of range");for(let i=len-1;i>=0;i--)bb.push(val>>>i&1)}function getBit(x,i){return(x>>>i&1)!==0}class QrSegment{constructor(mode,numChars,bitData){if(this.mode=mode,this.numChars=numChars,this.bitData=bitData,numChars<0)throw RangeError("Invalid argument");this.bitData=bitData.slice()}getData(){return this.bitData.slice()}}var MODE_NUMERIC=[1,10,12,14],MODE_ALPHANUMERIC=[2,9,11,13],MODE_BYTE=[4,8,16,16];function numCharCountBits(mode,ver){return mode[Math.floor((ver+7)/17)+1]}function makeBytes(data){let bb=[];for(let b of data)appendBits(b,8,bb);return new QrSegment(MODE_BYTE,data.length,bb)}function makeNumeric(digits){if(!isNumeric(digits))throw RangeError("String contains non-numeric characters");let bb=[];for(let i=0;i<digits.length;){let n=Math.min(digits.length-i,3);appendBits(Number.parseInt(digits.substring(i,i+n),10),n*3+1,bb),i+=n}return new QrSegment(MODE_NUMERIC,digits.length,bb)}function makeAlphanumeric(text){if(!isAlphanumeric(text))throw RangeError("String contains unencodable characters in alphanumeric mode");let bb=[],i;for(i=0;i+2<=text.length;i+=2){let temp=ALPHANUMERIC_CHARSET.indexOf(text.charAt(i))*45;temp+=ALPHANUMERIC_CHARSET.indexOf(text.charAt(i+1)),appendBits(temp,11,bb)}if(i<text.length)appendBits(ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)),6,bb);return new QrSegment(MODE_ALPHANUMERIC,text.length,bb)}function makeSegments(text){if(text==="")return[];else if(isNumeric(text))return[makeNumeric(text)];else if(isAlphanumeric(text))return[makeAlphanumeric(text)];else return[makeBytes(toUtf8ByteArray(text))]}function isNumeric(text){return NUMERIC_REGEX.test(text)}function isAlphanumeric(text){return ALPHANUMERIC_REGEX.test(text)}function getTotalBits(segs,version){let result=0;for(let seg of segs){let ccbits=numCharCountBits(seg.mode,version);if(seg.numChars>=1<<ccbits)return Number.POSITIVE_INFINITY;result+=4+ccbits+seg.bitData.length}return result}function toUtf8ByteArray(str){str=encodeURI(str);let result=[];for(let i=0;i<str.length;i++)if(str.charAt(i)!=="%")result.push(str.charCodeAt(i));else result.push(Number.parseInt(str.substring(i+1,i+3),16)),i+=2;return result}function getNumRawDataModules(ver){if(ver<MIN_VERSION||ver>MAX_VERSION)throw RangeError("Version number out of range");let result=(16*ver+128)*ver+64;if(ver>=2){let numAlign=Math.floor(ver/7)+2;if(result-=(25*numAlign-10)*numAlign-55,ver>=7)result-=36}return result}function getNumDataCodewords(ver,ecl){return Math.floor(getNumRawDataModules(ver)/8)-ECC_CODEWORDS_PER_BLOCK[ecl[0]][ver]*NUM_ERROR_CORRECTION_BLOCKS[ecl[0]][ver]}function reedSolomonComputeDivisor(degree){if(degree<1||degree>255)throw RangeError("Degree out of range");let result=[];for(let i=0;i<degree-1;i++)result.push(0);result.push(1);let root=1;for(let i=0;i<degree;i++){for(let j=0;j<result.length;j++)if(result[j]=reedSolomonMultiply(result[j],root),j+1<result.length)result[j]^=result[j+1];root=reedSolomonMultiply(root,2)}return result}function reedSolomonComputeRemainder(data,divisor){let result=divisor.map((_)=>0);for(let b of data){let factor=b^result.shift();result.push(0),divisor.forEach((coef,i)=>result[i]^=reedSolomonMultiply(coef,factor))}return result}function reedSolomonMultiply(x,y){if(x>>>8!==0||y>>>8!==0)throw RangeError("Byte out of range");let z=0;for(let i=7;i>=0;i--)z=z<<1^(z>>>7)*285,z^=(y>>>i&1)*x;return z}function encodeSegments(segs,ecl,minVersion=1,maxVersion=40,mask=-1,boostEcl=!0){if(!(MIN_VERSION<=minVersion&&minVersion<=maxVersion&&maxVersion<=MAX_VERSION)||mask<-1||mask>7)throw RangeError("Invalid value");let version,dataUsedBits;for(version=minVersion;;version++){let dataCapacityBits2=getNumDataCodewords(version,ecl)*8,usedBits=getTotalBits(segs,version);if(usedBits<=dataCapacityBits2){dataUsedBits=usedBits;break}if(version>=maxVersion)throw RangeError("Data too long")}for(let newEcl of[MEDIUM,QUARTILE,HIGH])if(boostEcl&&dataUsedBits<=getNumDataCodewords(version,newEcl)*8)ecl=newEcl;let bb=[];for(let seg of segs){appendBits(seg.mode[0],4,bb),appendBits(seg.numChars,numCharCountBits(seg.mode,version),bb);for(let b of seg.getData())bb.push(b)}let dataCapacityBits=getNumDataCodewords(version,ecl)*8;appendBits(0,Math.min(4,dataCapacityBits-bb.length),bb),appendBits(0,(8-bb.length%8)%8,bb);for(let padByte=236;bb.length<dataCapacityBits;padByte^=253)appendBits(padByte,8,bb);let dataCodewords=Array.from({length:Math.ceil(bb.length/8)},()=>0);return bb.forEach((b,i)=>dataCodewords[i>>>3]|=b<<7-(i&7)),new QrCode(version,ecl,dataCodewords,mask)}function encode(data,options){let{ecc="L",boostEcc=!1,minVersion=1,maxVersion=40,maskPattern=-1,border=1}=options||{},segment=typeof data==="string"?makeSegments(data):Array.isArray(data)?[makeBytes(data)]:void 0;if(!segment)throw Error(`uqr only supports encoding string and binary data, but got: ${typeof data}`);let qr=encodeSegments(segment,EccMap[ecc],minVersion,maxVersion,maskPattern,boostEcc),result=addBorder({version:qr.version,maskPattern:qr.mask,size:qr.size,data:qr.modules,types:qr.types},border);if(options?.invert)result.data=result.data.map((row)=>row.map((mod)=>!mod));return options?.onEncoded?.(result),result}function addBorder(input,border=1){if(!border)return input;let{size}=input,newSize=size+border*2;input.size=newSize,input.data.forEach((row)=>{for(let i=0;i<border;i++)row.unshift(!1),row.push(!1)});for(let i=0;i<border;i++)input.data.unshift(Array.from({length:newSize},(_)=>!1)),input.data.push(Array.from({length:newSize},(_)=>!1));let b=QrCodeDataType.Border;input.types.forEach((row)=>{for(let i=0;i<border;i++)row.unshift(b),row.push(b)});for(let i=0;i<border;i++)input.types.unshift(Array.from({length:newSize},(_)=>b)),input.types.push(Array.from({length:newSize},(_)=>b));return input}function getDataAt(data,x,y,defaults=!1){if(x<0||y<0||x>=data.length||y>=data.length)return defaults;return data[y][x]}function renderUnicodeCompact(data,options={}){let palette={WHITE_ALL:"\u2588",WHITE_BLACK:"\u2580",BLACK_WHITE:"\u2584",BLACK_ALL:" "},result=encode(data,options),WHITE=!1,BLACK=!0,at=(x,y)=>getDataAt(result.data,x,y,!0),lines=[],line="";for(let row=0;row<result.size;row+=2){for(let col=0;col<result.size;col++)if(at(col,row)===!1&&at(col,row+1)===!1)line+=palette.WHITE_ALL;else if(at(col,row)===!1&&at(col,row+1)===!0)line+=palette.WHITE_BLACK;else if(at(col,row)===!0&&at(col,row+1)===!1)line+=palette.BLACK_WHITE;else line+=palette.BLACK_ALL;lines.push(line),line=""}return lines.join(`
3
- `)}import{randomBytes,timingSafeEqual}from"crypto";import{chmodSync,existsSync,mkdirSync,readFileSync,writeFileSync}from"fs";import{dirname}from"path";function loadOrCreateToken(path,rotate=!1){if(!rotate&&existsSync(path)){let saved=readFileSync(path,"utf8").trim();if(/^[A-Za-z0-9_-]{43}$/.test(saved))return saved}let token=randomBytes(32).toString("base64url");return mkdirSync(dirname(path),{recursive:!0,mode:448}),writeFileSync(path,`${token}
4
- `,{mode:384}),chmodSync(path,384),token}function tokenMatches(expected,presented){if(!presented)return!1;let a=Buffer.from(expected),b=Buffer.from(presented);if(a.length!==b.length)return!1;return timingSafeEqual(a,b)}var WEB_SUBPROTOCOL="rove.v1",TOKEN_SUBPROTOCOL_PREFIX="rove.bearer.";function presentedToken(req){let header=req.headers.get("authorization");if(header?.startsWith("Bearer "))return header.slice(7).trim();for(let offered of(req.headers.get("sec-websocket-protocol")??"").split(",")){let name=offered.trim();if(name.startsWith(TOKEN_SUBPROTOCOL_PREFIX))return name.slice(TOKEN_SUBPROTOCOL_PREFIX.length)}return null}function offersWebSubprotocol(req){return(req.headers.get("sec-websocket-protocol")??"").split(",").some((p)=>p.trim()===WEB_SUBPROTOCOL)}function webUrl(opts){let shown=opts.host.includes(":")?`[${opts.host}]`:opts.host;return`${opts.tls?`https://${shown}`:`http://${shown}:${opts.port}`}/#token=${opts.token}`}function pairingUrl(opts){let shown=opts.host.includes(":")?`[${opts.host}]`:opts.host,origin=opts.tls?`wss://${shown}`:`ws://${shown}:${opts.port}`,preset=opts.preset==="none"?"":`&preset=${opts.preset}`;return`${origin}/?token=${opts.token}${preset}`}import{createPublicKey,verify}from"crypto";class AccessDenied extends Error{constructor(reason){super(reason);this.name="AccessDenied"}}var fetchJwksOverHttps=async(url)=>{let res=await fetch(url,{signal:AbortSignal.timeout(1e4)});if(!res.ok)throw Error(`GET ${url} \u2192 ${res.status}`);let body=await res.json();if(!body||typeof body!=="object"||!("keys"in body)||!Array.isArray(body.keys))throw Error(`${url} did not return a JWKS`);return{keys:body.keys}},REFETCH_MIN_MS=60000,LEEWAY_S=30;function decodeSegment(segment,what){try{let value=JSON.parse(Buffer.from(segment,"base64url").toString("utf8"));if(value&&typeof value==="object"&&!Array.isArray(value))return value}catch{}throw new AccessDenied(`malformed JWT ${what}`)}function createAccessVerifier(access,deps={}){let fetchJwks=deps.fetchJwks??fetchJwksOverHttps,now=deps.now??Date.now,certsUrl=`${access.teamDomain}/cdn-cgi/access/certs`,keys=new Map,fetchedAt=Number.NEGATIVE_INFINITY,refresh=async()=>{fetchedAt=now();let jwks=await fetchJwks(certsUrl),next=new Map;for(let jwk of jwks.keys){if(jwk.kty!=="RSA"||!jwk.kid)continue;next.set(jwk.kid,createPublicKey({key:jwk,format:"jwk"}))}keys=next},keyFor=async(kid)=>{let key=keys.get(kid);if(!key&&now()-fetchedAt>=REFETCH_MIN_MS)await refresh(),key=keys.get(kid);if(!key)throw new AccessDenied(`no signing key ${kid} at ${certsUrl}`);return key};return{async verify(assertion){if(!assertion)throw new AccessDenied("missing Cf-Access-Jwt-Assertion header");let parts=assertion.split(".");if(parts.length!==3)throw new AccessDenied("malformed JWT");let[h,p,s]=parts,header=decodeSegment(h,"header");if(header.alg!=="RS256")throw new AccessDenied(`unexpected JWT alg ${String(header.alg)}`);if(typeof header.kid!=="string")throw new AccessDenied("JWT has no kid");let key=await keyFor(header.kid);if(!verify("RSA-SHA256",Buffer.from(`${h}.${p}`),key,Buffer.from(s,"base64url")))throw new AccessDenied("bad JWT signature");let claims=decodeSegment(p,"payload");if(claims.iss!==access.teamDomain)throw new AccessDenied(`JWT issuer ${String(claims.iss)} is not ${access.teamDomain}`);if(!(Array.isArray(claims.aud)?claims.aud:[claims.aud]).includes(access.aud))throw new AccessDenied("JWT audience does not match this Access application");let nowS=now()/1000;if(typeof claims.exp!=="number"||claims.exp+LEEWAY_S<nowS)throw new AccessDenied("JWT expired");if(typeof claims.nbf==="number"&&claims.nbf-LEEWAY_S>nowS)throw new AccessDenied("JWT not yet valid")}}}function teamDomainOf(team){let bare=team.trim().replace(/^https?:\/\//,"").replace(/\/.*$/,"");if(!/^[A-Za-z0-9-]+(\.cloudflareaccess\.com)?$/.test(bare))throw Error(`--cf-team must be a team name or <team>.cloudflareaccess.com, got ${team}`);return`https://${bare.endsWith(".cloudflareaccess.com")?bare:`${bare}.cloudflareaccess.com`}`}function parseBridgeArgs(argv,deps={}){let config=deps.config??{},hosts=[],preset="none",port=7878,publicHost=null,cfTeam=config.cfTeam,cfAud=config.cfAud,rotateToken=!1,qr=!0;for(let i=0;i<argv.length;i++){let arg=argv[i],value=()=>{let v=argv[++i];if(v===void 0||v.startsWith("--"))throw Error(`${arg} needs a value
5
- usage: rove-bridge [--preset tailscale|cf] [--host <addr>]... [--port <n>]
6
- [--public-host <name>] [--cf-team <team>] [--cf-aud <aud>]
7
- [--rotate-token] [--no-qr]
8
-
9
- (no preset) listen on 127.0.0.1 only (or each --host you name)
10
- --preset tailscale listen on this Mac's Tailscale IPv4 only; fails if Tailscale
11
- is not up. With --public-host <name> (a \`tailscale serve\`
12
- HTTPS name) the phone dials wss://<name>.
13
- --preset cf listen on 127.0.0.1 for cloudflared; every connection must
14
- carry a valid Cloudflare Access JWT. Needs --public-host
15
- (the tunnel hostname), --cf-team and --cf-aud.
16
- --port <n> TCP port (default 7878)
17
- --rotate-token mint a new pairing token, revoking every paired phone
18
- --no-qr print the pairing URL without a QR code
19
-
20
- --cf-team, --cf-aud and --public-host may also live in
21
- <ROVE_HOME>/.rove/bridge/config.json as cfTeam, cfAud, publicHost.`);return v};if(arg==="--host")hosts.push(value());else if(arg==="--preset"){let p=value();if(p==="tailscale")preset="tailscale";else if(p==="cf"||p==="cloudflare")preset="cf";else throw Error(`--preset must be tailscale or cf, got ${p}`)}else if(arg==="--port"){let raw=value();if(port=Number(raw),!Number.isInteger(port)||port<1||port>65535)throw Error(`--port must be 1..65535, got ${raw}`)}else if(arg==="--public-host")publicHost=value();else if(arg==="--cf-team")cfTeam=value();else if(arg==="--cf-aud")cfAud=value();else if(arg==="--rotate-token")rotateToken=!0;else if(arg==="--no-qr")qr=!1;else throw Error(`unknown argument ${arg}
22
- usage: rove-bridge [--preset tailscale|cf] [--host <addr>]... [--port <n>]
23
- [--public-host <name>] [--cf-team <team>] [--cf-aud <aud>]
24
- [--rotate-token] [--no-qr]
25
-
26
- (no preset) listen on 127.0.0.1 only (or each --host you name)
27
- --preset tailscale listen on this Mac's Tailscale IPv4 only; fails if Tailscale
28
- is not up. With --public-host <name> (a \`tailscale serve\`
29
- HTTPS name) the phone dials wss://<name>.
30
- --preset cf listen on 127.0.0.1 for cloudflared; every connection must
31
- carry a valid Cloudflare Access JWT. Needs --public-host
32
- (the tunnel hostname), --cf-team and --cf-aud.
33
- --port <n> TCP port (default 7878)
34
- --rotate-token mint a new pairing token, revoking every paired phone
35
- --no-qr print the pairing URL without a QR code
36
-
37
- --cf-team, --cf-aud and --public-host may also live in
38
- <ROVE_HOME>/.rove/bridge/config.json as cfTeam, cfAud, publicHost.`)}if(preset==="cf")publicHost??=config.publicHost??null;if(publicHost!==null&&!/^[A-Za-z0-9.-]+$/.test(publicHost))throw Error(`--public-host must be a bare hostname (no scheme, port or path), got ${publicHost}`);if(preset!=="none"&&hosts.length>0)throw Error("--host cannot be combined with --preset; the preset picks the address");let base={port,rotateToken,qr};if(preset==="tailscale"){let ts=deps.tailscale?.()??{ipv4:null,dnsName:null};if(!ts.ipv4)throw Error("--preset tailscale: `tailscale ip -4` returned no address \u2014 is Tailscale installed and up?");return{...base,preset,hosts:[ts.ipv4],publicHost:publicHost??ts.dnsName,tls:publicHost!==null,cloudflare:null}}if(preset==="cf"){if(!cfTeam||!cfAud)throw Error("--preset cf needs --cf-team and --cf-aud (or cfTeam/cfAud in bridge/config.json)");if(!publicHost)throw Error("--preset cf needs --public-host: the tunnel hostname the phone dials");return{...base,preset,hosts:["127.0.0.1"],publicHost,tls:!0,cloudflare:{teamDomain:teamDomainOf(cfTeam),aud:cfAud.trim()}}}return{...base,preset,hosts:hosts.length>0?[...new Set(hosts)]:["127.0.0.1"],publicHost:null,tls:!1,cloudflare:null}}class TaskFeed{read;now;latest=null;latestKey="";listeners=new Set;timer=null;inflight=null;constructor(read,now=Date.now){this.read=read;this.now=now}poke(){if(this.listeners.size===0||this.timer)return;this.timer=setTimeout(()=>{this.timer=null,this.refresh().catch(()=>{})},250)}refresh(){if(this.inflight)return this.inflight;let p=this.read().then((payload)=>{this.latest={payload,readAt:this.now()};let key=JSON.stringify(payload,(k,v)=>k==="forMs"?void 0:v);if(key!==this.latestKey){this.latestKey=key;for(let listener of this.listeners)listener(payload)}return payload});this.inflight=p;let clear=()=>{if(this.inflight===p)this.inflight=null};return p.then(clear,clear),p}current(){if(!this.latest)return this.refresh();let elapsed=Math.max(0,this.now()-this.latest.readAt),{payload}=this.latest;return Promise.resolve({...payload,tasks:payload.tasks.map((t)=>t.activity?{...t,activity:{...t.activity,forMs:t.activity.forMs+elapsed}}:t)})}subscribe(listener){return this.listeners.add(listener),()=>this.listeners.delete(listener)}}class NoticeFeed{now;listeners=new Set;lastAt=0;constructor(now=Date.now){this.now=now}push(notice){if(!notice||typeof notice.title!=="string"||typeof notice.at!=="number")return;if(notice.at<=this.lastAt)return;if(this.lastAt=notice.at,this.now()-notice.at>1e4)return;let{title,body,kind,taskId,source,at}=notice,clean={title,kind:typeof kind==="string"?kind:"done",at,...body?{body}:{},...taskId?{taskId}:{},...source?{source}:{}};for(let listener of this.listeners)listener(clean)}subscribe(listener){return this.listeners.add(listener),()=>this.listeners.delete(listener)}}var BRIDGE_PROTOCOL_VERSION=1,OPS=["hello","api.invoke","tasks.subscribe","tasks.list","engines.list","repos.list","task.create","task.delete","task.land","task.tabs","tab.new","tab.close","term.attach","term.input","term.resize","term.detach","diff.files","diff.file","attention.dismiss"];function isCoreOp(op){return OPS.includes(op)}class BridgeError extends Error{code;detail;constructor(code,message,detail){super(message);this.code=code;this.detail=detail;this.name="BridgeError"}}function parseRequest(raw,known=isCoreOp){let frame;try{frame=JSON.parse(raw)}catch{throw new BridgeError("BAD_FRAME","frame is not JSON")}if(!frame||typeof frame!=="object"||Array.isArray(frame))throw new BridgeError("BAD_FRAME","frame is not an object");let{id,op,args}=frame;if(typeof id!=="number"||!Number.isSafeInteger(id))throw new BridgeError("BAD_FRAME","frame needs an integer id");if(typeof op!=="string"||!known(op))throw Object.assign(new BridgeError("UNKNOWN_OP",`unknown op: ${String(op)}`),{requestId:id});if(args!==void 0&&(args===null||typeof args!=="object"||Array.isArray(args)))throw Object.assign(new BridgeError("BAD_ARGS","args must be an object"),{requestId:id});return{id,op,args:args??{}}}function requestIdOf(err){let id=err&&typeof err==="object"&&"requestId"in err?err.requestId:null;return typeof id==="number"?id:null}function str(args,name){let v=args[name];if(typeof v!=="string"||v.length===0)throw new BridgeError("BAD_ARGS",`${name} must be a non-empty string`);return v}function optStr(args,name){let v=args[name];if(v===void 0||v===null||v==="")return;if(typeof v!=="string")throw new BridgeError("BAD_ARGS",`${name} must be a string`);return v}function optInt(args,name,min,max){let v=args[name];if(v===void 0||v===null)return;if(typeof v!=="number"||!Number.isInteger(v)||v<min||v>max)throw new BridgeError("BAD_ARGS",`${name} must be an integer in ${min}..${max}`);return v}function optBool(args,name){let v=args[name];if(v===void 0||v===null)return!1;if(typeof v!=="boolean")throw new BridgeError("BAD_ARGS",`${name} must be a boolean`);return v}function responseOk(id,result){return JSON.stringify({id,ok:!0,result:result??{}})}function responseError(id,code,message,detail){return JSON.stringify({id,ok:!1,error:{...detail,code,message}})}function pushEvent(event,data){return JSON.stringify({event,data})}function asBridgeError(err,fallback){let code=err&&typeof err==="object"&&"code"in err?err.code:void 0,data=err instanceof ApiError?err.data:void 0;return new BridgeError(typeof code==="string"?code:fallback,err instanceof Error?err.message:String(err),data)}function taskIdOf(argv){for(let i=0;i<argv.length;i++){let arg=argv[i];if(arg==="--task-id")return argv[i+1];if(arg?.startsWith("--task-id="))return arg.slice(10)}return}function createBridgeApi(client,remote){return{machineOf:(taskId)=>remote?.machineOf(taskId),async verb(name,argv){let taskId=taskIdOf(argv),alias=taskId===void 0?void 0:remote?.machineOf(taskId);if(alias!==void 0&&remote){let envelope;try{envelope=await remote.forward(alias,name,argvToParams(name,argv))}catch(err){throw asBridgeError(err,"RPC_ERROR")}if(envelope.ok)return envelope.result;let{code,message,...data}=envelope.error;throw new BridgeError(code,message,data)}try{return await invokeVerb(name,argv,{client})}catch(err){throw asBridgeError(err,"RPC_ERROR")}},async rpc(name,payload){try{return await client.request(name,payload)}catch(err){throw asBridgeError(err,"RPC_ERROR")}}}}class MachineStore{rows=[];set(statuses){this.rows=statuses.map((s)=>({alias:s.alias,hostLabel:s.hostLabel,state:s.state,version:s.version??null}))}get(){return this.rows}}var machineStore=new MachineStore;function createMachineOps(store=machineStore){return{"machines.list":{kind:"read",destructive:!1,wraps:"MachineHub.machinesSignal (the TUI sidebar's machine layer), held by the bridge",async run(){return{machines:store.get()}}}}}var machineOps=createMachineOps();class PromptFeed{now;listeners=new Set;seen=new Set;constructor(now=Date.now){this.now=now}push(prompt){if(!prompt||typeof prompt.promptId!=="string"||typeof prompt.title!=="string")return;if(typeof prompt.at!=="number"||this.now()-prompt.at>600000)return;if(this.seen.has(prompt.promptId))return;this.seen.add(prompt.promptId);let{promptId,title,placeholder,initial,at}=prompt,clean={promptId,title,at,...typeof placeholder==="string"?{placeholder}:{},...typeof initial==="string"?{initial}:{}};for(let listener of this.listeners)listener(clean)}subscribe(listener){return this.listeners.add(listener),()=>this.listeners.delete(listener)}}import{isAbsolute,normalize}from"path";function gateRows(gates,tasks){let titles=new Map(tasks.map((t)=>[t.id,t.title]));return gates.filter((g)=>g.status==="open"||g.status==="answered"&&g.handoff==="undelivered").map((g)=>({id:g.id,taskId:g.taskId,tabId:g.tabId,question:g.question,options:g.options.map((o)=>({key:o.key,label:o.label,...o.detail?{detail:o.detail}:{}})),...g.recommended?{recommended:g.recommended}:{},...g.context?{context:g.context}:{},status:g.status,...g.status==="answered"&&g.handoff?{handoff:g.handoff}:{},createdAt:g.createdAt,label:titles.get(g.taskId)??g.taskId})).sort((a,b)=>a.createdAt.localeCompare(b.createdAt)||a.id.localeCompare(b.id,void 0,{numeric:!0}))}function dismissHitsOnlyGates(attention,taskId,tabId){let here=attention.filter((r)=>r.taskId===taskId&&r.tabId===(tabId??null));return here.length>0&&here.every((r)=>r.state==="decision_needed")}function flag(name,value){return`--${name}=${value}`}function oneOf(args,name,values){let v=args[name];if(typeof v!=="string"||!values.includes(v))throw new BridgeError("BAD_ARGS",`${name} must be one of ${values.join("|")}`);return v}function optOneOf(args,name,values){let v=args[name];if(v===void 0||v===null||v==="")return;return oneOf(args,name,values)}function bool(args,name){let v=args[name];if(typeof v!=="boolean")throw new BridgeError("BAD_ARGS",`${name} must be a boolean`);return v}function text(args,name,max){let v=args[name];if(typeof v!=="string"||v.trim().length===0)throw new BridgeError("BAD_ARGS",`${name} must be non-empty text`);if(v.length>max)throw new BridgeError("BAD_ARGS",`${name} is longer than ${max} characters`);return v}function optText(args,name,max){let v=args[name];if(v===void 0||v===null||v==="")return;return text(args,name,max)}function taskId(args,name="taskId"){let v=args[name];if(typeof v!=="string"||!/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/.test(v))throw new BridgeError("BAD_ARGS",`${name} is not a task id`);return v}function tabId(args,name="tabId"){let v=args[name];if(typeof v!=="string"||!/^tab-\d{1,6}$/.test(v))throw new BridgeError("BAD_ARGS",`${name} is not a tab id`);return v}function absPath(args,name){let v=args[name];if(typeof v!=="string"||!v.startsWith("/")||v.includes("\x00"))throw new BridgeError("BAD_ARGS",`${name} must be an absolute path`);return v}function newTabInvocation(taskId2,tab){let command=tab.engine?[flag("command",tab.engine)]:[];if(tab.prompt!==void 0)return{verb:"send",argv:[flag("task-id",taskId2),"--tab=new","--plain",flag("prompt",tab.prompt),...command]};return{verb:"tab-new",argv:[flag("task-id",taskId2),flag("kind",tab.kind),...command]}}function tabRows(rows){let nameOf=(vendor)=>vendor?engineDisplayName(vendor):null;return rows.map((t)=>({id:t.id,kind:t.kind,title:t.title??t.lastTitle??t.autoTitle,engineName:t.kind==="engine"?nameOf(t.liveVendor??t.vendor):null,liveEngine:t.kind==="engine"&&t.alive!==!1?nameOf(t.liveVendor):null,alive:t.alive,engineAlive:t.engineAlive,...t.split?{split:t.split}:{},...t.viewOf?{viewOf:t.viewOf}:{}}))}async function tryRead(read){try{return await read()}catch{return null}}var ENGINE_CACHE_MS=60000;function engineFor(task,engines){let command=task.command?.trim(),preset=(command?engines.find((e)=>e.id===command||e.command===command):void 0)??engines.find((e)=>e.builtin&&e.protocol===task.vendor);if(preset)return{id:preset.id,name:preset.name};return task.vendor?{id:null,name:task.vendor}:null}function rowChanges(map,worktreePath){if(!map||!map.has(worktreePath))return;let c=map.get(worktreePath);if(!c)return{unreadable:!0};return{added:c.added,deleted:c.deleted,...c.ahead!==void 0?{ahead:c.ahead}:{},...c.behind!==void 0?{behind:c.behind}:{}}}function rowTokenChips(map,taskId2,now){return liveRowTokens(map,taskId2,now).map((t)=>({text:t.text,...t.tone?{tone:t.tone}:{},source:t.source,expiresAt:t.expiresAt}))}var PR_CHIP_BY_GLYPH={[DEFAULT_GLYPHS.conflict]:"conflict",[DEFAULT_GLYPHS.checksFailing]:"failing",[DEFAULT_GLYPHS.checksPassing]:"passing"};function prChipKind(task){let chip=prChip(task);return chip?PR_CHIP_BY_GLYPH[chip.glyph]??null:null}function attentionRows(items,tasks){let byId=new Map(tasks.map((t)=>[t.id,t]));return items.map((i)=>{let resumeAt=i.state==="rate_limited"&&i.taskId?byId.get(i.taskId)?.quotaResume?.resumeAt:void 0,label=i.detail?.routine?.name,deliveryId=i.state==="report_missing"?i.detail?.delivery?.id:void 0,gateId=i.state==="decision_needed"?i.detail?.gate?.id:void 0,automationId=i.detail?.routine?.automationId;return{taskId:i.taskId,tabId:i.tabId,state:i.state,unread:i.unread,at:i.at,...resumeAt?{resumeAt}:{},...label?{label}:{},...deliveryId?{deliveryId}:{},...gateId?{gateId}:{},...automationId?{automationId}:{}}})}function machineOf(task){let origin=task.origin;if(!origin||origin.machineId==="local")return{};return{machine:{id:origin.machineId,label:origin.hostLabel,...origin.stale?{stale:!0}:{}}}}function taskRows(tasks,activity,liveTaskIds,engines,now,extras){let byId=new Map(tasks.map((t)=>[t.id,t])),orderOf=new Map(tasks.map((t,i)=>[t.id,i]));return buildContext({repo:"",tasks,activity,liveTaskIds,attention:[],gates:[],notes:[],now,limit:Number.MAX_SAFE_INTEGER}).tasks.flatMap((row)=>{let task=byId.get(row.taskId);if(!task)return[];let pr=task.prStatus,changes=rowChanges(extras?.changes??null,task.worktreePath),tokens=rowTokenChips(extras?.tokens,task.id,now),chip=prChipKind(task);return[{id:task.id,title:task.title,branch:task.branch,repo:task.repo,kind:task.kind??"task",status:task.status,group:row.group,rank:row.rank,activity:row.activity?{...row.activity,since:now-row.activity.forMs}:null,engine:engineFor(task,engines),pr:pr?{...pr.number!==void 0?{number:pr.number}:{},...pr.url?{url:pr.url}:{},lifecycle:pr.lifecycle,checkState:pr.checkState,...pr.reviewDecision?{reviewDecision:pr.reviewDecision}:{},...pr.mergeable?{mergeable:pr.mergeable}:{}}:null,report:task.report?{summary:task.report.summary??"",at:task.report.at}:null,deleting:task.deletion?.phase==="queued"||task.deletion?.phase==="running",pinned:task.pinned===!0,...task.routine?{routine:!0}:{},order:orderOf.get(task.id)??0,createdAt:task.createdAt,updatedAt:task.updatedAt,...machineOf(task),...changes?{changes}:{},...tokens.length>0?{rowTokens:tokens}:{},prChip:chip,...task.prompt!==void 0?{hasPrompt:!0}:{},prChipStale:chip!==null&&pr?.lastError!==void 0,...task.worktreePath?{}:{noWorktree:!0}}]})}function createRoveOps(client,signals,api=createBridgeApi(client)){let engineCache=null,engineLoad=null,engines=()=>{if(engineCache&&Date.now()-engineCache.at<ENGINE_CACHE_MS)return Promise.resolve(engineCache.engines);return engineLoad??=loadEngines().finally(()=>{engineLoad=null}),engineLoad},loadEngines=async()=>{let res=await api.verb("engine-list",[]),rows=await Promise.all(res.engines.map(async({id,name,command,protocol,builtin,models})=>{let status=await tryRead(()=>detectEngineStatus(id));return{id,name,command,protocol,builtin,models:models??null,effortLevels:[...engineEntry(protocol).effortLevels??[]],acceptsModel:engineEntry(protocol).modelArgv!==void 0,...status?{ready:status.binary.found&&status.account?.kind!=="none"}:{}}}));return engineCache={at:Date.now(),engines:rows},rows},getTask=async(taskId2)=>{return(await api.verb("get-task",[flag("task-id",taskId2)])).task};return{async tasks(){let{tasks}=await client.request("task.list"),[inspect,inbox,live,engineRows]=await Promise.all([tryRead(()=>client.request("debug.inspect")),tryRead(()=>client.request("attention.list")),tryRead(()=>defaultApiRuntime.liveTaskIds()),tryRead(engines)]),now=Date.now(),local=taskRows(tasks,inspect?.activity?.tasks??null,live,engineRows??[],now,signals?{changes:signals.changes(),tokens:signals.tokens()}:void 0),remote=taskRows(signals?.remote?.()??[],null,null,engineRows??[],now).map((row)=>({...row,order:(row.order??0)+tasks.length}));return{tasks:[...local,...remote],attention:attentionRows(inbox?.items??[],tasks),gates:gateRows(signals?.gates()??[],tasks)}},engines,async repos(){let{tasks}=await client.request("task.list");return[...new Set([...getSavedRepos(),...tasks.map((t)=>t.repo)])].sort()},async createTask({repo,engine,prompt,title}){let argv=[flag("repo",repo)];if(engine)argv.push(flag("command",engine));if(title)argv.push(flag("title",title));if(prompt)argv.push(flag("prompt",prompt));return{taskId:(await api.verb("add",argv)).taskId}},async deleteTask(taskId2,force){let argv=[flag("task-id",taskId2),"--wait"];if(force)argv.push("--force");let res=await api.verb("delete",argv);return{status:res.status??(res.queued?"queued":"removed")}},async landTask(taskId2,strategy){let res=await api.verb("land",[flag("task-id",taskId2),flag("strategy",strategy)]);return{landedOn:res.landedOn,commit:res.commit}},async tabs(taskId2){let res=await api.verb("get-task",[flag("task-id",taskId2)]);return tabRows(res.tabs??[])},async newTab(taskId2,tab){let call=newTabInvocation(taskId2,tab),res=await api.verb(call.verb,call.argv),tabId2=res.tabId??res.session?.split("::")[1];if(!tabId2)throw new BridgeError("SESSION_FAILED","the new tab did not report its session");return{tabId:tabId2}},async closeTab(taskId2,tabId2){await api.verb("tab-close",[flag("task-id",taskId2),flag("tab",tabId2)])},worktreeOf:async(taskId2)=>{let task=await getTask(taskId2);if(!task.worktreePath)throw new BridgeError("NO_WORKTREE",`task ${taskId2} has no worktree yet`);return task.worktreePath},viewedSession(taskId2,tabId2){let tab=loadStateFile()[terminalTabsKey(taskId2)]?.tabs?.find((t)=>t.id===tabId2);return tab?.kind==="engine"&&tab.ptyTask?{taskId:tab.ptyTask.id,tabId:"tab-1",cwd:tab.ptyTask.worktree}:null},machineOf:(taskId2)=>api.machineOf?.(taskId2),async diffFiles(taskId2){if(api.machineOf?.(taskId2))return api.verb("worktree-diff",[flag("task-id",taskId2)]);let task=await getTask(taskId2);if(!task.worktreePath)return{base:null,files:[]};return worktreeDiff(task.worktreePath,task.prStatus?.baseRef)},async diffFile(taskId2,path,scope){if(isAbsolute(path)||normalize(path).split(/[\\/]/).includes(".."))throw new BridgeError("BAD_ARGS","path must be relative to the worktree");if(api.machineOf?.(taskId2))return api.verb("worktree-diff-file",[flag("task-id",taskId2),flag("path",path),flag("scope",scope)]);let task=await getTask(taskId2);if(!task.worktreePath)throw new BridgeError("NO_WORKTREE",`task ${taskId2} has no worktree yet`);if(scope==="working")return loadPreviewData(task.worktreePath,path);let base=await resolveBase(task.worktreePath,task.prStatus?.baseRef);if(!base)throw new BridgeError("NO_BASE","could not resolve a base branch for this task");return loadPreviewData(task.worktreePath,path,{base})},async dismissAttention(taskId2,tabId2,deliveryId){await client.request("attention.dismiss",{taskId:taskId2,...tabId2?{tabId:tabId2}:{},...deliveryId?{deliveryId}:{}})}}}import{hostname as osHostname}from"os";import{unlinkSync}from"fs";import{join}from"path";var realDeps={installHooks:async()=>{let{ensureGlobalRoveHooks}=await import("./chunk-jd02zgwf.js");await ensureGlobalRoveHooks({quiet:!0})},uninstallHooks:async()=>{let{activityHookAdapters}=await import("./chunk-c8anewjn.js");await Promise.all(activityHookAdapters().map(async(adapter)=>{try{await adapter.removeActivityHooks(adapter.globalSettingsPath())}catch{}}))},resetState:()=>{replaceStateFile({});try{unlinkSync(join(roveStateDir(),"tasks.json"))}catch(err){if(err.code!=="ENOENT")throw new BridgeError("RESET_FAILED",`could not remove tasks.json: ${err.message}`)}}};function createDevOps(deps){return{"engines.hooks.install":{kind:"write",destructive:!0,wraps:"Settings \u2192 Engines install row: ensureGlobalRoveHooks into every engine's own config",async run(){return await deps.installHooks(),{}}},"engines.hooks.uninstall":{kind:"write",destructive:!0,wraps:"Settings \u2192 Engines uninstall row: removeActivityHooks for every hook adapter",async run(){return await deps.uninstallHooks(),{}}},"dev.reset":{kind:"write",destructive:!0,wraps:"Settings \u2192 Dev reset: replaceStateFile({}), remove tasks.json, then daemon.stop {reason: restart} so its in-memory tasks go too (worktrees and engine history stay)",async run(_args,{api}){return deps.resetState(),await api.rpc("daemon.stop",{reason:"restart"}).catch(()=>{return}),{}}},"daemon.restart":{kind:"write",destructive:!0,wraps:"Settings \u2192 Dev restart: daemon RPC daemon.stop {reason: restart} (PTY host and engine sessions keep running)",async run(_args,{api}){return await api.rpc("daemon.stop",{reason:"restart"}).catch(()=>{return}),{}}}}}var devOps=createDevOps(realDeps);var MAX_FILES=50000,MAX_NOTES=200,NOTE_MAX=4000,PATH_MAX=1024,MAX_LINE=1e7,stateNoteStore={read(id){let notes=[];return updateStateFile((state)=>{let v=state[diffCommentsKey(id)];return notes=Array.isArray(v)?v:[],!1}),notes},update(id,mutate){updateStateFile((state)=>{let v=state[diffCommentsKey(id)];state[diffCommentsKey(id)]=mutate(Array.isArray(v)?v:[]);return})}},defaultDeps={listFiles:(worktree)=>listFiles(worktree),notes:stateNoteStore,now:Date.now,newId:()=>crypto.randomUUID()};async function worktreeOf(api,id){let path=(await api.verb("get-task",[flag("task-id",id)])).task.worktreePath;if(!path)throw new BridgeError("NO_WORKTREE",`task ${id} has no worktree yet`);return path}function relPath(args,name){let v=text(args,name,PATH_MAX);if(v.startsWith("/")||v.includes("\x00")||v.split(/[\\/]/).includes(".."))throw new BridgeError("BAD_ARGS",`${name} must be relative to the worktree`);return v}function lineNo(args,name){let v=optInt(args,name,1,MAX_LINE);if(v===void 0)throw new BridgeError("BAD_ARGS",`${name} must be an integer in 1..${MAX_LINE}`);return v}var samePath2=(a,b)=>a.replace(/^\/private\//,"/")===b.replace(/^\/private\//,"/");function createFilesOps(deps=defaultDeps){let spec=(s)=>s;return{"files.list":spec({kind:"read",destructive:!1,wraps:"listFiles(worktree) \u2014 tracked + untracked-not-ignored files",async run(args,{api}){let id=taskId(args),all=api.machineOf?.(id)?(await api.verb("worktree-files",[flag("task-id",id)])).files:await deps.listFiles(await worktreeOf(api,id)),truncated=all.length>MAX_FILES;return{files:truncated?all.slice(0,MAX_FILES):all,truncated}}}),"review.list":spec({kind:"read",destructive:!1,wraps:"state.json diffComments.<taskId>",async run(args){let notes=deps.notes.read(taskId(args));return{notes,unsent:unsentComments(notes).length}}}),"review.add":spec({kind:"write",destructive:!1,wraps:"state.json diffComments.<taskId> (append)",async run(args){let id=taskId(args),line=lineNo(args,"line"),startLine=optInt(args,"startLine",1,MAX_LINE);if(startLine!==void 0&&startLine>line)throw new BridgeError("BAD_ARGS","startLine is after line");let note={id:deps.newId(),filePath:relPath(args,"filePath"),...startLine!==void 0&&startLine!==line?{startLine}:{},line,body:text(args,"body",NOTE_MAX),createdAt:deps.now()};return deps.notes.update(id,(cur)=>{if(cur.length>=MAX_NOTES)throw new BridgeError("TOO_MANY_NOTES",`a task keeps at most ${MAX_NOTES} notes`);return[...cur,note]}),{note}}}),"review.remove":spec({kind:"write",destructive:!0,wraps:"state.json diffComments.<taskId> (drop one note)",async run(args){let id=taskId(args),noteId=text(args,"id",128),removed=!1;return deps.notes.update(id,(cur)=>{let next=cur.filter((c)=>c.id!==noteId);return removed=next.length!==cur.length,next}),{removed}}}),"review.send":spec({kind:"write",destructive:!1,wraps:"rove api send --plain (all unsent notes as one prompt; sentAt only on confirmed delivery)",async run(args,{api}){let id=taskId(args),tab=args.tabId===void 0||args.tabId===null||args.tabId===""?void 0:tabId(args),unsent=unsentComments(deps.notes.read(id));if(unsent.length===0)return{sent:0,delivered:!0};let worktree=await worktreeOf(api,id).catch(()=>{return}),prompt=formatDiffComments(unsent,worktree),res=await api.verb("send",[flag("task-id",id),"--plain",flag("prompt",prompt),...tab?[flag("tab",tab)]:[]]);if(res.delivered!==!0||res.targetState)return{sent:0,delivered:!1,reason:reasonText(res.targetDetail??res.reason)??(res.targetState?`engine is ${res.targetState}`:"not confirmed")};let batch=new Set(unsent.map((c)=>c.id)),sentAt=deps.now();return deps.notes.update(id,(cur)=>cur.map((c)=>batch.has(c.id)&&c.sentAt===void 0?{...c,sentAt}:c)),{sent:unsent.length,delivered:!0}}}),"worktrees.list":spec({kind:"read",destructive:!1,wraps:"daemon RPC worktree.list {network} + task.list (taskId/taskKind joined by path)",async run(args,{api}){let network=args.network===void 0?!0:args.network===!0;if(args.network!==void 0&&typeof args.network!=="boolean")throw new BridgeError("BAD_ARGS","network must be a boolean");let[list,tasks]=await Promise.all([api.rpc("worktree.list",{network}),api.rpc("task.list")]);return{projects:list.projects.map((p)=>({repo:p.repo,worktrees:p.worktrees.map((w)=>{let t=tasks.tasks.find((x)=>x.worktreePath&&samePath2(x.worktreePath,w.path));return t?{...w,taskId:t.id,taskKind:t.kind??"task"}:w})}))}}}),"worktrees.remove":spec({kind:"write",destructive:!0,wraps:"daemon RPC worktree.remove {path, force}",async run(args,{api}){let path=absPath(args,"path"),force=bool(args,"force");if(!(await api.rpc("worktree.list",{network:!1})).projects.some((p)=>p.worktrees.some((w)=>w.path===path)))throw new BridgeError("UNKNOWN_WORKTREE","that path is not a listed worktree");try{let res=await api.rpc("worktree.remove",{path,force});return{removed:res.removed,...res.residue?{residue:res.residue}:{}}}catch(err){if(err instanceof BridgeError&&err.message.startsWith("DIRTY_WORKTREE: "))throw new BridgeError("DIRTY_WORKTREE",err.message.slice(16));throw err}}})}}function reasonText(reason){if(reason===void 0||reason===null||reason==="")return;return typeof reason==="string"?reason:JSON.stringify(reason)}var filesOps=createFilesOps();var GATE_ID=/^g-\d{1,9}$/,OPTION_KEY=/^[A-Za-z0-9][A-Za-z0-9_-]{0,31}$/,TEXT_MAX=4000,DAEMON_CODE=/^(GATE_NOT_FOUND|GATE_NOT_OPEN|BAD_OPTION): /;function gateId(args){let v=args.gateId;if(typeof v!=="string"||!GATE_ID.test(v))throw new BridgeError("BAD_ARGS","gateId is not a gate id");return v}async function answer(args,ctx){let id=gateId(args),option=args.option;if(option!==void 0&&option!==null&&(typeof option!=="string"||!OPTION_KEY.test(option)))throw new BridgeError("BAD_ARGS","option is not an option key");let text2=optText(args,"text",TEXT_MAX);if(!option&&!text2)throw new BridgeError("BAD_ARGS","answer needs an option or text");try{return{delivered:(await ctx.api.rpc("gate.answer",{gateId:id,...option?{option}:{},...text2?{text:text2}:{},by:"bridge"})).delivered===!0}}catch(err){let message=err instanceof Error?err.message:String(err),code=DAEMON_CODE.exec(message)?.[1];if(code)throw new BridgeError(code,message.replace(DAEMON_CODE,""));throw err}}var gateAnswer={kind:"write",destructive:!1,wraps:"rpc gate.answer (by: bridge)",run:answer},GATE_OPS={"gate.answer":gateAnswer};var CURSOR=/^[A-Za-z0-9._~=-]{1,1024}$/;function given(args,name){let v=args[name];return v!==void 0&&v!==null&&v!==""}function optCursor(args){let raw=optText(args,"cursor",1024);if(raw===void 0)return;if(!CURSOR.test(raw))throw new BridgeError("BAD_ARGS","cursor is not a read-output cursor");return raw}var insightOps={"output.read":{kind:"read",destructive:!1,wraps:"rove api read-output (structured engine history, else a labeled terminal tail)",run(args,{api}){let argv=[flag("task-id",taskId(args))];if(given(args,"tab"))argv.push(flag("tab",tabId(args,"tab")));let source=optOneOf(args,"source",["auto","history","terminal"]);if(source)argv.push(flag("source",source));let cursor=optCursor(args);if(cursor)argv.push(flag("cursor",cursor));let limit=optInt(args,"limit",1,50);if(limit!==void 0)argv.push(flag("limit",limit));return api.verb("read-output",argv)}},"repo.digest":{kind:"read",destructive:!1,wraps:"rove api digest",run(args,{api}){let argv=[flag("repo",absPath(args,"repo"))],days=optInt(args,"sinceDays",1,365);if(days!==void 0)argv.push(flag("since-days",days));return api.verb("digest",argv)}},"turns.list":{kind:"read",destructive:!1,wraps:"rove api agent-turns",run(args,{api}){let argv=[];if(given(args,"taskId"))argv.push(flag("task-id",taskId(args)));if(given(args,"repo"))argv.push(flag("repo",absPath(args,"repo")));let days=optInt(args,"sinceDays",1,365);if(days!==void 0)argv.push(flag("since-days",days));let limit=optInt(args,"limit",1,500);if(limit!==void 0)argv.push(flag("limit",limit));return api.verb("agent-turns",argv)}}};var CHORD_MAX=64,realDeps2={read(){return resetKeybindingsFileCache(),readKeybindingsFile()},create:createKeybindingsFile,fireAction(target){let[cmd,...rest]=[...roveCliInvocation(),"plugin","action","invoke",target];return spawnDetached(cmd,rest,{onError:(err)=>console.warn(`[rove/plugins] ${target}: ${String(err)}`)})},platform:process.platform},same=(a,b)=>JSON.stringify(a??null)===JSON.stringify(b??null);function copyKeymap(){return RoveKeymap.map((row)=>({id:row.id,scope:row.scope,keys:[...row.keys],...row.prefixKeys?{prefixKeys:[...row.prefixKeys]}:{},...row.hint?{hint:{...row.hint}}:{}}))}function createKeybindingsOps(deps=realDeps2){return{"keybindings.get":{kind:"read",destructive:!1,wraps:"helper resolveUserKeybindings (the TUI's keybindings.yaml parser) on a copy of the default keymap",async run(){let file=deps.read(),rows=copyKeymap(),{report,prefix}=resolveUserKeybindings(file,rows,deps.platform),changed=[];for(let row of rows){let base=RoveKeymap.find((candidate)=>candidate.id===row.id);if(base&&same(base.keys,row.keys)&&same(base.prefixKeys,row.prefixKeys)&&same(base.hint,row.hint))continue;changed.push({id:row.id,keys:row.keys,...row.prefixKeys?{prefixKeys:row.prefixKeys}:{},...row.hint?{hint:row.hint}:{}})}return{path:report.path,exists:report.exists,rows:changed,prefix:{key:prefix.key,firstStrokes:prefixFirstStrokes(prefix),timeoutMs:prefix.timeoutMs},plugins:report.plugins,applied:report.applied,warnings:report.warnings}}},"keybindings.createFile":{kind:"write",destructive:!1,wraps:"helper createKeybindingsFile (the fixed, fully commented starter; never overwrites)",async run(){let file=deps.read();if(file.exists)return{path:file.path,created:!1};return{path:file.path,created:deps.create(file.path)}}},"plugin.chordAction":{kind:"write",destructive:!1,wraps:"rove plugin action invoke <target> (detached), only for an `action:` bound to this chord in keybindings.yaml",async run(a){let chord=str(a,"chord");if(chord.length>CHORD_MAX)throw new BridgeError("BAD_ARGS","chord is too long");let file=deps.read(),{report}=resolveUserKeybindings(file,copyKeymap(),deps.platform),bound=report.plugins.find((p)=>p.chord===chord&&p.kind==="action");if(!bound)throw new BridgeError("NOT_BOUND",`${chord} is not bound to a plugin action`);if(!deps.fireAction(bound.target))throw new BridgeError("SESSION_FAILED",`could not start plugin action ${bound.target}`);return{target:bound.target}}}}}var keybindingsOps=createKeybindingsOps();var PATH_MAX2=1000,PANE_ID=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/,launchDeps={panes(){return listPaneLaunches({socketPath:defaultDaemonSocketPath(),binPath:ROVE_PRODUCT_NAME}).map((p)=>({id:`${p.pluginId}.${p.paneId}`,title:p.title,placement:p.placement}))}};async function tabNew(api,id,kind,extra){let res=await api.verb("tab-new",[flag("task-id",id),flag("kind",kind),...extra]);if(!res.tabId)throw new BridgeError("SESSION_FAILED","the new tab did not report its id");return{tabId:res.tabId,...res.leaf?{leaf:res.leaf}:{}}}var launchOps={"file.openInEditor":{kind:"write",destructive:!1,wraps:"rove api tab-new --kind editor (the Mac's configured editor on a file inside the task's worktree)",async run(a,{api}){let rel=optStr(a,"path")??"";if(!rel||rel.length>PATH_MAX2||rel.startsWith("/")||rel.includes("\x00")||rel.split(/[\\/]/).includes(".."))throw new BridgeError("BAD_ARGS","path must be relative to the worktree");let{tabId:opened}=await tabNew(api,taskId(a),"editor",[flag("file",rel)]);return{tabId:opened}}},"plugin.panes":{kind:"read",destructive:!1,wraps:"helper listPaneLaunches (panes of enabled plugins)",async run(){return{panes:launchDeps.panes()}}},"plugin.paneOpen":{kind:"write",destructive:!1,wraps:"rove api tab-new --kind pane (a registered plugin pane; its argv comes from the manifest)",async run(a,{api}){let pane=str(a,"pane");if(!PANE_ID.test(pane))throw new BridgeError("BAD_ARGS","pane is not a plugin pane id");let extra=[flag("pane",pane)];if(a.tabId!==void 0&&a.tabId!==null)extra.push(flag("tab",tabId(a)));return tabNew(api,taskId(a),"pane",extra)}},"tab.openViewport":{kind:"write",destructive:!1,wraps:"rove api tab-new --kind viewport (a tab showing another task's first engine session)",async run(a,{api}){let{tabId:opened}=await tabNew(api,taskId(a),"viewport",[flag("view-task",taskId(a,"viewTaskId"))]);return{tabId:opened}}}};var PLUGIN_ID=/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;function control(a){let plugin=text(a,"plugin",128),pane=text(a,"pane",64),key=text(a,"key",64);if(!PLUGIN_ID.test(plugin)||!MOD_PANE_ID_PATTERN.test(pane)||!MOD_KEY_PATTERN.test(key))throw new BridgeError("BAD_ARGS","plugin, pane or key is not a mod id");return{plugin,pane,key}}function delivered(res){if(!res.ok)throw new BridgeError("MOD_UNAVAILABLE",`the mod did not get it (${res.reason??"unknown"})`);return{}}var modsOps={"mod.press":{kind:"write",destructive:!1,wraps:"daemon RPC mod.press (forwards a button key to the mod that drew it)",async run(a,{api}){return delivered(await api.rpc("mod.press",control(a)))}},"mod.submit":{kind:"write",destructive:!1,wraps:"daemon RPC mod.submit (forwards an input field's text to the mod that drew it)",async run(a,{api}){let value=a.value;if(typeof value!=="string")throw new BridgeError("BAD_ARGS","value must be text");if(value.length>MOD_INPUT_MAX)throw new BridgeError("BAD_ARGS",`value is longer than ${MOD_INPUT_MAX} characters`);return delivered(await api.rpc("mod.submit",{...control(a),value}))}}};var ISSUE_STATUSES=["open","doing","hold","done"],TITLE_MAX=300,BODY_MAX=20000,NAME_MAX=120,PROMPT_MAX=20000,EVENT_LIMIT=12;function issueId(args){let id=optInt(args,"id",1,1e9);if(id===void 0)throw new BridgeError("BAD_ARGS","id must be an issue number");return id}function cron(args,name){let v=text(args,name,120).trim();if(!/^[0-9A-Za-z*/,\-?#LW]+(?: [0-9A-Za-z*/,\-?#LW]+){4}$/.test(v))throw new BridgeError("BAD_ARGS",`${name} must be five space-separated cron fields`);return v}function setIfPresent(argv,name,value){if(value!==void 0)argv.push(flag(name,value))}var pagesOps={"issue.repos":{kind:"read",destructive:!1,wraps:"daemon RPC issue.repos",run:(_args,{api})=>api.rpc("issue.repos",{})},"issue.list":{kind:"read",destructive:!1,wraps:"rove api issue-list",run:(args,{api})=>api.verb("issue-list",[flag("repo",absPath(args,"repo"))])},"issue.create":{kind:"write",destructive:!1,wraps:"rove api issue-create",run:(args,{api})=>{let argv=[flag("repo",absPath(args,"repo")),flag("title",text(args,"title",TITLE_MAX).trim())];return setIfPresent(argv,"body",optText(args,"body",BODY_MAX)),api.verb("issue-create",argv)}},"issue.update":{kind:"write",destructive:!1,wraps:"rove api issue-update (title/body and/or task link; task=none unlinks; clearBody stores one space, the verb cannot send an empty body)",run:(args,{api})=>{let title=optText(args,"title",TITLE_MAX),typed=optText(args,"body",BODY_MAX);if(optBool(args,"clearBody")&&typed!==void 0)throw new BridgeError("BAD_ARGS","body and clearBody are exclusive");let body=optBool(args,"clearBody")?" ":typed,task=optStr(args,"task")===void 0?void 0:taskId(args,"task");if(title===void 0&&body===void 0&&task===void 0)throw new BridgeError("BAD_ARGS","issue.update needs title, body and/or task");let argv=[flag("repo",absPath(args,"repo")),flag("id",issueId(args))];return setIfPresent(argv,"title",title?.trim()),setIfPresent(argv,"body",body),setIfPresent(argv,"task",task),api.verb("issue-update",argv)}},"issue.setStatus":{kind:"write",destructive:!1,wraps:"rove api issue-update --status",run:(args,{api})=>api.verb("issue-update",[flag("repo",absPath(args,"repo")),flag("id",issueId(args)),flag("status",oneOf(args,"status",ISSUE_STATUSES))])},"issue.delete":{kind:"write",destructive:!0,wraps:"rove api issue-delete (the tracker record only; a linked task and worktree stay)",run:(args,{api})=>api.verb("issue-delete",[flag("repo",absPath(args,"repo")),flag("id",issueId(args))])},"issue.prompt":{kind:"read",destructive:!1,wraps:"rove api issue-list + the kanban drawer's issueWorktreePrompt/issueProjectPrompt helpers",async run(args,{api}){let where=oneOf(args,"where",["worktree","project"]),id=issueId(args),issue=(await api.verb("issue-list",[flag("repo",absPath(args,"repo"))])).issues.find((i)=>i.id===id);if(!issue)throw new BridgeError("ISSUE_NOT_FOUND",`no issue #${id}`);let cli=roveApiInvocation();return{title:issueChatTaskTitle(issue),prompt:where==="worktree"?issueWorktreePrompt(issue,cli):issueProjectPrompt(issue,cli)}}},"task.events":{kind:"read",destructive:!1,wraps:"daemon RPC task.recentEvents, newest first, as the drawer's EVENTS rows",async run(args,{api}){let id=taskId(args),limit=optInt(args,"limit",1,100)??EVENT_LIMIT;try{return{events:(await api.rpc("task.recentEvents",{taskId:id})).events.slice(-limit).reverse().map((event)=>({kind:event.kind,at:event.at,tail:[detailFragment(event),event.vendor??""].filter((part)=>part.length>0).join(" \xB7 ")}))}}catch(err){if(err instanceof Error&&/task not found/i.test(err.message))return{events:[]};throw err}}},"routine.list":{kind:"read",destructive:!1,wraps:"rove api routine-list",run:(_args,{api})=>api.verb("routine-list",[])},"routine.create":{kind:"write",destructive:!1,wraps:"rove api routine-create (name, repo, prompt, schedule, optional existing conversation targetTask+targetTab; never a precheck)",run:(args,{api})=>{let argv=[flag("repo",absPath(args,"repo")),flag("name",text(args,"name",NAME_MAX).trim()),flag("prompt",text(args,"prompt",PROMPT_MAX)),flag("schedule",cron(args,"schedule"))],hasTask=optStr(args,"targetTask")!==void 0,hasTab=optStr(args,"targetTab")!==void 0;if(hasTask!==hasTab)throw new BridgeError("BAD_ARGS","targetTask and targetTab go together");if(hasTask)argv.push(flag("target-task",taskId(args,"targetTask")),flag("target-tab",tabId(args,"targetTab")));return api.verb("routine-create",argv)}},"routine.update":{kind:"write",destructive:!1,wraps:"rove api routine-update (name, prompt, schedule; never a precheck)",run:(args,{api})=>{let name=optText(args,"name",NAME_MAX),prompt=optText(args,"prompt",PROMPT_MAX),schedule=optStr(args,"schedule")===void 0?void 0:cron(args,"schedule");if(name===void 0&&prompt===void 0&&schedule===void 0)throw new BridgeError("BAD_ARGS","routine.update needs name, prompt and/or schedule");let argv=[flag("id",taskId(args,"id"))];return setIfPresent(argv,"name",name?.trim()),setIfPresent(argv,"prompt",prompt),setIfPresent(argv,"schedule",schedule),api.verb("routine-update",argv)}},"routine.setEnabled":{kind:"write",destructive:!1,wraps:"rove api routine-update --enabled",run:(args,{api})=>api.verb("routine-update",[flag("id",taskId(args,"id")),flag("enabled",String(bool(args,"enabled")))])},"routine.runNow":{kind:"write",destructive:!1,wraps:"rove api routine-run-now (skips the precheck; the schedule does not shift)",run:(args,{api})=>api.verb("routine-run-now",[flag("id",taskId(args,"id"))])},"routine.runs":{kind:"read",destructive:!1,wraps:"rove api routine-runs",run:(args,{api})=>api.verb("routine-runs",[flag("id",taskId(args,"id"))])},"routine.delete":{kind:"write",destructive:!0,wraps:"rove api routine-delete (the routine and its history; tasks it created stay)",run:(args,{api})=>api.verb("routine-delete",[flag("id",taskId(args,"id"))])},"attention.dismissRoutine":{kind:"write",destructive:!1,wraps:"daemon RPC attention.dismissRoutine (a routine episode is keyed on its schedule, not a task)",run:(args,{api})=>api.rpc("attention.dismissRoutine",{automationId:taskId(args,"automationId")})},"workitem.list":{kind:"read",destructive:!1,wraps:"daemon RPC workitem.list (same call as rove api workitem-list, plus refresh)",run:(args,{api})=>{let state=optOneOf(args,"state",["open","closed","all"]),assignee=optOneOf(args,"assignee",["@me"]),search=optText(args,"search",200),limit=optInt(args,"limit",1,50);return api.rpc("workitem.list",{repo:absPath(args,"repo"),...state?{state}:{},...assignee?{assignee}:{},...search?{search}:{},...limit!==void 0?{limit}:{},...optBool(args,"refresh")?{refresh:!0}:{}})}},"workitem.links":{kind:"read",destructive:!1,wraps:"daemon RPC task.list, folded to the tasks already started from a GitHub issue of this repo",async run(args,{api}){let repo=absPath(args,"repo"),{tasks}=await api.rpc("task.list",{});return{links:tasks.flatMap((task)=>task.linkedWorkItem?.number!==void 0&&samePath(task.repo,repo)?[{number:task.linkedWorkItem.number,taskId:task.id}]:[])}}},"workitem.start":{kind:"write",destructive:!1,wraps:"rove api workitem-start",run(args,{api}){let number=optInt(args,"number",1,1e9);if(number===void 0)throw new BridgeError("BAD_ARGS","number must be an issue number");let argv=[flag("repo",absPath(args,"repo")),flag("number",number)];return setIfPresent(argv,"vendor",optOneOf(args,"engine",ALL_VENDORS)),api.verb("workitem-start",argv)}}};var KEY_HINTS_ENABLED_KEY="hints.keyboard.enabled",PANE_HINT_USED_KEYS=["hints.sidebar.used","hints.files.used"],RAIL_FOLD_STYLES=appearanceChoices("railFold",[]).map((c)=>c.value),bool2=(key,fallback,extra)=>({kind:"bool",read:(s)=>typeof s[key]==="boolean"?s[key]:fallback,write:(v)=>({[key]:v,...extra?.(v)})}),choice=(key,choices,read,stored=(v)=>v)=>({kind:"enum",choices:()=>choices,read:(s)=>read(s[key]),write:(v)=>({[key]:stored(v)})}),appearance=(setting)=>appearanceChoices(setting,[]).map((c)=>c.value),text2=(raw)=>typeof raw==="string"?raw.trim():"";function worktreeKind(state){return worktreeBaseKindOf(text2(state[WORKTREE_BASE_KEY]))}function worktreeCustomPath(state){return text2(state[WORKTREE_BASE_CUSTOM_KEY])||(worktreeKind(state)==="custom"?text2(state[WORKTREE_BASE_KEY]):"")}var WORKTREE_KINDS=["default","nextToProject","custom"],PREF_DEFS={locale:choice("locale",LOCALES.map((l)=>l.id),(raw)=>isLocaleId(raw)?raw:DEFAULT_LOCALE),theme:{kind:"enum",choices:(themes)=>themes,read:(s)=>typeof s.activeTheme==="string"&&s.activeTheme?s.activeTheme:DEFAULT_THEME,write:(v)=>({activeTheme:v})},themeMode:choice("themeMode",THEME_MODE_PREFERENCES,(raw)=>THEME_MODE_PREFERENCES.includes(raw)?raw:DEFAULT_THEME_MODE),transparent:bool2("transparentBackground",defaultTransparentBackground()),focusAccent:choice("focusAccent",FOCUS_ACCENT_SLOTS,(raw)=>FOCUS_ACCENT_SLOTS.includes(raw)?raw:"primary"),splitStyle:choice(SPLIT_STYLE_KEY,appearance("splitStyle"),normalizeSplitStyle),railFold:choice(RAIL_FOLD_STYLE_KEY,RAIL_FOLD_STYLES,(raw)=>RAIL_FOLD_STYLES.includes(raw)?raw:"glyphs"),tabRowHeight:choice(TAB_ROW_HEIGHT_KEY,appearance("tabRowHeight"),(raw)=>normalizeTabRowHeight(raw??DEFAULT_TAB_ROW_HEIGHT)),workingBorder:choice(WORKING_BORDER_KEY,appearance("workingBorder"),(raw)=>normalizeWorkingBorder(raw??DEFAULT_WORKING_BORDER)),taskColors:choice(TASK_COLORS_KEY,appearance("taskColors"),(raw)=>normalizeTaskColors(raw??DEFAULT_TASK_COLORS)),glyphSet:choice(GLYPH_SET_KEY,appearance("glyphSet"),(raw)=>normalizeGlyphSet(raw??DEFAULT_GLYPH_SET)),colorblind:choice(COLORBLIND_KEY,appearance("colorblind"),(raw)=>normalizeColorblind(raw??DEFAULT_COLORBLIND)),runningTitle:choice(RUNNING_TITLE_KEY,appearance("runningTitle"),(raw)=>normalizeRunningTitle(raw??DEFAULT_RUNNING_TITLE)),toast:bool2("notifications.toast.enabled",!0),sound:bool2("notifications.sound.enabled",!0),soundVolume:choice(SOUND_VOLUME_KEY,SOUND_VOLUME_STEPS,(raw)=>normalizeSoundVolume(raw??DEFAULT_SOUND_VOLUME)),crossTask:bool2("notifications.crossTask.enabled",!0),keyHints:bool2(KEY_HINTS_ENABLED_KEY,!0,(on)=>on?Object.fromEntries(PANE_HINT_USED_KEYS.map((k)=>[k,!1])):{}),zen:bool2(ZEN_ACTIVE_KEY,ZEN_DEFAULT),editorKind:choice(EDITOR_KIND_KEY,EDITOR_KINDS,(raw)=>normalizeEditorKind(raw??DEFAULT_EDITOR_KIND)),worktreeBase:{kind:"enum",choices:()=>WORKTREE_KINDS,read:(s)=>worktreeKind(s),write:(kind,s)=>{let current=text2(s[WORKTREE_BASE_KEY]),remember=worktreeKind(s)==="custom"&&kind!=="custom"?{[WORKTREE_BASE_CUSTOM_KEY]:current}:{};if(kind==="default")return{...remember,[WORKTREE_BASE_KEY]:""};if(kind==="nextToProject")return{...remember,[WORKTREE_BASE_KEY]:PROJECT_SIBLING_BASE};let custom=worktreeCustomPath(s);if(!custom)throw new BridgeError("BAD_ARGS","no custom worktree base is remembered; set one in the TUI");return{[WORKTREE_BASE_KEY]:custom}}},scrollbackRows:{kind:"int",min:SCROLLBACK_ROWS_MIN,max:SCROLLBACK_ROWS_MAX,read:(s)=>normalizeScrollbackRows(s[SCROLLBACK_ROWS_KEY]??DEFAULT_SCROLLBACK_ROWS),write:(v)=>({[SCROLLBACK_ROWS_KEY]:normalizeScrollbackRows(v)})},tabStrip:{kind:"enum",choices:()=>TAB_STRIP_MODES,read:(s)=>resolveTabStripMode(s[TAB_STRIP_MODE_KEY],s[TAB_STRIP_HIDE_SINGLE_KEY]),write:(v)=>({[TAB_STRIP_MODE_KEY]:v})},prefixTap:choice(PREFIX_TAP_PRESENTATION_KEY,PREFIX_TAP_PRESENTATIONS,(raw)=>normalizePrefixTapPresentation(raw??DEFAULT_PREFIX_TAP_PRESENTATION)),remoteProjects:bool2("experimental.remoteProjects",!1),autoStatus:bool2(AUTO_STATUS_KEY,!1),dispatcher:bool2(DISPATCHER_KEY,!1)};function prefInfo(state){return{editorCustomSet:text2(state[EDITOR_CUSTOM_KEY]).length>0,worktreeCustomPath:worktreeCustomPath(state)}}function checkPrefValue(name,def,value,themes){let bad=(why)=>{throw new BridgeError("BAD_ARGS",`${name} ${why}`)};if(def.kind==="bool")return typeof value==="boolean"?value:bad("must be a boolean");if(def.kind==="int"){if(typeof value!=="number"||!Number.isInteger(value))return bad("must be an integer");return value<def.min||value>def.max?bad(`must be ${def.min}\u2013${def.max}`):value}let choices=def.choices(themes);if((typeof value==="string"||typeof value==="number")&&choices.includes(value))return value;return bad(`must be one of ${choices.join("|")}`)}var SLOTS={text:null,background:null,textMuted:"text",backgroundPanel:"background",backgroundElement:"background",backgroundMenu:"backgroundElement",border:"text",borderActive:"border",borderSubtle:"border",primary:"text",secondary:"text",accent:"primary",error:"text",warning:"text",success:"text",info:"text",diffAdded:"success",diffRemoved:"error",diffLineNumber:"textMuted",syntaxType:"info",diffAddedBg:"background",diffRemovedBg:"background"},hex2=(n)=>n.toString(16).padStart(2,"0"),rgbHex=(c)=>`#${c.toInts().slice(0,3).map(hex2).join("")}`;function palette(theme,mode){let out={},slot=(name)=>{let known=out[name];if(known)return known;let fallback=SLOTS[name],value=resolveThemeSlotHex(theme,name,mode)??(fallback?slot(fallback):name==="background"?"#000000":"#ffffff");return out[name]=value,value};for(let name of Object.keys(SLOTS))slot(name);let cb=diffInks(resolveTheme(theme,mode),!0);return out.diffAddedColorblind=rgbHex(cb.added),out.diffAddedBgColorblind=rgbHex(cb.addedBg),out}function taskColors(theme,mode,ids){let resolved=resolveTheme(theme,mode);return Object.fromEntries(ids.map((id)=>[id,rgbHex(taskColor(id,resolved))]))}var realDeps3={load:loadStateFile,patch:(patch)=>{patchStateFile(patch)},themes:()=>{let user=loadUserThemes(),names=new Set(user.map((u)=>u.name));return[...Object.entries(BUNDLED_THEME_JSONS).filter(([name])=>!names.has(name)).map(([name,theme])=>({name,bundled:!0,theme})),...user.map((u)=>({name:u.name,bundled:!1,theme:u.theme}))]}},MAX_TASK_IDS=500,MODES=["dark","light"];function readAll(deps,state){let themes=deps.themes(),names=themes.map((t)=>t.name),prefs={},options={};for(let[name,def]of Object.entries(PREF_DEFS))if(prefs[name]=def.read(state),def.kind==="enum")options[name]=def.choices(names);else if(def.kind==="int")options[name]={min:def.min,max:def.max};return{prefs,options,info:prefInfo(state),themes}}function createPrefsOps(deps){return{"prefs.get":{kind:"read",destructive:!1,wraps:"state.json Settings keys read through the TUI normalizers, plus resolved theme palettes and glyph sets",async run(){let{prefs,options,info,themes}=readAll(deps,deps.load());return{prefs,options,info,glyphSets:GLYPH_SETS,themes:themes.map((t)=>({name:t.name,bundled:t.bundled,dark:palette(t.theme,"dark"),light:palette(t.theme,"light")}))}}},"prefs.set":{kind:"write",destructive:!1,wraps:"state.json: one allowlisted Settings key per call, value checked against the TUI's own choices",async run(args){let name=args.key;if(typeof name!=="string"||!Object.hasOwn(PREF_DEFS,name))throw new BridgeError("BAD_ARGS",`key must be one of ${Object.keys(PREF_DEFS).join("|")}`);let def=PREF_DEFS[name],state=deps.load(),names=deps.themes().map((t)=>t.name),value=checkPrefValue(name,def,args.value,names);deps.patch(def.write(value,state));let{prefs}=readAll(deps,deps.load());return{key:name,value:prefs[name],prefs}}},"theme.taskColors":{kind:"read",destructive:!1,wraps:"the TUI's taskColor(taskId, theme) for each id under one theme and mode",async run(args){let{themes}=readAll(deps,deps.load()),theme=themes.find((t)=>t.name===args.theme);if(!theme)throw new BridgeError("BAD_ARGS","theme is not an installed theme");let mode=MODES.find((m)=>m===args.mode);if(!mode)throw new BridgeError("BAD_ARGS","mode must be dark|light");let ids=args.taskIds;if(!Array.isArray(ids)||ids.length>MAX_TASK_IDS||!ids.every((id)=>typeof id==="string"&&id.length<=64))throw new BridgeError("BAD_ARGS",`taskIds must be at most ${MAX_TASK_IDS} task ids`);return{colors:taskColors(theme.theme,mode,ids)}}}}}var prefsOps=createPrefsOps(realDeps3);var PROMPT_ID=/^[A-Za-z0-9-]{1,64}$/,ANSWER_MAX=4000,promptOps={"ui.promptReply":{kind:"write",destructive:!1,wraps:"daemon RPC ui.promptReply (answers or cancels a plugin's input dialog; first answer wins)",async run(a,{api}){let promptId=a.promptId;if(typeof promptId!=="string"||!PROMPT_ID.test(promptId))throw new BridgeError("BAD_ARGS","promptId is not a prompt id");let value=a.value;if(value!==void 0&&(typeof value!=="string"||value.length>ANSWER_MAX))throw new BridgeError("BAD_ARGS",`value must be text of at most ${ANSWER_MAX} characters`);if(!(await api.rpc("ui.promptReply",{promptId,...value!==void 0?{value}:{}})).ok)throw new BridgeError("PROMPT_GONE","this question was already answered or timed out");return{}}}};var NAME_MAX2=60,realDeps4={load:loadStateFile,patch:(patch)=>{patchStateFile(patch)},installed:installedEngineIds,statuses:detectEngineStatuses,integrations:engineIntegrations,engineName:engineDisplayName,defaultCommand:defaultEngineCommand};function strings(state,key){let raw=state[key];return Array.isArray(raw)?raw.filter((s)=>typeof s==="string"&&s.trim().length>0):[]}function trimmed(state,key){let raw=state[key];return typeof raw==="string"?raw.trim():""}async function engineIds(deps,state){return[...new Set([...ALL_VENDORS,...strings(state,"customEngineIds"),...await deps.installed()])]}function canBeDefault(state,id){return isBuiltinVendor(id)||strings(state,"customEngineIds").includes(id)}function effectiveDefault(state,ids){let off=strings(state,"disabledEngineIds"),stored=trimmed(state,"defaultVendor");if(stored&&canBeDefault(state,stored)&&!off.includes(stored))return stored;return ids.find((id)=>canBeDefault(state,id)&&!off.includes(id))??null}function listedId(args,ids){let id=text(args,"id",64);if(!ids.includes(id))throw new BridgeError("BAD_ARGS",`${id} is not a listed engine`);return id}function createEngineOps(deps){let without=(list,id)=>list.filter((x)=>x!==id);function afterLeaving(state,ids,id,off){let remaining=ids.filter((x)=>x!==id&&!off.includes(x));if(remaining.length===0)throw new BridgeError("LAST_ENGINE","this is the last enabled engine; it stays on");if(effectiveDefault(state,ids)!==id)return{};let next=remaining.find((x)=>canBeDefault(state,x));if(!next)throw new BridgeError("LAST_ENGINE","no other enabled engine can take over as the default");return{defaultVendor:next}}return{"engines.settings":{kind:"read",destructive:!1,wraps:"settings Engines section: registry + detectEngineStatuses + engineIntegrations + state.json switches",async run(){let state=deps.load(),ids=await engineIds(deps,state),[statuses,integrations]=await Promise.all([deps.statuses(ids).catch(()=>[]),Promise.resolve(deps.integrations(ids)).catch(()=>[])]),off=strings(state,"disabledEngineIds"),custom=strings(state,"customEngineIds"),current=effectiveDefault(state,ids);return{defaultId:current,engines:ids.map((id)=>{let status=statuses.find((s)=>s.vendor===id),integration=integrations.find((i)=>i.vendor===id),override=trimmed(state,`engineCommand.${id}`),command=override?override.split(/\s+/):deps.defaultCommand(id),account=status?.account;return{id,name:deps.engineName(id),builtin:isBuiltinVendor(id),custom:custom.includes(id),enabled:!off.includes(id),isDefault:id===current,canBeDefault:canBeDefault(state,id),binary:command[0]??null,customized:override.length>0||trimmed(state,`engineName.${id}`).length>0,protocol:trimmed(state,`engineProtocol.${id}`)||null,binaryFound:status?status.binary.found:null,binaryPath:status?.binary.found?status.binary.path??null:null,login:!status||account===null||account===void 0||status.accountError?"unknown":account.kind==="none"?"no":"yes",hooks:integration?.hooksSupported?integration.hookState:"unsupported",markers:integration?.markers??!1,screen:integration?.screen??!1,...integration?.configIssue?{configIssue:integration.configIssue}:{}}})}}},"engine.setEnabled":{kind:"write",destructive:!0,wraps:"state.json disabledEngineIds (+ defaultVendor handoff), as Settings \u2192 Engines `space`",async run(args){let state=deps.load(),ids=await engineIds(deps,state),id=listedId(args,ids),enabled=bool(args,"enabled"),off=strings(state,"disabledEngineIds");if(enabled)deps.patch({disabledEngineIds:without(off,id)});else if(!off.includes(id)){let handoff=afterLeaving(state,ids,id,off);deps.patch({disabledEngineIds:[...off,id],...handoff})}return{id,enabled}}},"engine.setDefault":{kind:"write",destructive:!0,wraps:"state.json defaultVendor (+ re-enables it), as Settings \u2192 Engines `d`",async run(args){let state=deps.load(),ids=await engineIds(deps,state),id=listedId(args,ids);if(!canBeDefault(state,id))throw new BridgeError("BAD_ARGS",`${id} cannot be the default; only built-in and custom engines can`);let off=strings(state,"disabledEngineIds");return deps.patch({defaultVendor:id,...off.includes(id)?{disabledEngineIds:without(off,id)}:{}}),{id}}},"engine.rename":{kind:"write",destructive:!0,wraps:"state.json engineName.<id>, as Settings \u2192 Engines `r` (blank clears)",async run(args){let state=deps.load(),id=listedId(args,await engineIds(deps,state)),raw=typeof args.name==="string"?args.name.trim():"";if(raw.length>NAME_MAX2||/[\u0000-\u001f\u007f]/.test(raw))throw new BridgeError("BAD_ARGS",`name must be at most ${NAME_MAX2} printable characters`);return deps.patch({[`engineName.${id}`]:raw.length>0?raw:void 0}),{id,name:raw.length>0?raw:deps.engineName(id)}}},"engine.reset":{kind:"write",destructive:!0,wraps:"state.json engineCommand/engineName/engineProtocol/customEngineIds, as Settings \u2192 Engines `x`",async run(args){let state=deps.load(),ids=await engineIds(deps,state),id=listedId(args,ids),clear={[`engineCommand.${id}`]:void 0,[`engineName.${id}`]:void 0},custom=strings(state,"customEngineIds");if(!custom.includes(id))return deps.patch(clear),{id,removed:!1};let off=strings(state,"disabledEngineIds"),handoff=off.includes(id)?{}:afterLeaving(state,ids,id,off);return deps.patch({...clear,[`engineProtocol.${id}`]:void 0,customEngineIds:without(custom,id),disabledEngineIds:without(off,id),...handoff}),{id,removed:!0}}}}}var engineOps=createEngineOps(realDeps4);var taskOpDeps={savedRepos:()=>getSavedRepos(),listLocalBranches,getCurrentBranch,validateRepoPath,cloneRepo,validateGitUrl,validateCloneTarget,resolveCloneTarget,findAvailableFolderName,deriveFolderName},GIT_REF=/^[A-Za-z0-9][A-Za-z0-9._/-]{0,199}$/,MODEL=/^[A-Za-z0-9][A-Za-z0-9._:/@+-]{0,99}$/,EFFORT_LEVEL=/^[a-z0-9][a-z0-9-]{0,19}$/,ENGINE_ID=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/,FOLDER=/^[A-Za-z0-9_][A-Za-z0-9._-]{0,99}$/;function gitRef(args,name){let v=args[name];if(typeof v!=="string"||!GIT_REF.test(v)||v.includes("..")||v.includes("//")||v.includes("/.")||v.endsWith("/")||v.endsWith(".")||v.endsWith(".lock"))throw new BridgeError("BAD_ARGS",`${name} is not a valid git ref name`);return v}function optGitRef(args,name){let v=args[name];return v===void 0||v===null||v===""?void 0:gitRef(args,name)}function model(args,name="model"){let v=args[name];if(typeof v!=="string"||!MODEL.test(v))throw new BridgeError("BAD_ARGS",`${name} is not a model id`);return v}function optModel(args,name="model"){let v=args[name];return v===void 0||v===null||v===""?void 0:model(args,name)}function effortLevel(args,name="level"){let v=args[name];if(typeof v!=="string"||!EFFORT_LEVEL.test(v))throw new BridgeError("BAD_ARGS",`${name} is not an effort level`);return v}async function knownEngineIds(ctx){let res=await ctx.api.verb("engine-list",[]);return new Set((res.engines??[]).map((e)=>e.id))}function checkEngineShape(id,name){if(typeof id!=="string"||!ENGINE_ID.test(id))throw new BridgeError("BAD_ARGS",`${name} is not an engine id`);return id}async function engineId(args,name,ctx){let id=checkEngineShape(args[name],name);if(!(await knownEngineIds(ctx)).has(id))throw new BridgeError("UNKNOWN_ENGINE",`${name} ${id} is not a known engine`);return id}var FANOUT_CAP=10;function parseAgents(args,name="agents"){let raw=args[name];if(typeof raw!=="string"||raw.length===0||raw.length>300)throw new BridgeError("BAD_ARGS",`${name} must be engine:count pairs like claude:2,codex:1`);let plan=[],total=0;for(let part of raw.split(",")){let m=/^([^:]+):(\d{1,2})$/.exec(part);if(!m)throw new BridgeError("BAD_ARGS",`${name} entry "${part}" must be engine:count`);let id=checkEngineShape(m[1],name),n=Number.parseInt(m[2],10);if(n<1)throw new BridgeError("BAD_ARGS",`${name} count for ${id} must be at least 1`);if(plan.some((p)=>p.id===id))throw new BridgeError("BAD_ARGS",`${name} names ${id} twice`);if(total+=n,total>FANOUT_CAP)throw new BridgeError("BAD_ARGS",`${name} asks for more than ${FANOUT_CAP} agents`);plan.push({id,n})}return plan}async function checkAgentEngines(plan,ctx){let known=await knownEngineIds(ctx);for(let{id}of plan)if(!known.has(id))throw new BridgeError("UNKNOWN_ENGINE",`agents engine ${id} is not a known engine`)}async function knownRepo(args,ctx,name="repo"){let repo=absPath(args,name);if(taskOpDeps.savedRepos().includes(repo))return repo;let{tasks}=await ctx.api.rpc("task.list");if(!tasks.some((t)=>t.repo===repo))throw new BridgeError("UNKNOWN_REPO",`${repo} is not a known project`);return repo}var URL_SCHEME=/^(https?|ssh|git):\/\/[A-Za-z0-9[]\S*$/i,URL_SCP=/^[A-Za-z0-9._-]+@[A-Za-z0-9][A-Za-z0-9.-]*:\S+$/;function cloneUrl(args,name="url"){let v=args[name];if(typeof v!=="string")throw new BridgeError("BAD_ARGS",`${name} must be a git URL`);let url=v.trim(),lower=url.toLowerCase();if(url.length===0||url.length>2000||url.startsWith("-")||lower.startsWith("ext::")||lower.startsWith("fd::")||lower.startsWith("file:")||url.includes("@-")||/\p{Cc}/u.test(url)||!(URL_SCHEME.test(url)||URL_SCP.test(url)))throw new BridgeError("BAD_ARGS",`${name} must be an https, http, ssh or git URL (or user@host:path)`);return url}function cloneFolder(folder){if(!FOLDER.test(folder)||folder==="."||folder==="..")throw new BridgeError("BAD_ARGS","folder must be one plain directory name");return folder}var MODE_KEY="autoRouting.classifier",ENDPOINT_KEY="autoRouting.classifierEndpoint",CLASSIFIER_MODES=["off","jev","custom"],real={...realDeps4,secret:(name)=>secretStatus(name).source},str2=(v)=>typeof v==="string"?v.trim():"";async function pickable(deps,ids){return ids.map((id)=>{let entry=protocolEntry(id);return{id,name:deps.engineName(id),effortLevels:entry.effortLevels??[],takesModel:Boolean(entry.modelArgv)}})}function classifierView(deps,state){let config=readClassifierConfig((key)=>state[key]),mode=config.mode.kind==="url"?"custom":config.mode.kind==="jev"?"jev":"off",endpoint=displayEndpoint(config.mode.kind,str2(state[MODE_KEY]),str2(state[ENDPOINT_KEY])),host="";try{host=endpoint?new URL(endpoint).host:""}catch{host=""}return{mode,endpointHost:host,canCustom:str2(state[ENDPOINT_KEY]).length>0&&readClassifierConfig((k)=>k===MODE_KEY?state[ENDPOINT_KEY]:state[k]).mode.kind==="url",threshold:config.threshold,keyConfigured:config.keyEnv!==void 0,keySource:config.keyEnv?deps.secret(config.keyEnv):"none",...config.keyEnv?{keyEnv:config.keyEnv}:{}}}function createRoutingOps(deps){return{"autoRouting.get":{kind:"read",destructive:!1,wraps:"settings Auto routing section: readAutoRoutingTable + tierBlock over the engine statuses + readClassifierConfig",async run(){let state=deps.load(),ids=await engineIds(deps,state),statuses=await deps.statuses(ids).catch(()=>[]),table=readAutoRoutingTable((key)=>state[key]),engineSet=new Set(ids);return{configured:table!==null,tiers:table?AUTO_ROUTING_TIERS.map((tier)=>{let target=table[tier],block=tierBlock(target,{engineIds:engineSet,accountKind:(engine)=>statuses.find((s)=>s.vendor===engine)?.account?.kind??null});return{tier,engine:target.engine,engineName:deps.engineName(target.engine),model:target.model??null,effort:target.effort??null,block:block?describeTierBlock(block):null}}):[],engines:await pickable(deps,ids),classifier:classifierView(deps,state)}}},"autoRouting.setTier":{kind:"write",destructive:!0,wraps:"state.json autoRouting.<tier>.engine|model|effort, as the Auto routing row's engine picker",async run(args){let tier=oneOf(args,"tier",AUTO_ROUTING_TIERS),state=deps.load(),engine=(await pickable(deps,await engineIds(deps,state))).find((e)=>e.id===args.engine);if(!engine)throw new BridgeError("BAD_ARGS","engine is not a listed engine");let model2=optModel(args,"model")??"";if(model2&&!engine.takesModel)throw new BridgeError("BAD_ARGS",`${engine.id} takes no model`);let effort=typeof args.effort==="string"?args.effort:"";if(effort&&!engine.effortLevels.includes(effort))throw new BridgeError("BAD_ARGS",`effort must be one of ${engine.effortLevels.join("|")||"(none)"}`);return deps.patch({[autoRoutingKey(tier,"engine")]:engine.id,[autoRoutingKey(tier,"model")]:model2,[autoRoutingKey(tier,"effort")]:effort}),{tier,engine:engine.id,model:model2||null,effort:effort||null}}},"autoRouting.setClassifier":{kind:"write",destructive:!0,wraps:"state.json autoRouting.classifier: off | jev | the endpoint the TUI remembered (never a client-sent URL)",async run(args){let mode=oneOf(args,"mode",CLASSIFIER_MODES),state=deps.load(),view=classifierView(deps,state),live=str2(state[MODE_KEY]),remember=view.mode==="custom"&&live?{[ENDPOINT_KEY]:live}:{};if(mode==="custom"){if(!view.canCustom)throw new BridgeError("BAD_ARGS","no custom endpoint is remembered; set one in the TUI");deps.patch({[MODE_KEY]:str2(state[ENDPOINT_KEY])})}else deps.patch({...remember,[MODE_KEY]:mode});return{mode}}}}}var routingOps=createRoutingOps(real);class UsageStore{rows=null;sessions=null;set(snapshot){this.rows=snapshot===null?null:[...snapshot.entries()].map(([vendor,usage])=>({vendor,capturedAt:usage.capturedAt,windows:usage.windows.map(({kind,label,percent,resetsAt})=>({kind,label,percent,resetsAt}))})).sort((a,b)=>a.vendor.localeCompare(b.vendor))}get(){return this.rows}setContext(map){this.sessions=map}context(session){return this.sessions?.get(session)??null}}var usageStore=new UsageStore;var PLUGIN_ID2=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/,CATEGORY=/^[a-z0-9][a-z0-9-]{0,39}$/,realDeps5={usage:usageStore,bridgeVersion:CURRENT_VERSION,engineName:engineDisplayName,listPlugins:()=>readPluginRows(),pluginIds:()=>loadPluginRegistry().plugins.map((p)=>p.id),setPluginEnabled:(id,enabled)=>setPluginEnabled(id,enabled)};function createSettingsOps(deps){return{"usage.get":{kind:"read",destructive:!1,wraps:"the orchestrator's usageSnapshotSignal (daemon usage.snapshot) and, given taskId+tabId, its contextUsageSignal for that session; both held by the bridge",async run(args){let rows=deps.usage.get(),usage=rows===null?null:rows.map((row)=>({...row,name:deps.engineName(row.vendor)}));if(args.taskId===void 0)return{usage};return{usage,context:deps.usage.context(`${taskId(args)}::${tabId(args)}`)}}},"daemon.info":{kind:"read",destructive:!1,wraps:"daemon RPC daemon.status, compared with the bridge's own Rove build (isDaemonVersionStale)",async run(_args,{api}){let status=await api.rpc("daemon.status",{});return{daemonVersion:status.roveVersion??null,bridgeVersion:deps.bridgeVersion,stale:isDaemonVersionStale(status.roveVersion,deps.bridgeVersion),...status.uptimeMs!==void 0?{uptimeMs:status.uptimeMs}:{},...status.startedAt!==void 0?{startedAt:status.startedAt}:{},...status.taskCount!==void 0?{taskCount:status.taskCount}:{},...status.attachedClients!==void 0?{attachedClients:status.attachedClients}:{},...status.automationHold!==void 0?{automationHold:status.automationHold}:{}}}},"plugins.list":{kind:"read",destructive:!1,wraps:"settings Plugins section's readPluginRows (registry + manifest + last run)",async run(){return{plugins:deps.listPlugins().map((p)=>({id:p.id,version:p.version,enabled:p.enabled,linked:p.linked,platformOk:p.platformOk,hooksDeclared:p.hooksDeclared,updateAvailable:p.updateAvailable,declares:p.declares,lastRun:p.lastRun?{at:p.lastRun.at,label:p.lastRun.label,ok:p.lastRun.ok,running:p.lastRun.running}:null}))}}},"plugin.setEnabled":{kind:"write",destructive:!0,wraps:"settings Plugins section's setPluginEnabled (plugins.json; the daemon file-watches it)",async run(args){let id=text(args,"id",64);if(!PLUGIN_ID2.test(id)||!deps.pluginIds().includes(id))throw new BridgeError("BAD_ARGS",`${id} is not an installed plugin`);let enabled=bool(args,"enabled");return deps.setPluginEnabled(id,enabled),{id,enabled}}},"feedback.send":{kind:"write",destructive:!0,wraps:"rove api feedback (GitHub Discussion in the Rove repo's Feedback category, via gh)",run(args,{api}){let argv=[flag("title",text(args,"title",200).trim()),flag("body",text(args,"body",1e4))],category=optText(args,"category",40);if(category!==void 0){if(!CATEGORY.test(category))throw new BridgeError("BAD_ARGS","category is not a category slug");argv.push(flag("category",category))}return api.verb("feedback",argv)}}}}var settingsOps=createSettingsOps(realDeps5);import{existsSync as existsSync2,realpathSync,statSync}from"fs";import{homedir}from"os";import{extname,join as join2,sep}from"path";var SUBDIR_CAP=200,PATH_MAX3=1000;function reorderPersistedTab(taskId2,tab,delta){let moved=!1;return updateStateFile((store)=>{let state=store[terminalTabsKey(taskId2)];if(!state||!Array.isArray(state.tabs))return!1;let next=moveTab(state,tab,delta);if(next===state)return!1;store[terminalTabsKey(taskId2)]=next,moved=!0;return}),moved}function canonical(path){try{return realpathSync(path)}catch{return null}}function runnable(path){let info=statSync(path);return!info.isFile()||(info.mode&73)!==0}var OPENABLE=new Set("pdf png jpg jpeg gif webp bmp tif tiff heic svg ico mp3 wav m4a aac flac ogg mp4 m4v mov webm mkv avi ttf otf".split(" ")),realDeps6={openExternal:openExternally,detectOpener:detectWorktreeOpener,openWorktree,exists:existsSync2,canonical,runnable,homeDir:homedir,buildCIPrompt:buildCIPromptForWorktree,reorderTab:reorderPersistedTab};function typedPath(args,name){let v=args[name];if(typeof v!=="string"||v.length>PATH_MAX3||v.includes("\x00"))throw new BridgeError("BAD_ARGS",`${name} must be a path`);let trimmed2=v.trim();if(!(trimmed2.startsWith("/")||trimmed2.startsWith("~")))throw new BridgeError("BAD_ARGS",`${name} must be absolute or start with ~`);return trimmed2}function clearable(args,name,check){let v=args[name];if(v===void 0||v===null)return;return v===""?"":check(args,name)}async function worktreeAndBranch(ctx,id){let res=await ctx.api.verb("get-task",[flag("task-id",id)]),{worktreePath,branch,prStatus}=res.task;if(!worktreePath)throw new BridgeError("NO_WORKTREE",`task ${id} has no worktree yet`);return{worktree:worktreePath,branch:branch??"",prNumber:prStatus?.number}}function taskOps(deps=realDeps6){return{"task.landPreflight":{kind:"read",destructive:!1,wraps:"rpc task.landPreflight",async run(a,{api}){return(await api.rpc("task.landPreflight",{taskId:taskId(a)})).result}},"task.syncBase":{kind:"write",destructive:!1,wraps:"rpc task.syncBase (merge base into the worktree)",async run(a,{api}){let res=await api.rpc("task.syncBase",{taskId:taskId(a)});return{baseRef:res.result.baseRef,alreadyCurrent:res.result.alreadyCurrent===!0}}},"task.fixChecks":{kind:"write",destructive:!1,wraps:"rpc pr.failingChecks + buildCIPromptForWorktree + rove api send --plain",async run(a,ctx){let id=taskId(a),{worktree,branch,prNumber}=await worktreeAndBranch(ctx,id),read=await ctx.api.rpc("pr.failingChecks",{taskId:id}),checks=read.checks??[];if(read.unavailable)return{outcome:"unavailable",detail:read.unavailable.detail};if(checks.length===0)return{outcome:"none"};let state={checks,totalFailing:read.totalFailing??checks.length},prompt=await deps.buildCIPrompt(worktree,{branch:branch||"HEAD",...prNumber===void 0?{}:{prNumber},...state}),argv=[flag("task-id",id),"--plain",flag("prompt",prompt)];if(a.tabId!==void 0&&a.tabId!==null)argv.push(flag("tab",tabId(a)));return await ctx.api.verb("send",argv),{outcome:"sent"}}},"task.openEditor":{kind:"write",destructive:!1,wraps:"helpers detectWorktreeOpener + openWorktree (the TUI's `o`) on the task's worktree, via ensure-worktree",async run(a,{api}){let id=taskId(a),path=(await api.verb("get-task",[flag("task-id",id)])).task.worktreePath??"";if(!path||!deps.exists(path))path=(await api.verb("ensure-worktree",[flag("task-id",id)])).worktreePath??"";if(!path||!deps.exists(path))throw new BridgeError("NO_WORKTREE","this task has no worktree on disk");let opener=deps.detectOpener();if(!opener)return{outcome:"noEditor"};if(!deps.openWorktree(path,opener))return{outcome:"failed",label:opener.label};return{outcome:"opened",label:opener.label}}},"task.openScratch":{kind:"write",destructive:!1,wraps:"rpc task.openDir {dir: $HOME, scratch: true}",async run(a,ctx){let vendor=optStr(a,"engine")===void 0?void 0:await engineId(a,"engine",ctx);return{taskId:(await ctx.api.rpc("task.openDir",{dir:deps.homeDir(),scratch:!0,...vendor?{vendor}:{}})).taskId}}},"task.setEngine":{kind:"write",destructive:!1,wraps:"rpc task.setVendor (engine + reasoning level + model; '' clears)",async run(a,ctx){let id=taskId(a),vendor=await engineId(a,"engine",ctx),effort=clearable(a,"effort",(args,n)=>effortLevel(args,n)),mdl=clearable(a,"model",(args,n)=>model(args,n));return await ctx.api.rpc("task.setVendor",{taskId:id,vendor,...effort===void 0?{}:{effort},...mdl===void 0?{}:{model:mdl}}),{}}},"tab.move":{kind:"write",destructive:!1,wraps:"state.json terminalTabs.<task> order (moveTab)",async run(a){let direction=oneOf(a,"direction",["up","down"]);return{moved:deps.reorderTab(taskId(a),tabId(a),direction==="up"?-1:1)}}},"file.openExternal":{kind:"write",destructive:!1,wraps:"helper openExternally (the Mac's `open`) on a media/document file inside the task's worktree",async run(a,{api}){let rel=optStr(a,"path")??"";if(!rel||rel.length>PATH_MAX3||rel.startsWith("/")||rel.includes("\x00")||rel.split(/[\\/]/).includes(".."))throw new BridgeError("BAD_ARGS","path must be relative to the worktree");let worktree=(await api.verb("get-task",[flag("task-id",taskId(a))])).task.worktreePath;if(!worktree)throw new BridgeError("NO_WORKTREE","this task has no worktree yet");let root=deps.canonical(worktree),abs=deps.canonical(join2(worktree,rel));if(!root||!abs)throw new BridgeError("NOT_FOUND",`${rel} does not exist`);if(abs!==root&&!abs.startsWith(root.endsWith(sep)?root:root+sep))throw new BridgeError("BAD_ARGS","path is outside the worktree");if(!OPENABLE.has(extname(abs).slice(1).toLowerCase())||deps.runnable(abs))throw new BridgeError("NOT_OPENABLE","only images, audio, video, PDFs and fonts open in the system app");return deps.openExternal(abs),{}}},"fs.subdirs":{kind:"read",destructive:!1,wraps:"helpers splitPathForDirSuggest + listSubdirs + filterSubdirs",async run(a){let split=splitPathForDirSuggest(typedPath(a,"path")),names=filterSubdirs(listSubdirs(split.base),split.filter);return{base:split.base,home:deps.homeDir(),names:names.slice(0,SUBDIR_CAP)}}},"repo.probe":{kind:"read",destructive:!1,wraps:"helpers validateRepoPath + listLocalBranches + getCurrentBranch (+ rpc worktree.discoverAdoptable)",async run(a,{api}){let repo=stripTrailingSlash(expandHome(typedPath(a,"path"))),reason=taskOpDeps.validateRepoPath(repo);if(reason)return{ok:!1,repo,reason};let out={ok:!0,repo,branches:taskOpDeps.listLocalBranches(repo),current:taskOpDeps.getCurrentBranch(repo)};if(a.adoptable===!0){let found=await api.rpc("worktree.discoverAdoptable",{repo});out.worktrees=found.worktrees??[]}return out}}}}var TASKOPS_OPS=taskOps();var STATUSES=["backlog","in_progress","in_review","done","canceled","error"],DIRECTIONS=["up","down","top"],PROMPT_MAX2=1e5;function write(wraps,run,destructive=!1){return{kind:"write",destructive,wraps,run:async(args,ctx)=>run(args,ctx)}}async function done(call){return await call,{}}async function spawn(args,ctx){let repo=absPath(args,"repo"),title=optText(args,"title",200),prompt=optText(args,"prompt",PROMPT_MAX2),branch=optGitRef(args,"branch"),baseBranch=optGitRef(args,"baseBranch"),mdl=optModel(args),effort=args.effort===void 0||args.effort===null||args.effort===""?void 0:effortLevel(args,"effort"),count=optInt(args,"count",1,10),agentsGiven=args.agents!==void 0&&args.agents!==null&&args.agents!=="",status=optOneOf(args,"status",STATUSES),pin=optBool(args,"pin"),engineGiven=args.engine!==void 0&&args.engine!==null&&args.engine!=="",parallel=count!==void 0||agentsGiven;if(count!==void 0&&agentsGiven)throw new BridgeError("BAD_ARGS","count and agents are exclusive");if(parallel&&!prompt)throw new BridgeError("BAD_ARGS","count/agents need a prompt");if(parallel&&branch)throw new BridgeError("BAD_ARGS","branch cannot be shared by parallel tasks");if(agentsGiven&&engineGiven)throw new BridgeError("BAD_ARGS","engine conflicts with agents");let plan=agentsGiven?parseAgents(args):void 0;if(plan)await checkAgentEngines(plan,ctx);let engine=engineGiven?await engineId(args,"engine",ctx):void 0,argv=[flag("repo",repo)];if(title)argv.push(flag("title",title));if(branch)argv.push(flag("branch",branch));if(baseBranch)argv.push(flag("base-branch",baseBranch));if(engine)argv.push(flag("command",engine));if(effort)argv.push(flag("effort",effort));if(mdl)argv.push(flag("model",mdl));if(count!==void 0)argv.push(flag("count",count));if(plan)argv.push(flag("agents",plan.map((p)=>`${p.id}:${p.n}`).join(",")));if(status)argv.push(flag("status",status));if(pin)argv.push(flag("pin","true"));if(prompt)argv.push(flag("prompt",prompt));let res=await ctx.api.verb("add",argv);if(Array.isArray(res.tasks))return{taskIds:res.tasks.flatMap((t)=>typeof t.taskId==="string"?[t.taskId]:[]),...res.groupId?{groupId:res.groupId}:{}};if(typeof res.taskId!=="string")throw new BridgeError("BAD_RESULT","add returned no task id");return{taskIds:[res.taskId]}}var PROGRESS_GAP_MS=150;async function clone(args,ctx){let url=cloneUrl(args),parentDir=absPath(args,"parentDir"),urlProblem=taskOpDeps.validateGitUrl(url);if(urlProblem)throw new BridgeError("BAD_ARGS",urlProblem);let given2=optStr(args,"folder"),folder=cloneFolder(given2??taskOpDeps.findAvailableFolderName(parentDir,taskOpDeps.deriveFolderName(url))),targetProblem=taskOpDeps.validateCloneTarget(parentDir,folder);if(targetProblem)throw new BridgeError("BAD_ARGS",targetProblem);let lastSent=0,result=await taskOpDeps.cloneRepo(url,taskOpDeps.resolveCloneTarget(parentDir,folder),(line)=>{let now=Date.now();if(now-lastSent<PROGRESS_GAP_MS)return;lastSent=now,ctx.progress?.(line)});if(!result.ok)throw new BridgeError("CLONE_FAILED",result.error);return{path:result.path}}async function adopt(args,ctx){let repo=await knownRepo(args,ctx),worktreePath=absPath(args,"worktreePath"),branch=optGitRef(args,"branch"),title=optText(args,"title",200),engine=args.engine===void 0||args.engine===null||args.engine===""?void 0:await engineId(args,"engine",ctx);if(!((await ctx.api.rpc("worktree.discoverAdoptable",{repo})).worktrees??[]).some((w)=>w.path===worktreePath))throw new BridgeError("NOT_ADOPTABLE",`${worktreePath} is not an adoptable worktree of ${repo}`);return{taskId:(await ctx.api.rpc("worktree.adopt",{repo,worktreePath,...branch?{branch}:{},...title?{title}:{},...engine?{vendor:engine}:{}})).task.id}}var TASK_WRITE_OPS={"task.spawn":write("verb add",spawn),"task.rename":write("verb update --task-id --title",(a,c)=>done(c.api.verb("update",[flag("task-id",taskId(a)),flag("title",text(a,"title",200))]))),"task.setBranch":write("verb update --task-id --branch",(a,c)=>done(c.api.verb("update",[flag("task-id",taskId(a)),flag("branch",gitRef(a,"branch"))]))),"task.setCommand":write("verb update --task-id --command=<engine id>",async(a,c)=>{let id=taskId(a),engine=await engineId(a,"engine",c),res=await c.api.verb("update",[flag("task-id",id),flag("command",engine)]);return res.protocol?{protocol:res.protocol}:{}}),"task.setModel":write("verb update --task-id --model",(a,c)=>done(c.api.verb("update",[flag("task-id",taskId(a)),flag("model",model(a))]))),"task.setEffort":write("verb update --task-id --effort",(a,c)=>done(c.api.verb("update",[flag("task-id",taskId(a)),flag("effort",effortLevel(a))]))),"task.setStatus":write("verb update --task-id --status",(a,c)=>done(c.api.verb("update",[flag("task-id",taskId(a)),flag("status",oneOf(a,"status",STATUSES))]))),"task.pin":write("verb update --task-id --pinned",(a,c)=>done(c.api.verb("update",[flag("task-id",taskId(a)),flag("pinned",String(bool(a,"pinned")))]))),"task.move":write("rpc task.move",(a,c)=>done(c.api.rpc("task.move",{taskId:taskId(a),direction:oneOf(a,"direction",DIRECTIONS)}))),"project.forget":write("rpc project.forget",async(a,c)=>done(c.api.rpc("project.forget",{repo:await knownRepo(a,c)})),!0),"notes.delete":write("verb note-delete --repo --id",async(a,c)=>{let repo=await knownRepo(a,c),id=optInt(a,"id",1,Number.MAX_SAFE_INTEGER);if(id===void 0)throw new BridgeError("BAD_ARGS","id must be an integer in 1..");return{deleted:(await c.api.verb("note-delete",[flag("repo",repo),flag("id",id)])).deleted===!0}},!0),"task.openMain":write("rpc task.ensureMain",async(a,c)=>{let repo=absPath(a,"repo"),problem=taskOpDeps.validateRepoPath(repo);if(problem)throw new BridgeError("BAD_ARGS",problem);return{taskId:(await c.api.rpc("task.ensureMain",{repo})).task.id}}),"worktree.adopt":write("rpc worktree.adopt",adopt),"repo.clone":write("helper cloneRepo (git clone, fixed argv)",clone),"task.ensureWorktree":write("verb ensure-worktree --task-id",async(a,c)=>{let res=await c.api.verb("ensure-worktree",[flag("task-id",taskId(a))]);if(typeof res.worktreePath!=="string")throw new BridgeError("BAD_RESULT","ensure-worktree returned no path");return{worktreePath:res.worktreePath}}),"task.removeWorktree":write("verb remove-worktree --task-id --force",async(a,c)=>{let argv=[flag("task-id",taskId(a))];if(optBool(a,"force"))argv.push(flag("force","true"));let res=await c.api.verb("remove-worktree",argv);return{removed:res.removed===!0,...res.worktreePath?{worktreePath:res.worktreePath}:{},...res.branch?{branch:res.branch}:{}}},!0)};var TAIL_MAX=2000;function read(wraps,run){return{kind:"read",destructive:!1,wraps,run}}async function taskGet(args,ctx){let t=(await ctx.api.verb("get-task",[flag("task-id",taskId(args))])).task,pr=t.prStatus;return{task:{id:t.id,title:t.title,repo:t.repo,branch:t.branch,worktreePath:t.worktreePath,kind:t.kind??"task",status:t.status,pinned:t.pinned===!0,...t.vendor?{engine:t.vendor}:{},...t.command?{command:t.command}:{},...t.model?{model:t.model}:{},...t.modelEffort?{effort:t.modelEffort}:{},...t.prompt?{prompt:t.prompt}:{},...t.baseRef?{baseRef:t.baseRef}:{},...t.groupId?{groupId:t.groupId}:{},createdAt:t.createdAt,updatedAt:t.updatedAt,...pr?{pr:{...pr.number!==void 0?{number:pr.number}:{},...pr.url?{url:pr.url}:{},lifecycle:pr.lifecycle,checkState:pr.checkState,...pr.reviewDecision?{reviewDecision:pr.reviewDecision}:{},...pr.mergeable?{mergeable:pr.mergeable}:{},...pr.baseRef?{baseRef:pr.baseRef}:{}}}:{},...t.report?{report:{summary:t.report.summary??"",at:t.report.at}}:{}}}}async function taskInfo(args,ctx){let id=taskId(args),row=(await ctx.api.verb("collect",[flag("task-ids",id)])).tasks?.[0];if(!row)throw new BridgeError("NOT_FOUND",`no task ${id}`);return{taskId:row.taskId,running:row.running??null,activity:row.activity?{state:row.activity.state,forMs:row.activity.forMs}:null,changes:row.changes?{added:row.changes.added,deleted:row.changes.deleted}:null,base:row.base?{baseRef:row.base.baseRef,ahead:row.base.ahead,behind:row.base.behind}:null,tabs:(row.tabs??[]).map((tab)=>{let exit=tab.exit,tail=exit?.tail?.join(`
39
- `).slice(-TAIL_MAX);return{id:tab.id,kind:tab.kind,alive:tab.alive??null,...exit?{exit:{code:exit.code,signal:exit.signal,...exit.layer?{cause:exit.layer}:{}}}:{},...tail?{tail}:{}}})}}async function repoBranches(args,ctx){let repo=await knownRepo(args,ctx);return{branches:taskOpDeps.listLocalBranches(repo),current:taskOpDeps.getCurrentBranch(repo)}}async function notesList(args,ctx){let repo=await knownRepo(args,ctx);return{notes:(await ctx.api.verb("note-list",[flag("repo",repo)])).notes??[]}}async function worktreeAdoptable(args,ctx){let repo=await knownRepo(args,ctx),res=await ctx.api.rpc("worktree.discoverAdoptable",{repo});return{worktrees:res.worktrees??[],unreadable:res.unreadable??[]}}var TASK_OPS={"task.get":read("verb get-task --task-id",taskGet),"task.info":read("verb collect --task-ids (first row, trimmed)",taskInfo),"repo.branches":read("helpers listLocalBranches + getCurrentBranch",repoBranches),"notes.list":read("verb note-list --repo",notesList),"worktree.adoptable":read("rpc worktree.discoverAdoptable",worktreeAdoptable),...TASK_WRITE_OPS};import{randomBytes as randomBytes2}from"crypto";import{mkdirSync as mkdirSync2,writeFileSync as writeFileSync2}from"fs";import{join as join3}from"path";var ATTACHMENT_TYPES={"image/png":{ext:"png",kind:"image",magic:[137,80,78,71,13,10,26,10]},"image/jpeg":{ext:"jpg",kind:"image",magic:[255,216,255]},"image/gif":{ext:"gif",kind:"image",magic:[71,73,70,56]},"image/webp":{ext:"webp",kind:"image",magic:[82,73,70,70]},"application/pdf":{ext:"pdf",kind:"pdf",magic:[37,80,68,70]}},MAX_ATTACHMENT_BYTES=5242880,MAX_ATTEMPTS=5,PROMPT_MAX3=1e5,realDeps7={attachmentsDir:promptAttachmentsDir,now:()=>new Date,nonce:()=>randomBytes2(4).toString("hex"),writeFile(path,bytes){mkdirSync2(join3(path,".."),{recursive:!0,mode:448}),writeFileSync2(path,bytes,{mode:384})},readTab(task,tab){return loadStateFile()[terminalTabsKey(task)]?.tabs?.find((t)=>t.id===tab)},planHandoff:planWorktreeHandoff,prPrompt:buildPRPrompt};function gitRef2(args,name){let v=args[name];if(!(typeof v==="string"&&v.length<=200&&/^[A-Za-z0-9][A-Za-z0-9._/@+-]*$/.test(v)&&!v.includes("..")&&!v.includes("//")&&!v.endsWith("/")&&!v.endsWith(".")&&!v.endsWith(".lock")&&!v.includes("@{")))throw new BridgeError("BAD_ARGS",`${name} is not a branch name`);return v}function engineId2(args,name="engine"){let v=optStr(args,name);if(v===void 0)return;if(!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$/.test(v))throw new BridgeError("BAD_ARGS",`${name} is not an engine id`);return v}function tabTitle(args){let title=text(args,"title",80).trim();if(/[\u0000-\u001f\u007f]/.test(title))throw new BridgeError("BAD_ARGS","title must be one line");return title}async function worktreeOf2(api,id){let res=await api.verb("get-task",[flag("task-id",id)]);if(!res.task.worktreePath)throw new BridgeError("NO_WORKTREE",`task ${id} has no worktree yet`);return{task:res.task,worktree:res.task.worktreePath}}function matchesMagic(mime,bytes){let{magic}=ATTACHMENT_TYPES[mime];if(bytes.length<magic.length||!magic.every((b,i)=>bytes[i]===b))return!1;return mime!=="image/webp"||Buffer.from(bytes.subarray(8,12)).toString("ascii")==="WEBP"}function terminalOps(deps=realDeps7){return{"tab.states":{kind:"read",destructive:!1,wraps:"daemon RPC debug.inspect (activity.tabs of one task)",async run(a,{api}){let id=taskId(a),res=await api.rpc("debug.inspect"),tabs={};for(let[tab,entry]of Object.entries(res?.activity?.tabs?.[id]??{}))if(typeof entry?.state==="string"&&typeof entry.at==="number")tabs[tab]={state:entry.state,at:entry.at};return{tabs}}},"tab.rename":{kind:"write",destructive:!1,wraps:"rove api update --tab",async run(a,{api}){return await api.verb("update",[flag("task-id",taskId(a)),flag("tab",tabId(a)),flag("title",tabTitle(a))]),{}}},"tab.interrupt":{kind:"write",destructive:!1,wraps:"rove api interrupt",async run(a,{api}){let argv=[flag("task-id",taskId(a))];if(a.tabId!==void 0&&a.tabId!==null)argv.push(flag("tab",tabId(a)));return await api.verb("interrupt",argv),{}}},"tab.forkTask":{kind:"write",destructive:!1,wraps:"rove api add --base-branch (--count for attempts)",async run(a,{api}){let count=optInt(a,"count",1,MAX_ATTEMPTS)??1,argv=[flag("repo",absPath(a,"repo")),flag("base-branch",gitRef2(a,"baseBranch")),flag("prompt",text(a,"prompt",PROMPT_MAX3))],engine=engineId2(a);if(engine)argv.push(flag("command",engine));let title=optText(a,"title",200);if(title)argv.push(flag("title",title));if(count>1)argv.push(flag("count",count));let res=await api.verb("add",argv),taskIds=res.tasks?res.tasks.map((t)=>t.taskId):res.taskId?[res.taskId]:[];if(taskIds.length===0)throw new BridgeError("ADD_FAILED","no task was created");return{taskIds}}},"tab.handoff":{kind:"read",destructive:!1,wraps:"planWorktreeHandoff (transcript handoff prompt)",async run(a,{api}){let id=taskId(a),tab=tabId(a),{task,worktree}=await worktreeOf2(api,id),active=deps.readTab(id,tab)??initialTabs().tabs[0],tabVendor=(active.kind==="engine"?active.vendor:void 0)??task.vendor,plan=await deps.planHandoff(active,liveSourceProtocol(active,tabVendor),worktree);return plan.kind==="handoff"?{kind:"handoff",prompt:plan.prompt}:plan.kind==="no-transcript"?{kind:plan.kind,engine:plan.engine}:{kind:plan.kind}}},"tab.requestPR":{kind:"write",destructive:!1,wraps:"buildPRPrompt + rove api send --plain",async run(a,{api}){let id=taskId(a),{worktree}=await worktreeOf2(api,id),argv=[flag("task-id",id),"--plain",flag("prompt",await deps.prPrompt(worktree))];if(a.tabId!==void 0&&a.tabId!==null)argv.push(flag("tab",tabId(a)));return await api.verb("send",argv),{}}},"attachment.put":{kind:"write",destructive:!1,wraps:"writes ~/.rove/attachments/<generated name> (tui/lib/attachments.ts layout)",async run(a){let mime=str(a,"mime");if(!Object.hasOwn(ATTACHMENT_TYPES,mime))throw new BridgeError("BAD_ARGS",`mime must be one of ${Object.keys(ATTACHMENT_TYPES).join("|")}`);let type=ATTACHMENT_TYPES[mime],encoded=str(a,"data");if(encoded.length>Math.ceil(MAX_ATTACHMENT_BYTES*4/3)+4)throw new BridgeError("TOO_LARGE",`attachment is larger than ${MAX_ATTACHMENT_BYTES} bytes`);let bytes=Buffer.from(encoded,"base64");if(bytes.length===0)throw new BridgeError("BAD_ARGS","data is empty");if(bytes.length>MAX_ATTACHMENT_BYTES)throw new BridgeError("TOO_LARGE",`attachment is larger than ${MAX_ATTACHMENT_BYTES} bytes`);if(!matchesMagic(mime,bytes))throw new BridgeError("BAD_ARGS",`data is not a ${type.ext} file`);let stamp=deps.now().toISOString().slice(0,10).replaceAll("-",""),path=join3(deps.attachmentsDir(),`attach-${stamp}-${deps.nonce()}.${type.ext}`);return deps.writeFile(path,bytes),{path,kind:type.kind,bytes:bytes.length}}}}}var TERMINAL_OPS=terminalOps();var VERSION=/^\d+\.\d+\.\d+$/,realDeps8={current:CURRENT_VERSION,check:(force)=>checkLatestVersion({force}),notesRange:(current,latest)=>fetchReleaseNotesRange({current,latest}),notes:fetchReleaseNotes};function createUpdateOps(deps=realDeps8){return{"update.check":{kind:"read",destructive:!1,wraps:"checkLatestVersion + fetchReleaseNotesRange (npm registry, GitHub releases)",async run(args){let info=await deps.check(optBool(args,"force")===!0),notes=info?.hasUpdate?await deps.notesRange(info.current,info.latest):[];return{current:deps.current,info,notes}}},"release.notes":{kind:"read",destructive:!1,wraps:"fetchReleaseNotes (GitHub release body for vX.Y.Z)",async run(args){let version=text(args,"version",32);if(!VERSION.test(version))throw new BridgeError("BAD_ARGS","version must be x.y.z");return{notes:await deps.notes(version)}}}}}var updateOps=createUpdateOps();import{basename}from"path";var chip=(id)=>({id,name:engineDisplayName(id)}),welcomeDeps={cwd:()=>process.cwd(),gitOnPath:()=>Bun.which("git")!==null,async engines(){return probeableEngineIds().then(detectEngineStatuses).then(summarizeEngines).catch(()=>({usable:[],signedOut:[]}))},repoOf(dir){try{return validateRepoPath(dir)===null?resolveMainRepoRoot(dir):null}catch{return null}},branchOf:(dir)=>getCurrentBranch(dir),preferredEngine:(repo)=>resolvePreferredVendor(repo)},welcomeOps={"welcome.env":{kind:"read",destructive:!1,wraps:"helpers probeableEngineIds + detectEngineStatuses, firstTaskRepo",async run(){let[{usable,signedOut},git]=await Promise.all([welcomeDeps.engines(),welcomeDeps.gitOnPath()]),dir=welcomeDeps.cwd(),repo=welcomeDeps.repoOf(dir),start=null;if(repo!==null&&git&&usable.length>0){let pref=welcomeDeps.preferredEngine(repo);start={repo,label:basename(repo),baseRef:welcomeDeps.branchOf(dir)??welcomeDeps.branchOf(repo)??DEFAULT_BASE_REF,engine:usable.includes(pref)?pref:usable[0]??pref}}return{engines:usable.map(chip),signedOut:signedOut.map(chip),git,start}}}};var LEAF=/^leaf-\d{1,4}$/,TITLE_MAX2=80;function leafId(a){let v=a.leafId;if(v===void 0||v===null||v==="")return;if(typeof v!=="string"||!LEAF.test(v))throw new BridgeError("BAD_ARGS","leafId is not a pane id");return v}function paneTitle(a){let v=a.title;if(v===void 0||v===null)return"";if(typeof v!=="string"||v.length>TITLE_MAX2||/[\u0000-\u001f\u007f]/.test(v))throw new BridgeError("BAD_ARGS",`title must be one line of at most ${TITLE_MAX2} characters`);return v.trim()}var WORKSPACE_OPS={"tab.split":{kind:"write",destructive:!0,wraps:"rove api tab-split",async run(a,{api}){let action=oneOf(a,"action",["open","close","rename","reset"]),leaf=leafId(a);if(action!=="open"&&!leaf)throw new BridgeError("BAD_ARGS",`leafId is required to ${action}`);let cols=optInt(a,"cols",1,1000),rows=optInt(a,"rows",1,500);if(cols===void 0!==(rows===void 0))throw new BridgeError("BAD_ARGS","cols and rows go together");let argv=[flag("task-id",taskId(a)),flag("tab",tabId(a)),flag("action",action)];if(leaf)argv.push(flag("leaf",leaf));if(action==="open")argv.push(flag("direction",optOneOf(a,"direction",["right","down"])??"right"));if(action==="rename")argv.push(flag("title",paneTitle(a)));if(cols!==void 0&&rows!==void 0)argv.push(flag("cols",cols),flag("rows",rows));let res=await api.verb("tab-split",argv);return{leaf:res.leaf,changed:res.changed,split:res.split}}}};function mergeOpTables(...tables){let merged={};for(let table of tables)for(let[name,spec]of Object.entries(table)){if(name in merged)throw new BridgeError("DUPLICATE_OP",`op ${name} is registered twice`);merged[name]=spec}return merged}var AREA_OPS=mergeOpTables(TASK_OPS,TERMINAL_OPS,filesOps,GATE_OPS,pagesOps,settingsOps,engineOps,insightOps,updateOps,machineOps,devOps,prefsOps,routingOps,TASKOPS_OPS,WORKSPACE_OPS,welcomeOps,keybindingsOps,launchOps,modsOps,promptOps);class TerminalForwarder{pty;send;attached=new Set;offs;constructor(pty,send){this.pty=pty;this.send=send;this.offs=[pty.on("pty.data",(frame)=>{let{key,data}=frame.payload;if(this.attached.has(key))this.send(pushEvent("term.data",{stream:key,data}))}),pty.on("pty.exit",(frame)=>{let{key,code}=frame.payload;if(this.attached.has(key))this.send(pushEvent("term.exit",{stream:key,code:code??null}))})]}async attach(taskId2,tabId2,cwd,size,leafId2){let stream=leafId2?`${taskId2}::${tabId2}::${leafId2}`:`${taskId2}::${tabId2}`,{sessions}=await this.pty.request("pty.list"),session=sessions.find((s)=>s.key===stream);if(!session)throw new BridgeError("TAB_NOT_LIVE",`tab ${tabId2} has no hosted session`);if(!session.alive&&session.restored){let peek=await this.pty.request("pty.peek",{key:stream});return{stream,alive:!1,replay:peek.data}}this.attached.add(stream);try{let open=await this.pty.request("pty.open",{key:stream,cwd,...size?{cols:size.cols,rows:size.rows}:{}});return{stream,alive:open.alive,replay:open.replay}}catch(err){throw this.attached.delete(stream),err}}async input(stream,data){this.requireAttached(stream),await this.pty.request("pty.write",{key:stream,data})}async resize(stream,cols,rows){this.requireAttached(stream),await this.pty.request("pty.resize",{key:stream,cols,rows})}async detach(stream){if(!this.attached.delete(stream))return;await this.pty.request("pty.detach",{key:stream})}dispose(){for(let off of this.offs)off();this.attached.clear(),this.pty.close()}requireAttached(stream){if(!this.attached.has(stream))throw new BridgeError("NOT_ATTACHED",`not attached to ${stream}`)}}import{stat}from"fs/promises";import{resolve,sep as sep2}from"path";var SECURITY_HEADERS={"Content-Security-Policy":"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'","X-Content-Type-Options":"nosniff","Referrer-Policy":"no-referrer"};async function serveWebFile(web,req){if(req.method!=="GET"&&req.method!=="HEAD")return null;let root=resolve(web.root),pathname;try{pathname=decodeURIComponent(new URL(req.url).pathname)}catch{return new Response(`bad path
40
- `,{status:400})}let target=resolve(root,`.${pathname}`);if(target!==root&&!target.startsWith(root+sep2))return new Response(`not found
41
- `,{status:404});let isFile=await stat(target).then((s)=>s.isFile(),()=>!1),file=Bun.file(target);if(!isFile){if(pathname.startsWith("/assets/"))return new Response(`not found
42
- `,{status:404});if(file=Bun.file(resolve(root,"index.html")),!await file.exists())return new Response(`web client not built
43
- `,{status:503})}let cache=target.startsWith(resolve(root,"assets")+sep2)?"public, max-age=31536000, immutable":"no-cache";return new Response(req.method==="HEAD"?null:file,{headers:{...SECURITY_HEADERS,"Content-Type":file.type,"Cache-Control":cache}})}async function handle(deps,ws,req){let{ops}=deps,a=req.args,conn=ws.data,terminal=()=>{return conn.terminal??=new TerminalForwarder(deps.openPty(),(frame)=>ws.send(frame)),conn.terminal},terminalFor=async(taskId2)=>{let alias=ops.machineOf?.(taskId2);if(alias===void 0)return terminal();if(!deps.openMachinePty)throw new BridgeError("MACHINE_UNREACHABLE",`this bridge cannot reach machine ${alias}`);let open=deps.openMachinePty,pending=conn.machineTerminals.get(alias);if(!pending)pending=open(alias).then((pty)=>new TerminalForwarder(pty,(frame)=>ws.send(frame))),pending.catch(()=>conn.machineTerminals.delete(alias)),conn.machineTerminals.set(alias,pending);return pending},owner=(stream)=>conn.streamOwner.get(stream)??terminal();if(DESTRUCTIVE_CORE[req.op])logDestructive(deps,req);switch(req.op){case"hello":return{protocol:BRIDGE_PROTOCOL_VERSION,apiVersion:ROVE_API_VERSION,roveVersion:deps.roveVersion,host:osHostname()};case"api.invoke":{if(!deps.invoke)throw new BridgeError("UNKNOWN_OP","this bridge does not serve verbs");let verb=str(a,"verb"),params=a.params??{};if(typeof params!=="object"||params===null||Array.isArray(params))throw new BridgeError("BAD_ARGS","params must be an object keyed by flag name");if(DESTRUCTIVE_VERBS.has(verb))logDestructiveVerb(deps,verb,params);let envelope=await deps.invoke(verb,params,optStr(a,"machine"));if(envelope.ok)return envelope.result;throw new BridgeError(envelope.error.code,envelope.error.message,envelope.error)}case"tasks.subscribe":{let snapshot=await deps.feed.current();return conn.unsubscribeTasks??=deps.feed.subscribe((payload)=>ws.send(pushEvent("tasks",payload))),conn.unsubscribeTabs??=deps.tabs?.subscribe((taskIds)=>ws.send(pushEvent("tabs",{taskIds})))??null,snapshot}case"tasks.list":return deps.feed.refresh();case"engines.list":return{engines:await ops.engines()};case"repos.list":return{repos:await ops.repos()};case"task.create":return ops.createTask({repo:str(a,"repo"),engine:optStr(a,"engine"),prompt:optStr(a,"prompt"),title:optStr(a,"title")});case"task.delete":return ops.deleteTask(str(a,"taskId"),optBool(a,"force"));case"task.land":{let strategy=optStr(a,"strategy")??"merge";if(strategy!=="merge"&&strategy!=="squash")throw new BridgeError("BAD_ARGS","strategy is merge|squash");return ops.landTask(str(a,"taskId"),strategy)}case"task.tabs":return{tabs:await ops.tabs(str(a,"taskId"))};case"tab.new":{let prompt=optStr(a,"prompt"),kind=optStr(a,"kind")??"engine";if(kind!=="shell"&&kind!=="engine")throw new BridgeError("BAD_ARGS","kind is shell|engine");let engine=optStr(a,"engine");if(kind==="shell"&&(prompt!==void 0||engine!==void 0))throw new BridgeError("BAD_ARGS","a shell tab takes no prompt or engine");return ops.newTab(str(a,"taskId"),{kind,prompt,engine})}case"tab.close":return await ops.closeTab(str(a,"taskId"),str(a,"tabId")),{};case"term.attach":{let taskId2=str(a,"taskId"),tabId2=str(a,"tabId"),leafId2=optStr(a,"leafId");if(leafId2!==void 0&&!/^leaf-\d{1,4}$/.test(leafId2))throw new BridgeError("BAD_ARGS","leafId is not a pane id");let cols=optInt(a,"cols",10,1000),rows=optInt(a,"rows",4,500);if(cols===void 0!==(rows===void 0))throw new BridgeError("BAD_ARGS","cols and rows go together");let shown=ops.machineOf?.(taskId2)!==void 0?null:ops.viewedSession(taskId2,tabId2),target=shown?.taskId??taskId2,forwarder=await terminalFor(target),cwd=shown?.cwd??await ops.worktreeOf(taskId2),attached=await forwarder.attach(target,shown?.tabId??tabId2,cwd,cols!==void 0&&rows!==void 0?{cols,rows}:void 0,leafId2);return conn.streamOwner.set(attached.stream,forwarder),attached}case"term.input":{let data=a.data;if(typeof data!=="string")throw new BridgeError("BAD_ARGS","data must be a string");return await owner(str(a,"stream")).input(str(a,"stream"),data),{}}case"term.resize":return await owner(str(a,"stream")).resize(str(a,"stream"),optInt(a,"cols",10,1000)??80,optInt(a,"rows",4,500)??24),{};case"term.detach":{let stream=str(a,"stream"),forwarder=conn.streamOwner.get(stream)??conn.terminal;conn.streamOwner.delete(stream),await forwarder?.detach(stream)}return{};case"diff.files":return ops.diffFiles(str(a,"taskId"));case"diff.file":{let scope=str(a,"scope");if(scope!=="branch"&&scope!=="working")throw new BridgeError("BAD_ARGS","scope is branch|working");return ops.diffFile(str(a,"taskId"),str(a,"path"),scope)}case"attention.dismiss":{let taskId2=str(a,"taskId"),tabId2=optStr(a,"tabId"),deliveryId=optStr(a,"deliveryId");if(optStr(a,"gateId")||!deliveryId&&dismissHitsOnlyGates((await deps.feed.current()).attention,taskId2,tabId2))throw new BridgeError("GATE_NOT_DISMISSABLE","a decision is answered with gate.answer, not dismissed");return await ops.dismissAttention(taskId2,tabId2,deliveryId),{}}default:{let spec=(deps.areaOps??AREA_OPS)[req.op];if(!spec)throw new BridgeError("UNKNOWN_OP",`unknown op: ${req.op}`);if(spec.destructive)logDestructive(deps,req);return spec.run(a,{api:deps.api,progress:(line)=>ws.send(pushEvent("op.progress",{id:req.id,line}))})}}}var DESTRUCTIVE_CORE={"task.delete":!0,"task.land":!0,"tab.close":!0},DESTRUCTIVE_VERBS=new Set(["delete","land","remove-worktree","tab-close","issue-delete","routine-delete","note-delete"]);function logDestructiveVerb(deps,verb,params){let ids=["task-id","tab","id"].filter((key)=>typeof params[key]==="string"||typeof params[key]==="number").map((key)=>`${key}=${String(params[key])}`);(deps.log??((line)=>console.error(line)))(`[rove-bridge] api.invoke ${verb} ${ids.join(" ")}`.trimEnd())}function logDestructive(deps,req){let ids=["taskId","tabId","path"].filter((key)=>typeof req.args[key]==="string").map((key)=>`${key}=${String(req.args[key])}`);(deps.log??((line)=>console.error(line)))(`[rove-bridge] ${req.op} ${ids.join(" ")}`.trimEnd())}async function onMessage(deps,ws,raw){let req,area=deps.areaOps??AREA_OPS;try{req=parseRequest(raw,(op)=>isCoreOp(op)||Object.hasOwn(area,op))}catch(err){let e=err instanceof BridgeError?err:new BridgeError("BAD_FRAME",String(err));ws.send(responseError(requestIdOf(err),e.code,e.message));return}try{ws.send(responseOk(req.id,await handle(deps,ws,req)))}catch(err){let code=err instanceof BridgeError?err.code:"INTERNAL",detail=err instanceof BridgeError?err.detail:void 0;ws.send(responseError(req.id,code,err instanceof Error?err.message:String(err),detail))}}function sameHost(origin,host){try{return host!==null&&new URL(origin).host===host.toLowerCase()}catch{return!1}}function startBridgeServer(deps,listen){return Bun.serve({hostname:listen.hostname,port:listen.port,async fetch(req,server){let log=deps.log??((line)=>console.error(line)),from=req.headers.get("cf-connecting-ip")??server.requestIP(req)?.address??"?";if(deps.access)try{await deps.access.verify(req.headers.get("cf-access-jwt-assertion"))}catch(err){return log(`[rove-bridge] 401 from ${from}: Cloudflare Access \u2014 ${err instanceof Error?err.message:String(err)}`),new Response(`unauthorized
44
- `,{status:401})}if(req.headers.get("upgrade")?.toLowerCase()!=="websocket"&&deps.web){let file=await serveWebFile(deps.web,req);if(file)return file}let origin=req.headers.get("origin");if(origin!==null&&!sameHost(origin,req.headers.get("host")))return log(`[rove-bridge] 403 from ${from}: cross-origin upgrade from ${origin}`),new Response(`forbidden
45
- `,{status:403});if(!tokenMatches(deps.token,presentedToken(req)))return log(`[rove-bridge] 401 from ${from}: missing or wrong bearer token`),new Response(`unauthorized
46
- `,{status:401});let data={terminal:null,machineTerminals:new Map,streamOwner:new Map,unsubscribeTasks:null,unsubscribeTabs:null,unsubscribeNotices:null,unsubscribeMods:null,unsubscribePrompts:null},headers=offersWebSubprotocol(req)?{"Sec-WebSocket-Protocol":WEB_SUBPROTOCOL}:void 0;if(server.upgrade(req,{data,headers}))return;return new Response(`rove-bridge speaks WebSocket only
47
- `,{status:426})},websocket:{open(ws){if(ws.data.unsubscribeNotices=deps.notices?.subscribe((n)=>ws.send(pushEvent("notice",n)))??null,ws.data.unsubscribePrompts=deps.prompts?.subscribe((p)=>ws.send(pushEvent("prompt",p)))??null,deps.mods)ws.send(pushEvent("mods",{panes:deps.mods.current()})),ws.data.unsubscribeMods=deps.mods.subscribe((panes)=>ws.send(pushEvent("mods",{panes})))},maxPayloadLength:8388608,message(ws,message){onMessage(deps,ws,typeof message==="string"?message:message.toString("utf8"))},close(ws){ws.data.unsubscribeTasks?.(),ws.data.unsubscribeTabs?.(),ws.data.unsubscribeNotices?.(),ws.data.unsubscribeMods?.(),ws.data.unsubscribePrompts?.(),ws.data.terminal?.dispose();for(let pending of ws.data.machineTerminals.values())pending.then((forwarder)=>forwarder.dispose(),()=>{})}}})}class TabFeed{read;latest;listeners=new Set;constructor(read2){this.read=read2;this.latest=this.snapshots()}check(){let next=this.snapshots(),changed=new Set;for(let[id,json]of next)if(this.latest.get(id)!==json)changed.add(id);for(let id of this.latest.keys())if(!next.has(id))changed.add(id);if(this.latest=next,changed.size===0)return;let taskIds=[...changed];for(let listener of this.listeners)listener(taskIds)}subscribe(listener){return this.listeners.add(listener),()=>this.listeners.delete(listener)}snapshots(){let out=new Map;for(let[key,value]of Object.entries(this.read()))if(key.startsWith("terminalTabs."))out.set(key.slice(13),JSON.stringify(value));return out}}var TICK_MS=15000,TABS_DEBOUNCE_MS=200,TAILSCALE_BINARIES=["tailscale","/Applications/Tailscale.app/Contents/MacOS/Tailscale"];function tailscaleInfo(){for(let bin of TAILSCALE_BINARIES){let ip=spawnSync(bin,["ip","-4"],{encoding:"utf8",timeout:5000}),ipv4=ip.status===0?ip.stdout.split(`
48
- `)[0]?.trim()??"":"";if(!ipv4)continue;let status=spawnSync(bin,["status","--json"],{encoding:"utf8",timeout:5000}),dnsName=null;try{let self=JSON.parse(status.stdout).Self;if(self&&typeof self==="object"&&"DNSName"in self&&typeof self.DNSName==="string")dnsName=self.DNSName.replace(/\.$/,"")||null}catch{}return{ipv4,dnsName}}return{ipv4:null,dnsName:null}}function readConfig(path){if(!existsSync3(path))return{};let raw=JSON.parse(readFileSync2(path,"utf8"));if(!raw||typeof raw!=="object")throw Error(`${path} must hold a JSON object`);let fields=new Map(Object.entries(raw)),pick=(k)=>{let v=fields.get(k);return typeof v==="string"&&v.trim()?v.trim():void 0};return{cfTeam:pick("cfTeam"),cfAud:pick("cfAud"),publicHost:pick("publicHost")}}async function runBridge(argv,opts){let home=resolveProductHomeDir(),bridgeDir=join4(home,".rove","bridge"),args;try{args=parseBridgeArgs(argv,{config:readConfig(join4(bridgeDir,"config.json")),tailscale:tailscaleInfo})}catch(err){console.error(`${opts.name}: ${err instanceof Error?err.message:String(err)}`),process.exit(2)}Reflect.deleteProperty(process.env,"ROVE_TASK_ID"),Reflect.deleteProperty(process.env,"ROVE_TAB_ID"),ensurePluginEnginesLoaded();let token=loadOrCreateToken(join4(bridgeDir,"token"),args.rotateToken),daemonSocket=await ensureDaemonReachable(void 0,"autospawn");await ensurePtyHostReachable();let orchestrator=new RemoteOrchestrator(new RoveDaemonClient(daemonSocket),{role:"gui",graphicsOut:()=>{}});await orchestrator.init();let hub=new MachineHub(orchestrator);hub.attach();let machines=hub.machinesSignal();machineStore.set(machines.get()),machines.subscribe(()=>machineStore.set(machines.get()));let verbClient=new RoveDaemonClient(daemonSocket),api=createBridgeApi(verbClient,{machineOf:(taskId2)=>{let alias=hub.tasksSignal().get().find((row)=>String(row.id)===taskId2)?.origin?.machineId;return alias&&alias!=="local"?alias:void 0},forward:(alias,verb,params)=>forwardVerb(requireMachine(alias),verb,params)}),ops=createRoveOps(verbClient,{changes:()=>orchestrator.worktreeChangesSignal()(),tokens:()=>orchestrator.rowTokensSignal()(),gates:()=>orchestrator.gatesSignal()(),remote:()=>hub.tasksSignal().get().filter((task)=>task.origin&&task.origin.machineId!=="local").map((task)=>({...task,kind:task.kind??"task",pinned:task.pinned??!1}))},api),feed=new TaskFeed(()=>ops.tasks());for(let signal of[orchestrator.tasksSignal(),orchestrator.engineStateSignal(),orchestrator.attentionInboxSignal(),orchestrator.worktreeChangesSignal(),orchestrator.rowTokensSignal(),orchestrator.gatesSignal(),hub.tasksSignal()])signal.subscribe(()=>feed.poke());let usage=orchestrator.usageSnapshotSignal();usageStore.set(usage()),usage.subscribe(()=>usageStore.set(usage()));let context=orchestrator.contextUsageSignal();usageStore.setContext(context()),context.subscribe(()=>usageStore.setContext(context())),setInterval(()=>feed.poke(),TICK_MS);let notices=new NoticeFeed,noticeStore=orchestrator.noticeStore();noticeStore.subscribe(()=>notices.push(noticeStore.get()));let prompts=new PromptFeed,promptStore=orchestrator.uiPromptStore();promptStore.subscribe(()=>prompts.push(promptStore.get()));let modPanes=orchestrator.modPanesSignal(),mods={current:()=>modPanes.get(),subscribe:(listener)=>modPanes.subscribe(()=>listener(modPanes.get()))},statePath=defaultUiPrefsStatePath(),tabs=new TabFeed(()=>{try{let raw=JSON.parse(readFileSync2(statePath,"utf8"));return raw&&typeof raw==="object"?raw:{}}catch{return{}}});startFileWatchTrigger({filePath:statePath,debounceMs:TABS_DEBOUNCE_MS,onTrigger:()=>tabs.check(),onError:()=>{}});let ptySocket=defaultPtyHostSocketPath(),deps={token,ops,feed,notices,prompts,mods,tabs,openPty:()=>new RoveDaemonClient(ptySocket),openMachinePty:async(alias)=>{let entry=requireMachine(alias);if(!await machineDaemonSocket(entry))throw new BridgeError("MACHINE_UNREACHABLE",`machine "${alias}" did not answer`);return new RoveDaemonClient(localPtySocketPath(alias))},roveVersion:CURRENT_VERSION,api,invoke:(verb,params,machine)=>invokeRouted({verb,params,machine,isLocalTask:(id)=>verbClient.request("task.get",{taskId:id}).then(()=>!0,()=>!1),runHere:()=>executeVerb(verb,{params},{client:verbClient,caller:REMOTE_CALLER})}),...args.cloudflare?{access:createAccessVerifier(args.cloudflare)}:{},...opts.web?{web:opts.web}:{}},servers=args.hosts.map((hostname)=>{try{return startBridgeServer(deps,{hostname,port:args.port})}catch(err){if(!(err&&typeof err==="object"&&("code"in err)&&err.code==="EADDRINUSE"))throw err;console.error(`${opts.name}: ${hostname}:${args.port} is already in use (another rove-bridge?). Stop it or pass --port <n>.`),process.exit(2)}});console.log(`${opts.name} ${CURRENT_VERSION} \u2014 Rove home ${home}`);for(let server of servers){let host=server.hostname??"127.0.0.1",port=server.port??args.port;if(console.log(`listening on ${host}:${port}${args.preset==="none"?"":` (preset ${args.preset})`}`),host==="0.0.0.0"||host==="::")console.log(" WARNING: all interfaces, plain ws:// \u2014 prefer --preset tailscale or --preset cf");let publicHost=args.publicHost??host;if(opts.web)console.log(`
49
- open: ${webUrl({host:publicHost,port,token,tls:args.tls})}`);let url=pairingUrl({host:publicHost,port,token,tls:args.tls,preset:args.preset});if(console.log(`${opts.web?"":`
50
- `}pair: ${url}`),args.qr&&!opts.web)console.log(renderUnicodeCompact(url))}if(args.cloudflare)console.log(`
51
- Cloudflare Access: team ${args.cloudflare.teamDomain}, every connection needs a valid JWT for this AUD.`),console.log(`Point the tunnel at http://127.0.0.1:${args.port} (e.g. ingress service for ${args.publicHost}).`);console.log(`
52
- The ${opts.web?"links are passwords":"pairing URL is a password"}. \`--rotate-token\` revokes every paired phone${opts.web?" and browser":""}.`)}export{runBridge};
@@ -1,2 +0,0 @@
1
- // @bun
2
- import{MachineHub,classifyHandshakeFailure}from"./chunk-s1rg1zae.js";import"./chunk-dd0mtzje.js";import"./chunk-atn06ncf.js";import"./chunk-55ajzrj6.js";import"./chunk-t7j8g9f6.js";import"./chunk-dmzg9zdc.js";import"./chunk-6xppe9bj.js";import"./chunk-1vxykwb9.js";import"./chunk-7nr405tr.js";import"./chunk-1378mh2w.js";import"./chunk-b7xjhgts.js";import"./chunk-kqf371ff.js";import"./chunk-1vwngrmf.js";import"./chunk-yktnbb14.js";import"./chunk-xy8be792.js";import"./chunk-7kysxgbq.js";import"./chunk-1xrbb7gf.js";import"./chunk-40dgv828.js";import"./chunk-g4281a0g.js";import"./chunk-zh0c8yzw.js";import"./chunk-wqckm0k6.js";import"./chunk-prqeddy7.js";import"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-cmp64q4j.js";import"./chunk-s0jbtyd9.js";import"./chunk-278sc87n.js";import"./chunk-gskbzgc3.js";import"./chunk-pn5ygqn6.js";import"./chunk-n0rtj4z7.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";export{classifyHandshakeFailure,MachineHub};
@@ -1,3 +0,0 @@
1
- // @bun
2
- import{BUNDLED_THEME_JSONS,parseRgbLiteral}from"./chunk-z3zaa155.js";import{kvStatePath}from"./chunk-prqeddy7.js";import{DEFAULT_LOCALE,isLocaleId}from"./chunk-sma961wh.js";import{RGBA as RGBA3}from"@opentui/core";import{RGBA}from"@opentui/core";var BREATH_TICKS=30,SPEED_MIN=0.2,SPEED_SWING=0.9333333333333333,INK_FLOOR=0.3;function breathLevel(tick){return 0.5-0.5*Math.cos(2*Math.PI*tick/BREATH_TICKS)}function breathPhase(tick){let wave=tick/2-BREATH_TICKS/(4*Math.PI)*Math.sin(2*Math.PI*tick/BREATH_TICKS);return SPEED_MIN*tick+SPEED_SWING*wave}function breathGlyph(frames,tick){let index=Math.floor(breathPhase(tick)+0.000000001)%frames.length;return frames[index]??frames[0]??""}function mixInk(accent,muted,share){if(accent.intent!=="rgb"||muted.intent!=="rgb")return accent;let[ar,ag,ab,aa]=accent.toInts(),[mr,mg,mb]=muted.toInts(),mix=(a,m)=>Math.round(m+(a-m)*share);return RGBA.fromInts(mix(ar,mr),mix(ag,mg),mix(ab,mb),aa)}function breathColor(accent,muted,tick){return mixInk(accent,muted,INK_FLOOR+(1-INK_FLOOR)*breathLevel(tick))}var toLinear=(v)=>v<=0.04045?v/12.92:((v+0.055)/1.055)**2.4,toGamma=(v)=>v<=0.0031308?12.92*v:1.055*v**0.4166666666666667-0.055;function toOklch(color){let[r,g,b]=color.toInts().map((v)=>toLinear(v/255)),l=Math.cbrt(0.4122214708*r+0.5363325363*g+0.0514459929*b),m=Math.cbrt(0.2119034982*r+0.6806995451*g+0.1073969566*b),s=Math.cbrt(0.0883024619*r+0.2817188376*g+0.6299787005*b),L=0.2104542553*l+0.793617785*m-0.0040720468*s,A=1.9779984951*l-2.428592205*m+0.4505937099*s,B=0.0259040371*l+0.7827717662*m-0.808675766*s;return{l:L,c:Math.hypot(A,B),h:Math.atan2(B,A)}}function linearRgb({l:L,c,h}){let A=c*Math.cos(h),B=c*Math.sin(h),l=(L+0.3963377774*A+0.2158037573*B)**3,m=(L-0.1055613458*A-0.0638541728*B)**3,s=(L-0.0894841775*A-1.291485548*B)**3;return[4.0767416621*l-3.3077115913*m+0.2309699292*s,-1.2684380046*l+2.6097574011*m-0.3413193965*s,-0.0041960863*l-0.7034186147*m+1.707614701*s]}function fromOklch(color,alpha){let ch=(v)=>Math.round(Math.min(1,Math.max(0,toGamma(v)))*255),[r,g,b]=linearRgb(color);return RGBA.fromInts(ch(r),ch(g),ch(b),alpha)}function turnHue(accent,deg,lightness=1,chroma=1){if(accent.intent!=="rgb")return accent;let base=toOklch(accent),turned={l:base.l*lightness,c:base.c*chroma,h:base.h+deg*Math.PI/180};return fromOklch(turned,accent.toInts()[3])}var inGamut=(color)=>linearRgb(color).every((v)=>v>=-0.0001&&v<=1.0001);function inkAtHue(deg,lightness,chroma,alpha=255){let h=deg*Math.PI/180,c=chroma;while(c>0&&!inGamut({l:lightness,c,h}))c-=0.005;return fromOklch({l:lightness,c:Math.max(0,c),h},alpha)}function oklchCusp(deg){let key=Math.round(deg)%360,hit=cuspCache.get(key);if(hit)return hit;let h=key*Math.PI/180,best={l:1,c:0};for(let l=0.2;l<=1;l+=0.01){let lo=0,hi=0.4;for(let i=0;i<16;i++){let mid=(lo+hi)/2;if(inGamut({l,c:mid,h}))lo=mid;else hi=mid}if(lo>best.c)best={l,c:lo}}return cuspCache.set(key,best),best}var cuspCache=new Map;import{RGBA as RGBA2}from"@opentui/core";var HOST_TEXT_MIN_CONTRAST=4.5;function srgbChannelToLinear(c){let v=c/255;return v<=0.04045?v/12.92:((v+0.055)/1.055)**2.4}function relativeLuminance([r,g,b]){return 0.2126*srgbChannelToLinear(r)+0.7152*srgbChannelToLinear(g)+0.0722*srgbChannelToLinear(b)}function contrastRatioTriplet(fg,bg){let l1=relativeLuminance(fg),l2=relativeLuminance(bg),lighter=Math.max(l1,l2),darker=Math.min(l1,l2);return(lighter+0.05)/(darker+0.05)}function rgbToHsl([r,g,b]){let rn=r/255,gn=g/255,bn=b/255,max=Math.max(rn,gn,bn),min=Math.min(rn,gn,bn),l=(max+min)/2;if(max===min)return[0,0,l];let d=max-min,s=l>0.5?d/(2-max-min):d/(max+min),h;if(max===rn)h=((gn-bn)/d+(gn<bn?6:0))*60;else if(max===gn)h=((bn-rn)/d+2)*60;else h=((rn-gn)/d+4)*60;return[h,s,l]}function hslToRgb(h,s,l){if(s===0){let v=Math.round(l*255);return[v,v,v]}let q=l<0.5?l*(1+s):l+s-l*s,p=2*l-q,channel=(t)=>{let tn=t;if(tn<0)tn+=1;if(tn>1)tn-=1;let c=tn<0.16666666666666666?p+(q-p)*6*tn:tn<0.5?q:tn<0.6666666666666666?p+(q-p)*(0.6666666666666666-tn)*6:p;return Math.round(c*255)};return[channel(h/360+0.3333333333333333),channel(h/360),channel(h/360-0.3333333333333333)]}var MID_HOST_LUMINANCE=0.5;function ensureContrast(fg,bg,minRatio=HOST_TEXT_MIN_CONTRAST){let fgInts=fg.toInts(),bgInts=bg.toInts(),fgTriplet=[fgInts[0],fgInts[1],fgInts[2]],bgTriplet=[bgInts[0],bgInts[1],bgInts[2]];if(contrastRatioTriplet(fgTriplet,bgTriplet)>=minRatio)return fg;let[h,s,l]=rgbToHsl(fgTriplet),lighten=relativeLuminance(bgTriplet)<=MID_HOST_LUMINANCE,at=(lightness)=>contrastRatioTriplet(hslToRgb(h,s,lightness),bgTriplet),lo=lighten?l:0,hi=lighten?1:l;for(let i=0;i<32;i++){let mid=(lo+hi)/2;if(at(mid)>=minRatio)if(lighten)hi=mid;else lo=mid;else if(lighten)lo=mid;else hi=mid}let[r,g,b]=hslToRgb(h,s,lighten?hi:lo);return RGBA2.fromInts(r,g,b,fgInts[3])}var BUNDLED_THEMES=BUNDLED_THEME_JSONS,DEFAULT_THEME="claude";function hasBundledTheme(name){return Boolean(BUNDLED_THEMES[name])}var THEME_MODE_PREFERENCES=["dark","light","auto"],DEFAULT_THEME_MODE="dark";function resolveThemeMode(preference,detected){return preference==="auto"?detected??"dark":preference}var FOCUS_ACCENT_SLOTS=["primary","success","info"];function resolveTheme(theme,mode="dark"){let defs=theme.defs??{};function resolve(c,chain=[]){if(typeof c==="string"){if(c==="transparent"||c==="none")return RGBA3.fromInts(0,0,0,0);if(c.startsWith("#"))return RGBA3.fromHex(c);let rgb=parseRgbLiteral(c);if(rgb)return RGBA3.fromInts(rgb.r,rgb.g,rgb.b,rgb.a);if(chain.includes(c))return RGBA3.fromInts(0,0,0);let next=defs[c]??theme.theme[c];if(next===void 0)return RGBA3.fromInts(0,0,0);return resolve(next,[...chain,c])}return resolve(c[mode],chain)}let out={};for(let[k,v]of Object.entries(theme.theme))out[k]=resolve(v);let text=out.text??RGBA3.fromHex("#ffffff"),background=out.background??RGBA3.fromHex("#000000");return{...{primary:out.primary??text,secondary:out.secondary??text,accent:out.accent??out.primary??text,error:out.error??text,warning:out.warning??text,warningOnHost:out.warning??text,success:out.success??text,info:out.info??text,text,textMuted:out.textMuted??text,background,backgroundPanel:out.backgroundPanel??background,backgroundElement:out.backgroundElement??background,backgroundMenu:out.backgroundMenu??out.backgroundElement??background,backgroundDialog:out.backgroundDialog??out.backgroundPanel??background,border:out.border??text,borderActive:out.borderActive??out.border??text,borderSubtle:out.borderSubtle??out.border??text,diffAdded:out.diffAdded??out.success??text,diffRemoved:out.diffRemoved??out.error??text,diffContext:out.diffContext??out.textMuted??text,diffHunkHeader:out.diffHunkHeader??out.textMuted??text,diffAddedBg:out.diffAddedBg??background,diffRemovedBg:out.diffRemovedBg??background,selectedListItemText:out.selectedListItemText??background},...out}}function applyDisplayOverlay(base,focusAccent,transparentBackground,hostBackground){let v={...base,focusAccent:base[focusAccent]??base.primary,warningOnHost:base.warning};if(!transparentBackground)return v;let[backgroundR,backgroundG,backgroundB]=base.background.toInts(),[panelR,panelG,panelB]=base.backgroundPanel.toInts(),transparent={...v,background:RGBA3.fromInts(backgroundR,backgroundG,backgroundB,0),backgroundPanel:RGBA3.fromInts(panelR,panelG,panelB,0)};if(!hostBackground)return transparent;return{...transparent,text:ensureContrast(transparent.text,hostBackground),textMuted:ensureContrast(transparent.textMuted,hostBackground),warningOnHost:ensureContrast(transparent.warning,hostBackground)}}function diffInks(theme,colorblind){let added=(ink)=>colorblind?turnHue(ink,60,1,0.71):ink;return{added:added(theme.diffAdded),removed:theme.diffRemoved,addedBg:added(theme.diffAddedBg),removedBg:theme.diffRemovedBg}}import{readFileSync}from"fs";var LOCALE_KEY="locale";function defaultTransparentBackground(platform=process.platform){return platform!=="win32"}function readPersistedUiPrefs(fallbackTheme,isKnownTheme=hasBundledTheme){try{let parsed=JSON.parse(readFileSync(kvStatePath(),"utf8")),theme=typeof parsed.activeTheme==="string"&&isKnownTheme(parsed.activeTheme)?parsed.activeTheme:fallbackTheme,themeMode=THEME_MODE_PREFERENCES.includes(parsed.themeMode)?parsed.themeMode:DEFAULT_THEME_MODE,transparent=typeof parsed.transparentBackground==="boolean"?parsed.transparentBackground:defaultTransparentBackground(),focusAccent=typeof parsed.focusAccent==="string"&&FOCUS_ACCENT_SLOTS.includes(parsed.focusAccent)?parsed.focusAccent:null,locale=isLocaleId(parsed[LOCALE_KEY])?parsed[LOCALE_KEY]:DEFAULT_LOCALE;return{theme,themeMode,transparent,focusAccent,locale}}catch{return{theme:fallbackTheme,themeMode:DEFAULT_THEME_MODE,transparent:defaultTransparentBackground(),focusAccent:null,locale:DEFAULT_LOCALE}}}
3
- export{breathGlyph,mixInk,breathColor,toOklch,turnHue,inkAtHue,oklchCusp,relativeLuminance,ensureContrast,BUNDLED_THEMES,DEFAULT_THEME,THEME_MODE_PREFERENCES,DEFAULT_THEME_MODE,resolveThemeMode,FOCUS_ACCENT_SLOTS,resolveTheme,applyDisplayOverlay,diffInks,LOCALE_KEY,defaultTransparentBackground,readPersistedUiPrefs};
@@ -1,2 +0,0 @@
1
- // @bun
2
- import{LOCAL_MACHINE,forwardVerb,invokeRouted,machineDaemonSocket,machineTasks,requireMachine,routeTarget}from"./chunk-2yjpqb47.js";import"./chunk-1vxykwb9.js";import"./chunk-7nr405tr.js";import"./chunk-1378mh2w.js";import"./chunk-b7xjhgts.js";import"./chunk-x7r75e3p.js";import"./chunk-2brn8e1y.js";import"./chunk-7kysxgbq.js";import"./chunk-g4281a0g.js";import"./chunk-zh0c8yzw.js";import"./chunk-wqckm0k6.js";import"./chunk-prqeddy7.js";import"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-s0jbtyd9.js";import"./chunk-278sc87n.js";import"./chunk-gskbzgc3.js";import"./chunk-pn5ygqn6.js";import"./chunk-n0rtj4z7.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";export{routeTarget,requireMachine,machineTasks,machineDaemonSocket,invokeRouted,forwardVerb,LOCAL_MACHINE};
@@ -1,2 +0,0 @@
1
- // @bun
2
- import{machineTasks}from"./chunk-2yjpqb47.js";import"./chunk-1vxykwb9.js";import"./chunk-7nr405tr.js";import{dedupeMachines,listMachines}from"./chunk-1378mh2w.js";import"./chunk-b7xjhgts.js";import"./chunk-x7r75e3p.js";import"./chunk-2brn8e1y.js";import"./chunk-7kysxgbq.js";import"./chunk-g4281a0g.js";import"./chunk-zh0c8yzw.js";import"./chunk-wqckm0k6.js";import"./chunk-prqeddy7.js";import"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-s0jbtyd9.js";import"./chunk-278sc87n.js";import"./chunk-gskbzgc3.js";import"./chunk-pn5ygqn6.js";import"./chunk-n0rtj4z7.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import"./chunk-91sykzqq.js";async function mergeTaskList(local){let machines=dedupeMachines(listMachines());if(machines.length===0)return local;let tasks=(local.tasks??[]).map((task)=>({...task,origin:{machineId:"local",hostLabel:"local"}}));for(let entry of machines){let remote=await machineTasks(entry);if(!remote)continue;let hostLabel=entry.alias;for(let task of remote)tasks.push({...task,origin:{machineId:entry.alias,hostLabel}})}return{tasks}}export{mergeTaskList};
@@ -1,2 +0,0 @@
1
- // @bun
2
- import{API_VERBS,VERBS,VERB_ALIASES,VERB_GROUPS,findVerb,unknownVerbError}from"./chunk-4ab8y1yp.js";import"./chunk-tpt3fn1q.js";import"./chunk-t60qcvcj.js";import"./chunk-6p9yxt3q.js";import"./chunk-rc2nwsdd.js";import"./chunk-cbc1h345.js";import"./chunk-jjv7xem7.js";import"./chunk-17e2tgyg.js";import"./chunk-68113ts1.js";import"./chunk-4hestsjp.js";import"./chunk-41x1hrqe.js";import"./chunk-5mhvjytn.js";import"./chunk-stcvcsfm.js";import"./chunk-2bgmby1d.js";import"./chunk-edsm15ht.js";import"./chunk-j11hzmwy.js";import"./chunk-atn06ncf.js";import"./chunk-cq5qv93a.js";import"./chunk-qkehg2c1.js";import"./chunk-csdne12x.js";import"./chunk-3q1rv3rm.js";import"./chunk-sabpcket.js";import"./chunk-ampdybqg.js";import"./chunk-sak1tphp.js";import"./chunk-4250pw6r.js";import"./chunk-82zpp3yx.js";import"./chunk-rxqdw6s5.js";import"./chunk-jetsx9zr.js";import"./chunk-f5n0tgky.js";import"./chunk-z3zaa155.js";import"./chunk-yewmtant.js";import"./chunk-hpay0ax8.js";import"./chunk-catrr0z3.js";import"./chunk-hdnqq66w.js";import"./chunk-b47k0cnw.js";import"./chunk-2tfnscxk.js";import"./chunk-9ecnsh42.js";import"./chunk-w7zv47mp.js";import"./chunk-etfh8f4z.js";import"./chunk-t7j8g9f6.js";import"./chunk-dmzg9zdc.js";import"./chunk-kg9kmwt9.js";import"./chunk-6xppe9bj.js";import"./chunk-b7xjhgts.js";import"./chunk-x7r75e3p.js";import"./chunk-zsfyj36g.js";import"./chunk-kqf371ff.js";import"./chunk-1vwngrmf.js";import"./chunk-yktnbb14.js";import"./chunk-ag3txfg2.js";import"./chunk-2brn8e1y.js";import"./chunk-t25xke74.js";import"./chunk-7zyyvc3d.js";import"./chunk-r6k3ve2g.js";import"./chunk-xy8be792.js";import"./chunk-7kysxgbq.js";import"./chunk-h2m7em1e.js";import"./chunk-7ghwdjdc.js";import"./chunk-tdhk4dz8.js";import"./chunk-1xrbb7gf.js";import"./chunk-22d0yc7m.js";import"./chunk-40dgv828.js";import"./chunk-3342qzve.js";import"./chunk-g4281a0g.js";import"./chunk-zh0c8yzw.js";import"./chunk-wqckm0k6.js";import"./chunk-prqeddy7.js";import"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-tq6pq1ws.js";import"./chunk-sma961wh.js";import"./chunk-cmp64q4j.js";import"./chunk-s0jbtyd9.js";import"./chunk-278sc87n.js";import"./chunk-gskbzgc3.js";import"./chunk-pn5ygqn6.js";import"./chunk-n0rtj4z7.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import"./chunk-pxk2c2vj.js";import"./chunk-91sykzqq.js";export{unknownVerbError,findVerb,VERB_GROUPS,VERB_ALIASES,VERBS,API_VERBS};
@@ -1,3 +0,0 @@
1
- // @bun
2
- import{RemoteOrchestrator}from"./chunk-dd0mtzje.js";import{startTunnel}from"./chunk-1vxykwb9.js";import{discoverMachine}from"./chunk-7nr405tr.js";import{dedupeMachines,duplicateAliasOf,listMachines,readMachines,setMachineIdentity,updateMachine}from"./chunk-1378mh2w.js";import{loadStateFile}from"./chunk-g4281a0g.js";import{createStateCell}from"./chunk-cmp64q4j.js";import{RoveDaemonClient}from"./chunk-s0jbtyd9.js";import{logClient,logClientError}from"./chunk-gskbzgc3.js";class MachineHub{local;slots=new Map;machinesAcc=createStateCell([],"machines.status");mergedAcc=createStateCell([],"machines.tasks");localUnsubscribe=null;disposed=!1;constructor(local){this.local=local}machinesSignal(){return this.machinesAcc}tasksSignal(){if(this.slots.size===0)return this.local.tasksSignal();return this.mergedAcc}remoteTaskIds(){let ids=new Set;for(let slot of this.slots.values())for(let task of slot.tasks)ids.add(String(task.id));return ids}attach(){let entries=dedupeMachines(listMachines());if(entries.length===0)return;for(let entry of entries)this.slots.set(entry.alias,{entry,tasks:[]});this.publishStatuses(),this.localUnsubscribe=this.local.tasksSignal().subscribe(()=>this.republishTasks()),this.republishTasks();for(let entry of entries)this.connect(entry)}async connect(entry){let slot=this.slots.get(entry.alias);if(!slot||this.disposed)return;let found=await discoverMachine(entry.alias,entry);if(this.disposed)return;let sockets=found.ok?{daemon:found.status.socketPath,pty:found.status.ptySocketPath}:entry.sockets;if(!sockets){this.setStatus(entry.alias,{state:"offline",error:found.ok?void 0:found.message});return}if(found.ok)updateMachine(entry.alias,{sockets,...found.roveBin?{roveBin:found.roveBin}:{}});let tunnel=startTunnel({alias:entry.alias,config:entry,remoteDaemonSocket:sockets.daemon,remotePtySocket:sockets.pty});if(slot.tunnel=tunnel,tunnel.state()==="unsupported"){this.setStatus(entry.alias,{state:"unsupported",error:"SSH unix-socket forwarding is not available on Windows"});return}if(tunnel.onState((state)=>{if(state==="online")this.openOrchestrator(entry,tunnel);else if(state==="offline")this.setStatus(entry.alias,{state:"offline"}),this.republishTasks()}),tunnel.state()==="online")await this.openOrchestrator(entry,tunnel)}async openOrchestrator(entry,tunnel){let slot=this.slots.get(entry.alias);if(!slot||this.disposed||slot.orchestrator)return;let client=new RoveDaemonClient(tunnel.daemonSocketPath),orchestrator=new RemoteOrchestrator(client,{role:"gui",expectForeignHome:!0,onPeerIdentity:(peer)=>this.onPeerIdentity(entry.alias,peer)});slot.orchestrator=orchestrator;try{await orchestrator.init()}catch(err){let message=err instanceof Error?err.message:String(err);this.setStatus(entry.alias,{state:classifyHandshakeFailure(message),error:message}),logClientError("machines",err);return}let stopTasks=orchestrator.tasksSignal().subscribe(()=>{slot.tasks=orchestrator.tasksSignal().get(),this.republishTasks()}),stopConnection=orchestrator.connectionStateSignal().subscribe(()=>{let connected=orchestrator.connectionStateSignal().get()==="online";if(this.slots.get(entry.alias)!==slot)return;this.setStatus(entry.alias,{state:connected?"online":"offline"}),this.republishTasks()});slot.unsubscribe=()=>{stopTasks(),stopConnection()},slot.tasks=orchestrator.tasksSignal().get(),this.setStatus(entry.alias,{state:"online",version:orchestrator.daemonVersionSignal().get()??void 0}),this.republishTasks(),logClient("machines",`${entry.alias} online (${slot.tasks.length} tasks)`)}onPeerIdentity(alias,peer){if(!peer.hostname&&!peer.homeDir)return;let identity={hostname:peer.hostname,homeDir:peer.homeDir,daemonPid:peer.daemonPid};setMachineIdentity(alias,identity);let duplicate=duplicateAliasOf(readMachines(loadStateFile()),alias,identity);this.setStatus(alias,{hostname:peer.hostname||void 0,version:peer.roveVersion||void 0,...duplicate?{duplicateOf:duplicate}:{}})}setStatus(alias,patch){let next=this.machinesAcc.get().map((row)=>row.alias===alias?{...row,...patch}:row);this.machinesAcc.set(next)}publishStatuses(){this.machinesAcc.set([...this.slots.values()].map((slot)=>({alias:slot.entry.alias,hostLabel:slot.entry.alias,state:"connecting"})))}republishTasks(){let merged=this.local.tasksSignal().get().map((task)=>stampOrigin(task,"local","local"));for(let slot of this.slots.values()){let status=this.machinesAcc.get().find((row)=>row.alias===slot.entry.alias);if(status?.duplicateOf)continue;let hostLabel=status?.hostLabel||slot.entry.alias,stale=status?.state!=="online";for(let task of slot.tasks)merged.push(stampOrigin(task,slot.entry.alias,hostLabel,stale))}this.mergedAcc.set(merged)}dispose(){this.disposed=!0,this.localUnsubscribe?.();for(let slot of this.slots.values())slot.unsubscribe?.(),slot.orchestrator?.dispose(),slot.tunnel?.stop();this.slots.clear()}}function classifyHandshakeFailure(message){return/protocol v\d/i.test(message)?"mismatch":"offline"}function stampOrigin(task,machineId,hostLabel,stale=!1){return{...task,origin:{machineId,hostLabel,...stale?{stale:!0}:{}}}}
3
- export{MachineHub,classifyHandshakeFailure};
@@ -1,11 +0,0 @@
1
- // @bun
2
- import{deliverPromptToLiveEngineAdapter,deliverPromptToLiveEngineDetailedAdapter,deliverPromptToLiveEngineTabDetailedAdapter,deriveTitleFromSession,ensureTaskSessionAdapter,startTaskSessionWithPromptAdapter,tearDownTaskSessionAdapter}from"./chunk-eyzjma6q.js";import{Orchestrator}from"./chunk-17fkjpj8.js";import{GH_PR_VIEW_FIELDS,classifyGhFailure,mapGhPrView,nextPrPoll,samePrStatus}from"./chunk-7jqd1vht.js";import{GitWorktreeManager,parseDirtyPaths}from"./chunk-eq0c4ecj.js";import"./chunk-nfq1q08c.js";import{resolvePluginBinPath}from"./chunk-b0ssvpht.js";import{startChatChannels}from"./chunk-rx2604ry.js";import{auditDeletionBranchKept,auditDeletionResidue,auditDeletionSalvaged,auditWorktreeResidue,auditWorktreeSalvaged,parseAheadBehind,startDaemonServer}from"./chunk-ya0h6hcd.js";import"./chunk-r3s2t9af.js";import"./chunk-a4hsqj1b.js";import"./chunk-dqprpnd6.js";import"./chunk-k67vbszb.js";import{preferredReleaseChannel}from"./chunk-ebea569y.js";import{TaskIndexStore}from"./chunk-1ys1maw7.js";import{PLACEHOLDER_TASK_TITLE}from"./chunk-cbc1h345.js";import"./chunk-jjv7xem7.js";import{affectsActivityState,isEngineActivityKind}from"./chunk-17e2tgyg.js";import"./chunk-4hestsjp.js";import"./chunk-41x1hrqe.js";import"./chunk-5mhvjytn.js";import"./chunk-stcvcsfm.js";import"./chunk-2bgmby1d.js";import"./chunk-edsm15ht.js";import"./chunk-atn06ncf.js";import"./chunk-qkehg2c1.js";import{autoStatusEnabled}from"./chunk-sabpcket.js";import{execHostForWorktreePath}from"./chunk-ampdybqg.js";import"./chunk-sak1tphp.js";import"./chunk-4250pw6r.js";import{GENERIC_PROTOCOL,engineDisplayName,engineProtocolKey,getEngineProtocol,protocolEntry,resolveCommandProtocol,roveApiInvocation,sessionProtocol}from"./chunk-82zpp3yx.js";import{installDaemonCrashHandlers,logDaemonInfo}from"./chunk-rxqdw6s5.js";import"./chunk-jetsx9zr.js";import"./chunk-f5n0tgky.js";import"./chunk-z3zaa155.js";import"./chunk-yewmtant.js";import"./chunk-hpay0ax8.js";import"./chunk-catrr0z3.js";import{foregroundEngineIn,parsePsSnapshot,psSnapshot}from"./chunk-hdnqq66w.js";import{createEngineTurnDetector,engineEntry,engineTitleTurnHint,vendorsWithQuotaProbe}from"./chunk-b47k0cnw.js";import{spawnCapture}from"./chunk-2tfnscxk.js";import{availableEngineIds}from"./chunk-9ecnsh42.js";import"./chunk-w7zv47mp.js";import"./chunk-sr4c9fve.js";import"./chunk-etfh8f4z.js";import"./chunk-rwx1y497.js";import{parsePorcelain}from"./chunk-55ajzrj6.js";import"./chunk-t7j8g9f6.js";import"./chunk-dmzg9zdc.js";import{DEFAULT_BASE_REF_CANDIDATES,readHeadSha,readRefSha,resolveGitDirs}from"./chunk-kg9kmwt9.js";import{readOnlyGitProcessEnv}from"./chunk-6xppe9bj.js";import"./chunk-b7xjhgts.js";import{argvHasFlag,switchFlag}from"./chunk-zsfyj36g.js";import{connectOrStartDaemon,daemonSpawnReason}from"./chunk-kqf371ff.js";import{stopDaemonProcess}from"./chunk-1vwngrmf.js";import"./chunk-yktnbb14.js";import"./chunk-2brn8e1y.js";import"./chunk-t25xke74.js";import"./chunk-7zyyvc3d.js";import"./chunk-r6k3ve2g.js";import{DEFAULT_TASK_VENDOR,isTaskStatus}from"./chunk-xy8be792.js";import"./chunk-7kysxgbq.js";import{BUILTIN_VENDORS,isBuiltinVendor}from"./chunk-h2m7em1e.js";import"./chunk-7ghwdjdc.js";import{migrateRoveDaemonStateLayout}from"./chunk-tdhk4dz8.js";import{SUBCOMMAND_VERBS}from"./chunk-1qge84t7.js";import{CURRENT_VERSION,checkLatestVersion}from"./chunk-1xrbb7gf.js";import{backfillSavedReposFromProjects,getCustomEngineIds,getPersistedString,getRemoteRepoConfig,getSavedRepos,setPersistedString}from"./chunk-22d0yc7m.js";import"./chunk-40dgv828.js";import"./chunk-3342qzve.js";import"./chunk-g4281a0g.js";import{releaseSync}from"./chunk-zh0c8yzw.js";import{isProcessAlive,readPidFile}from"./chunk-wqckm0k6.js";import"./chunk-prqeddy7.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-sma961wh.js";import"./chunk-cmp64q4j.js";import{RoveDaemonClient}from"./chunk-s0jbtyd9.js";import"./chunk-278sc87n.js";import{rotateLogIfNeeded}from"./chunk-gskbzgc3.js";import"./chunk-pn5ygqn6.js";import{defaultDaemonLogPath,defaultDaemonPidPath,defaultDaemonSocketPath}from"./chunk-n0rtj4z7.js";import"./chunk-b39sdtt0.js";import{readRoveHomeDirEnv}from"./chunk-nc46gddg.js";import"./chunk-pxk2c2vj.js";import{__require}from"./chunk-91sykzqq.js";function sniffProtocolFromTitle(title){let trimmed=title?.trim();if(!trimmed)return null;let owners=new Map;for(let vendor of BUILTIN_VENDORS)for(let glyph of engineEntry(vendor).terminalTitle?.statusPrefixes??[])owners.set(glyph,[...owners.get(glyph)??[],vendor]);for(let glyph of[...owners.keys()].sort((a,b)=>b.length-a.length)){if(!trimmed.startsWith(glyph))continue;if(trimmed.slice(glyph.length).trim().length===0)continue;let vendors=owners.get(glyph)??[];if(vendors.length===1)return vendors[0];return null}return null}function protocolUpgradeFromLiveSession(task,evidence){let command=task.command?.trim();if(!command)return null;if(task.vendor&&isBuiltinVendor(task.vendor))return null;if(resolveCommandProtocol(command)!==GENERIC_PROTOCOL)return null;let walk=evidence.walkVendor,vendor=walk!==null&&isBuiltinVendor(walk)?walk:sniffProtocolFromTitle(evidence.title);return vendor?{command,vendor}:null}function protocolWriteBackFromLiveSession(task,evidence,customEngineIds){let id=task.vendor?.trim();if(!id)return null;if(getEngineProtocol(id)!==void 0)return null;if(!customEngineIds.includes(id))return null;let walk=evidence.walkVendor;return walk!==null&&isBuiltinVendor(walk)?{id,protocol:walk}:null}async function latestTranscriptMtime(vendor,worktree){if(!worktree)return 0;return protocolEntry(vendor).history.latestTranscriptMtimeForWorktree(worktree)}async function maybeAutoStart(orch,taskId,enabled=autoStatusEnabled){if(!enabled())return"skipped";let task=orch.getTask(taskId);if(!task)return"skipped";if((task.kind??"task")==="main")return"skipped";if(task.status!=="backlog")return"skipped";return await orch.setStatus(taskId,"in_progress"),"moved"}var SYNC_TIMEOUT_MS=60000,SYNC_CONFLICT="SYNC_CONFLICT",SYNC_DIRTY="SYNC_WORKTREE_DIRTY";async function git(cwd,args,signal,write=!1){return spawnCapture("git",args,{cwd,env:write?process.env:readOnlyGitProcessEnv(),signal})}function parseConflictedPaths(stdout){return stdout.split(`
3
- `).map((line)=>line.trim()).filter((line)=>line.length>0)}async function syncWorktreeWithBase(worktreePath,baseRef,signal){let status=await git(worktreePath,["status","--porcelain=v1"],signal);if(status.status!==0)throw Error("git status failed");let dirty=parseDirtyPaths(status.stdout);if(dirty.length>0)throw Error(`${SYNC_DIRTY}: ${dirty.join(", ")}`);let before=await git(worktreePath,["rev-parse","HEAD"],signal),merge=await git(worktreePath,["merge","--no-edit",baseRef],signal,!0);if(merge.status!==0){let conflicted=await git(worktreePath,["diff","--name-only","--diff-filter=U"],signal),paths=parseConflictedPaths(conflicted.stdout);if(paths.length>0)throw Error(`${SYNC_CONFLICT}: ${paths.join(", ")}`);let detail=merge.stderr?.trim();throw Error(detail?`git merge ${baseRef} failed \u2014 ${detail}`:`git merge ${baseRef} failed`)}let after=await git(worktreePath,["rev-parse","HEAD"],signal);return{baseRef,alreadyCurrent:before.status===0&&before.stdout.trim()===after.stdout.trim()}}import{readFileSync}from"fs";import{join}from"path";var BASE_REF_TTL_MS=300000,cache=new Map;async function git2(worktreePath,args,signal){return await spawnCapture("git",[...args],{cwd:worktreePath,env:readOnlyGitProcessEnv(),signal})}async function refExists(worktreePath,ref,signal){return(await git2(worktreePath,["rev-parse","--verify","--quiet",ref],signal)).status===0}async function sharesHistory(worktreePath,ref,signal){return(await git2(worktreePath,["merge-base",ref,"HEAD"],signal)).status===0}async function baseCheckoutBranchFromGit(worktreePath,signal){let out=await git2(worktreePath,["worktree","list","--porcelain"],signal);if(out.status!==0)return null;return(out.stdout.split(`
4
-
5
- `,1)[0]??"").split(`
6
- `).find((l)=>l.startsWith("branch "))?.slice(7).trim().replace(/^refs\/heads\//,"")||null}function readRefSymbolic(dirs,ref){for(let dir of[dirs.gitDir,dirs.commonDir])try{let target=readFileSync(join(dir,ref),"utf8").trim().match(/^ref:\s*(.+)$/)?.[1]?.trim();if(target)return target}catch{}return null}function ladderFromFiles(worktreePath){let dirs=resolveGitDirs(worktreePath);if(!dirs)return;let originHead=readRefSymbolic(dirs,"refs/remotes/origin/HEAD")?.replace(/^refs\/remotes\//,""),baseCheckout=readRefSymbolic({gitDir:dirs.commonDir,commonDir:dirs.commonDir},"HEAD")?.replace(/^refs\/heads\//,""),names=[],head=readHeadSha(dirs),parts=[`HEAD=${head}`];for(let guess of[...originHead?[originHead]:[],...DEFAULT_BASE_REF_CANDIDATES,...baseCheckout?[baseCheckout]:[]]){if(names.includes(guess))continue;let sha=readRefSha(dirs,guess);if(sha===null)continue;names.push(guess),parts.push(`${guess}=${sha}`)}return{names,fingerprint:head===null?null:parts.join(",")}}async function ladderFromGit(worktreePath,signal){let head=await git2(worktreePath,["symbolic-ref","--short","refs/remotes/origin/HEAD"],signal),originHead=head.status===0?head.stdout.trim():"",baseCheckout=await baseCheckoutBranchFromGit(worktreePath,signal),names=[],candidates=[...originHead?[originHead]:[],...DEFAULT_BASE_REF_CANDIDATES,...baseCheckout?[baseCheckout]:[]];for(let guess of candidates){if(names.includes(guess))continue;if(await refExists(worktreePath,guess,signal))names.push(guess)}return{names,fingerprint:null}}async function resolveBaseRefCached(worktreePath,recordedBaseRef,signal,now=Date.now()){if(recordedBaseRef){let dirs=resolveGitDirs(worktreePath);if(dirs&&readRefSha(dirs,recordedBaseRef)!==null)return recordedBaseRef;if(await refExists(worktreePath,recordedBaseRef,signal))return recordedBaseRef}let hit=cache.get(worktreePath);if(hit&&now-hit.at<BASE_REF_TTL_MS)return hit.ref;let ladder=ladderFromFiles(worktreePath)??await ladderFromGit(worktreePath,signal);if(hit&&ladder.fingerprint!==null&&hit.fingerprint===ladder.fingerprint)return cache.set(worktreePath,{...hit,at:now}),hit.ref;let ref=null;for(let candidate of ladder.names)if(await sharesHistory(worktreePath,candidate,signal)){ref=candidate;break}return cache.set(worktreePath,{ref,at:now,fingerprint:ladder.fingerprint}),ref}var cache2=new Map;function readShas(worktreePath,baseRef){let dirs=resolveGitDirs(worktreePath);if(!dirs)return null;let head=readHeadSha(dirs),base=readRefSha(dirs,baseRef);return head&&base?{head,base}:null}async function driftCached(worktreePath,baseRef,compute){let shas=readShas(worktreePath,baseRef),hit=shas?cache2.get(worktreePath):void 0;if(shas&&hit&&hit.head===shas.head&&hit.base===shas.base)return hit.value;let value=await compute();if(shas&&value!==null)cache2.set(worktreePath,{...shas,value});return value}import{statSync}from"fs";function judgeWorktree(signals,nowMs){if(signals.dirty)return{verdict:"active",reason:"dirty"};if(signals.prState==="open")return{verdict:"active",reason:"prOpen"};if(signals.prState==="merged")return{verdict:"merged",reason:"prMerged"};if(signals.aheadOfDefault===0)return{verdict:"merged",reason:"inMain"};if(signals.prState==="closed")return{verdict:"stale",reason:"prClosed"};if(signals.lastActivityMs>0&&nowMs-signals.lastActivityMs>1209600000)return{verdict:"stale",reason:"idle"};return{verdict:"fresh",reason:"fresh"}}function parseGhPrList(stdout){let entries;try{entries=JSON.parse(stdout)}catch{return null}if(!Array.isArray(entries))return null;let rank={open:3,merged:2,closed:1},map=new Map;for(let entry of entries){let branch=entry?.headRefName,rawState=entry?.state;if(typeof branch!=="string"||typeof rawState!=="string")continue;let state=rawState.toLowerCase();if(state!=="open"&&state!=="merged"&&state!=="closed")continue;let cur=map.get(branch);if(!cur||rank[state]>rank[cur])map.set(branch,state)}return map}var TIMEOUT_MS=4000,manager=new GitWorktreeManager;function createdAt(path){try{let stat=statSync(path);return stat.birthtimeMs||stat.mtimeMs}catch{return 0}}async function run(path,argv){return execHostForWorktreePath(path).run(argv,{cwd:path,signal:AbortSignal.timeout(TIMEOUT_MS)})}async function branchOnRemote(path,branch){try{let out=await run(path,["git","ls-remote","--exit-code","--heads","origin",branch]);return out.exitCode===0?!0:out.exitCode===2?!1:null}catch{return null}}async function defaultRef(repo){try{let out=await run(repo,["git","symbolic-ref","--short","refs/remotes/origin/HEAD"]);if(out.exitCode===0&&out.stdout.trim())return out.stdout.trim()}catch{}for(let guess of["origin/main","origin/master"])try{if((await run(repo,["git","rev-parse","--verify","--quiet",guess])).exitCode===0)return guess}catch{}return null}async function prStates(repo){try{let origin=await run(repo,["git","remote","get-url","origin"]);if(origin.exitCode!==0||!origin.stdout.includes("github.com"))return null;let out=await run(repo,["gh","pr","list","--state","all","--limit","200","--json","headRefName,state"]);return out.exitCode===0?parseGhPrList(out.stdout):null}catch{return null}}async function ahead(path,ref){if(!ref)return null;try{let out=await run(path,["git","rev-list","--count",`${ref}..HEAD`]),value=Number.parseInt(out.stdout.trim(),10);return out.exitCode===0&&!Number.isNaN(value)?value:null}catch{return null}}async function listWorktreeProjectsAdapter(network){let repos=getSavedRepos().filter((repo)=>!getRemoteRepoConfig(repo)),now=Date.now();return Promise.all(repos.map(async(repo)=>{let[worktrees,states,ref]=await Promise.all([manager.listAll(repo),network?prStates(repo):null,defaultRef(repo)]),rows=await Promise.all(worktrees.map(async(worktree)=>{let[remote,aheadBy]=await Promise.all([network?branchOnRemote(worktree.path,worktree.branch):null,ahead(worktree.path,ref)]),judgement=judgeWorktree({dirty:worktree.dirty===!0,prState:states?.get(worktree.branch)??null,aheadOfDefault:aheadBy,lastActivityMs:worktree.lastActivityMs},now);return{...worktree,repo,createdAtMs:createdAt(worktree.path),branchOnRemote:remote,verdict:judgement.verdict,verdictReason:judgement.reason}}));return{repo,worktrees:rows}}))}async function listUnreadableWorktreesAdapter(repo){return manager.listUnreadableWorktrees(repo)}async function removeWorktreeAdapter(path,force){let residue=null;return await manager.remove(path,{force,onSalvage:(record)=>{if(record)auditWorktreeSalvaged(path,record.ref,record.commit,record.uncaptured)},onResidue:(r)=>{residue={path:r.path,reason:r.reason},auditWorktreeResidue(r.path,r.reason)}}),residue}function resolveProtocolUpgradeAndLearnPreset(task,evidence){let preset=protocolWriteBackFromLiveSession(task,evidence,getCustomEngineIds());if(preset)setPersistedString(engineProtocolKey(preset.id),preset.protocol);return protocolUpgradeFromLiveSession(task,evidence)}var daemonRuntime={currentVersion:CURRENT_VERSION,async invokeApi(verb,params,socketPath,route){let[{executeVerb,errorEnvelope},{REMOTE_CALLER},{RoveDaemonClient:RoveDaemonClient2}]=await Promise.all([import("./chunk-yc9bytrj.js"),import("./chunk-ft6ftksv.js"),import("./chunk-k2cnmb5q.js")]),client=new RoveDaemonClient2(socketPath);try{await client.connect();let{invokeRouted}=await import("./chunk-q301ewyq.js");return await invokeRouted({verb,params,machine:route?.machine,forwarded:route?.forwarded,isLocalTask:(id)=>client.request("task.get",{taskId:id}).then(()=>!0,()=>!1),runHere:()=>executeVerb(verb,{params},{client,caller:REMOTE_CALLER})})}catch(err){return errorEnvelope(err)}finally{client.close()}},defaultTaskVendor:DEFAULT_TASK_VENDOR,placeholderTaskTitle:PLACEHOLDER_TASK_TITLE,isTaskStatus,isEngineActivityKind,affectsActivityState,async foregroundEngines(pids){let rows=parsePsSnapshot(await psSnapshot([...pids])),out=new Map;for(let pid of pids){let found=foregroundEngineIn(rows,pid);out.set(pid,found?{vendor:found.vendor,pid:found.pid}:null)}return out},titleTurnHint:(vendor,title)=>engineTitleTurnHint(sessionProtocol(vendor),title),resolveProtocolUpgrade:resolveProtocolUpgradeAndLearnPreset,readEngineTurns:async(vendor,transcriptPath)=>await protocolEntry(vendor).readTurns?.(transcriptPath)??[],checkLatestVersion:()=>checkLatestVersion({channel:preferredReleaseChannel()}),latestTranscriptMtime,deriveTitleFromSession,createEngineTurnDetector:(vendor)=>createEngineTurnDetector(sessionProtocol(vendor)),async runWorktreeStatus(worktreePath,signal,baseRef){let result=await spawnCapture("git",["status","--porcelain=v1"],{cwd:worktreePath,env:readOnlyGitProcessEnv(),signal});if(result.status!==0)throw Error("git status failed");let counts=parsePorcelain(result.stdout),base=await resolveBaseRefCached(worktreePath,baseRef,signal);if(!base)return counts;let drift=await driftCached(worktreePath,base,async()=>{let out=await spawnCapture("git",["rev-list","--left-right","--count",`${base}...HEAD`],{cwd:worktreePath,env:readOnlyGitProcessEnv(),signal});return out.status===0?parseAheadBehind(out.stdout):null});return drift===null?counts:{...counts,...drift}},maybeAutoStart:(orch,taskId)=>maybeAutoStart(orch,taskId),listWorktreeProjects:listWorktreeProjectsAdapter,listUnreadableWorktrees:listUnreadableWorktreesAdapter,removeWorktree:removeWorktreeAdapter,async syncWorktreeWithBase(worktreePath,recordedBaseRef){let controller=new AbortController,timer=setTimeout(()=>controller.abort(),SYNC_TIMEOUT_MS);try{let baseRef=await resolveBaseRefCached(worktreePath,recordedBaseRef,controller.signal);if(!baseRef)throw Error("no base ref resolves for this worktree");return await syncWorktreeWithBase(worktreePath,baseRef,controller.signal)}finally{clearTimeout(timer)}},availableEngineIds,engineDisplayName,roveApiInvocation,engineEntry,ensureTaskSession:ensureTaskSessionAdapter,startTaskSessionWithPrompt:startTaskSessionWithPromptAdapter,tearDownTaskSession:tearDownTaskSessionAdapter,async readEngineContextUsage(vendor,sessionId){let read=protocolEntry(vendor).history.readUsageSnapshot;if(!read)return null;let snapshot=await read(sessionId);if(snapshot?.context_tokens===void 0)return null;return{contextTokens:snapshot.context_tokens,...snapshot.context_window_tokens===void 0?{}:{contextWindowTokens:snapshot.context_window_tokens},...snapshot.context_tokens_approximate?{approximate:!0}:{},...snapshot.input_tokens===void 0?{}:{inputTokens:snapshot.input_tokens},...snapshot.output_tokens===void 0?{}:{outputTokens:snapshot.output_tokens},...snapshot.cache_read_input_tokens===void 0?{}:{cacheReadTokens:snapshot.cache_read_input_tokens},...snapshot.cache_creation_input_tokens===void 0?{}:{cacheCreationTokens:snapshot.cache_creation_input_tokens}}},quotaUsage:(vendor)=>engineEntry(vendor).quotaUsage?.()??Promise.resolve(null),vendorsWithQuotaProbe,deliverPromptToLiveEngine:deliverPromptToLiveEngineAdapter,deliverPromptToLiveEngineDetailed:deliverPromptToLiveEngineDetailedAdapter,deliverPromptToLiveEngineTabDetailed:deliverPromptToLiveEngineTabDetailedAdapter,getPersistedString,setPersistedString,getSavedRepos:()=>[...getSavedRepos()],prStatus:{viewFields:GH_PR_VIEW_FIELDS,mapView:(view,at)=>mapGhPrView(view,at),sameStatus:samePrStatus,nextPoll:(outcome,failures,now,config,random)=>nextPrPoll(outcome,failures,now,config,random),classify:classifyGhFailure}};import{homedir}from"os";async function createRoveCore(options={}){let homeDir=options.homeDir??readRoveHomeDirEnv()??homedir(),store=new TaskIndexStore({homeDir});await store.load();let worktrees=new GitWorktreeManager,orchestrator=new Orchestrator({store,worktrees,onSalvage:(taskId,salvage)=>auditDeletionSalvaged(String(taskId),salvage.ref,salvage.commit,store.get(taskId)?.repo,salvage.uncaptured),onWorktreeResidue:(taskId,residue)=>auditDeletionResidue(String(taskId),residue.path,residue.reason),onBranchKept:(taskId,kept)=>auditDeletionBranchKept(String(taskId),kept.branch,kept.reason),tearDownSession:(taskId)=>tearDownTaskSessionAdapter(String(taskId))}),backfilled=backfillSavedReposFromProjects(store.list().filter((task)=>task.kind==="main").map((task)=>task.repo));if(backfilled.length>0)console.error(`[rove] added ${backfilled.length} existing project(s) to the new-task picker`);return{homeDir,orchestrator,store,worktrees,async close(){orchestrator.dispose()}}}import{existsSync,readFileSync as readFileSync2,readdirSync,statSync as statSync2,unlinkSync}from"fs";import{join as join2}from"path";var TMP_MAX_AGE_MS=300000;function sweepIndexLeftovers(stateDir,now=Date.now()){let tmp=[],tmpBytes=0,entries;try{entries=readdirSync(stateDir)}catch{return{tmp,tmpBytes,lock:!1}}for(let name of entries){if(!name.startsWith("tasks.json.")||!name.endsWith(".tmp"))continue;let path=join2(stateDir,name);try{let stat=statSync2(path);if(now-stat.mtimeMs<TMP_MAX_AGE_MS)continue;unlinkSync(path),tmp.push(path),tmpBytes+=stat.size}catch{}}return{tmp,tmpBytes,lock:clearStaleLock(join2(stateDir,"tasks.json.lock"))}}function clearStaleLock(lockPath){let holder;try{holder=readFileSync2(lockPath,"utf8").trim()}catch{return!1}let pid=Number.parseInt(holder,10);if(Number.isFinite(pid)&&pid>0&&isProcessAlive(pid))return!1;return releaseSync(lockPath,holder),!existsSync(lockPath)}var CLI_NAME=ROVE_PRODUCT_NAME;function printDaemonUsage(out){out.write([`Usage: ${CLI_NAME} daemon <command>`,"","Commands:"," status Print the running daemon's status JSON (default)"," start Run the daemon in the foreground (this process becomes it)"," --resident: never idle-stop when the last TUI leaves (headless host)"," stop Ask the running daemon to shut down"," restart Stop the daemon (graceful \u2192 SIGTERM \u2192 SIGKILL) and respawn it",""].join(`
7
- `))}async function runDaemonSubcommand(argv){let[command="status",...flags]=argv,socketPath=defaultDaemonSocketPath(),pidPath=defaultDaemonPidPath();if(command==="--help"||command==="-h"||command==="help"){printDaemonUsage(process.stdout);return}if(!SUBCOMMAND_VERBS.daemon.includes(command))process.stderr.write(`${CLI_NAME} daemon: unknown command "${command}"
8
-
9
- `),printDaemonUsage(process.stderr),process.exit(2);let badFlag=flags.find((flag)=>!(command==="start"&&argvHasFlag([flag],"--resident"))&&!(command==="status"&&flag==="--json")),resident=switchFlag(flags,"--resident");if(badFlag!==void 0||resident===void 0)process.stderr.write(`${CLI_NAME} daemon ${command}: bad option "${badFlag??flags.join(" ")}"
10
-
11
- `),printDaemonUsage(process.stderr),process.exit(2);if(command==="status"){let client=new RoveDaemonClient(socketPath);try{let status=await client.request("daemon.status");console.log(JSON.stringify(status,null,2))}catch{let pid=await readPidFile(pidPath);if(pid)console.log(`${CLI_NAME} daemon: no daemon socket at ${socketPath} (stale pidfile pid=${pid})`);else console.log(`${CLI_NAME} daemon: no daemon running at ${socketPath}`);process.exitCode=1}finally{client.close()}return}if(command==="stop"){let client=new RoveDaemonClient(socketPath);try{await client.request("daemon.stop"),console.log(`${CLI_NAME} daemon: stop requested`)}catch{console.log(`${CLI_NAME} daemon: no daemon running at ${socketPath}`)}finally{client.close()}return}if(command==="restart"){await stopDaemonProcess(socketPath,pidPath,{reason:"restart"}),(await connectOrStartDaemon("explicit-restart")).close(),console.log(`${CLI_NAME} daemon: restarted, listening on ${socketPath}`);return}rotateLogIfNeeded(defaultDaemonLogPath()),installDaemonCrashHandlers(),logDaemonInfo("boot",`daemon starting \u2014 ${daemonSpawnReason()} (pid ${process.pid}, v${CURRENT_VERSION})`);let core,channels=[],server=await startDaemonServer(async()=>{let migration=migrateRoveDaemonStateLayout();for(let warning of migration.warnings)console.error(`[rove] daemon state migration will retry: ${warning}`);core=await createRoveCore();let swept=sweepIndexLeftovers(core.store.stateDir);if(swept.lock||swept.tmp.length>0)console.error(`[rove] swept crash leftovers in ${core.store.stateDir}: ${swept.tmp.length} orphaned staging file(s) (${swept.tmpBytes} bytes)${swept.lock?", stale task-index lockfile":""}`);return core.orchestrator},{runtime:daemonRuntime,socketPath,pidPath,plugins:{binPath:resolvePluginBinPath()},keepAlive:()=>channels.some((channel)=>channel.keepAlive()),resident,onStop:async()=>{for(let channel of channels)channel.stop();await core?.close()}});console.log(`${CLI_NAME} daemon: listening on ${server.socketPath}`),channels=await startChatChannels({socketPath:server.socketPath,log:logDaemonInfo,keepAliveChanged:()=>server.reevaluateIdle()});let shutdown=async()=>{await server.close(),process.exit(0)};process.once("SIGINT",()=>void shutdown()),process.once("SIGTERM",()=>void shutdown())}export{runDaemonSubcommand};
@@ -1,2 +0,0 @@
1
- // @bun
2
- import{setMachineHub,useMachineRows,useMergedTasks}from"./chunk-yjyjg7sk.js";import"./chunk-2wd4744g.js";import"./chunk-91sykzqq.js";export{useMergedTasks,useMachineRows,setMachineHub};
@@ -1,2 +0,0 @@
1
- // @bun
2
- import{ENGINE_NOT_OBSERVED_REASON,ENGINE_START_POLL_MS,ENGINE_START_PROBE_MS,deliverHostedPrompt,deliverToKey,ensurePtyHost,findEngineKey,isTaskKey,killTaskSessions,listSessions,listSessionsOrNull,openPtyHost,outcomeFields,taskKeys}from"./chunk-csdne12x.js";import"./chunk-3q1rv3rm.js";import"./chunk-sabpcket.js";import"./chunk-ampdybqg.js";import"./chunk-sak1tphp.js";import"./chunk-4250pw6r.js";import"./chunk-82zpp3yx.js";import"./chunk-rxqdw6s5.js";import"./chunk-jetsx9zr.js";import"./chunk-f5n0tgky.js";import"./chunk-z3zaa155.js";import"./chunk-yewmtant.js";import"./chunk-hpay0ax8.js";import"./chunk-catrr0z3.js";import"./chunk-hdnqq66w.js";import"./chunk-b47k0cnw.js";import"./chunk-2tfnscxk.js";import"./chunk-9ecnsh42.js";import"./chunk-w7zv47mp.js";import"./chunk-etfh8f4z.js";import"./chunk-b7xjhgts.js";import"./chunk-x7r75e3p.js";import"./chunk-zsfyj36g.js";import"./chunk-kqf371ff.js";import"./chunk-1vwngrmf.js";import"./chunk-yktnbb14.js";import"./chunk-ag3txfg2.js";import"./chunk-2brn8e1y.js";import"./chunk-t25xke74.js";import"./chunk-7zyyvc3d.js";import"./chunk-r6k3ve2g.js";import"./chunk-xy8be792.js";import"./chunk-7kysxgbq.js";import"./chunk-h2m7em1e.js";import"./chunk-7ghwdjdc.js";import"./chunk-tdhk4dz8.js";import"./chunk-22d0yc7m.js";import"./chunk-40dgv828.js";import"./chunk-3342qzve.js";import"./chunk-g4281a0g.js";import"./chunk-zh0c8yzw.js";import"./chunk-wqckm0k6.js";import"./chunk-prqeddy7.js";import"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-tq6pq1ws.js";import"./chunk-sma961wh.js";import"./chunk-cmp64q4j.js";import"./chunk-s0jbtyd9.js";import"./chunk-278sc87n.js";import"./chunk-gskbzgc3.js";import"./chunk-pn5ygqn6.js";import"./chunk-n0rtj4z7.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import"./chunk-pxk2c2vj.js";import"./chunk-91sykzqq.js";export{taskKeys,outcomeFields,openPtyHost,listSessionsOrNull,listSessions,killTaskSessions,isTaskKey,findEngineKey,ensurePtyHost,deliverToKey,deliverHostedPrompt,ENGINE_START_PROBE_MS,ENGINE_START_POLL_MS,ENGINE_NOT_OBSERVED_REASON};
@@ -1,2 +0,0 @@
1
- // @bun
2
- import{argvToParams,booleanFlagsOf,callVerb,errorBody,errorEnvelope,executeVerb,paramsToFlags,repeatableFlagsOf,runVerb,successEnvelope,toApiError}from"./chunk-9v18qy3s.js";import"./chunk-4ab8y1yp.js";import"./chunk-tpt3fn1q.js";import"./chunk-t60qcvcj.js";import"./chunk-6p9yxt3q.js";import"./chunk-rc2nwsdd.js";import"./chunk-cbc1h345.js";import"./chunk-jjv7xem7.js";import"./chunk-17e2tgyg.js";import"./chunk-68113ts1.js";import"./chunk-4hestsjp.js";import"./chunk-41x1hrqe.js";import"./chunk-5mhvjytn.js";import"./chunk-stcvcsfm.js";import"./chunk-2bgmby1d.js";import"./chunk-edsm15ht.js";import"./chunk-j11hzmwy.js";import"./chunk-atn06ncf.js";import"./chunk-cq5qv93a.js";import"./chunk-qkehg2c1.js";import"./chunk-csdne12x.js";import"./chunk-3q1rv3rm.js";import"./chunk-sabpcket.js";import"./chunk-ampdybqg.js";import"./chunk-sak1tphp.js";import"./chunk-4250pw6r.js";import"./chunk-82zpp3yx.js";import"./chunk-rxqdw6s5.js";import"./chunk-jetsx9zr.js";import"./chunk-f5n0tgky.js";import"./chunk-z3zaa155.js";import"./chunk-yewmtant.js";import"./chunk-hpay0ax8.js";import"./chunk-catrr0z3.js";import"./chunk-hdnqq66w.js";import"./chunk-b47k0cnw.js";import"./chunk-2tfnscxk.js";import"./chunk-9ecnsh42.js";import"./chunk-w7zv47mp.js";import"./chunk-etfh8f4z.js";import"./chunk-t7j8g9f6.js";import"./chunk-dmzg9zdc.js";import"./chunk-kg9kmwt9.js";import"./chunk-6xppe9bj.js";import"./chunk-b7xjhgts.js";import"./chunk-x7r75e3p.js";import"./chunk-zsfyj36g.js";import"./chunk-kqf371ff.js";import"./chunk-1vwngrmf.js";import"./chunk-yktnbb14.js";import"./chunk-ag3txfg2.js";import"./chunk-2brn8e1y.js";import"./chunk-t25xke74.js";import"./chunk-7zyyvc3d.js";import"./chunk-r6k3ve2g.js";import"./chunk-xy8be792.js";import"./chunk-7kysxgbq.js";import"./chunk-h2m7em1e.js";import"./chunk-7ghwdjdc.js";import"./chunk-tdhk4dz8.js";import"./chunk-1xrbb7gf.js";import"./chunk-22d0yc7m.js";import"./chunk-40dgv828.js";import"./chunk-3342qzve.js";import"./chunk-g4281a0g.js";import"./chunk-zh0c8yzw.js";import"./chunk-wqckm0k6.js";import"./chunk-prqeddy7.js";import"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-tq6pq1ws.js";import"./chunk-sma961wh.js";import"./chunk-cmp64q4j.js";import"./chunk-s0jbtyd9.js";import"./chunk-278sc87n.js";import"./chunk-gskbzgc3.js";import"./chunk-pn5ygqn6.js";import"./chunk-n0rtj4z7.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import"./chunk-pxk2c2vj.js";import"./chunk-91sykzqq.js";export{toApiError,successEnvelope,runVerb,repeatableFlagsOf,paramsToFlags,executeVerb,errorEnvelope,errorBody,callVerb,booleanFlagsOf,argvToParams};