@sma1lboy/rove 0.9.108 → 0.9.147

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 (194) hide show
  1. package/README.md +0 -1
  2. package/dist/cli/chunk-003tvywc.js +3 -0
  3. package/dist/cli/chunk-0m9j3yap.js +7 -0
  4. package/dist/cli/chunk-0r8333z3.js +3 -0
  5. package/dist/cli/chunk-0sm2k04y.js +3 -0
  6. package/dist/cli/chunk-0vmrteyg.js +3 -0
  7. package/dist/cli/chunk-0vy8351p.js +18 -0
  8. package/dist/cli/chunk-13s8rnk5.js +3 -0
  9. package/dist/cli/chunk-1kktfmtg.js +4 -0
  10. package/dist/cli/chunk-1rkb0j1j.js +3 -0
  11. package/dist/cli/chunk-1tx1s5v6.js +2 -0
  12. package/dist/cli/chunk-20pt9yqh.js +4 -0
  13. package/dist/cli/chunk-2brn8e1y.js +3 -0
  14. package/dist/cli/chunk-2fypr41w.js +3 -0
  15. package/dist/cli/chunk-2jxdzw4r.js +3 -0
  16. package/dist/cli/chunk-2pqqvse3.js +2 -0
  17. package/dist/cli/chunk-3bfm4hrs.js +3 -0
  18. package/dist/cli/chunk-3ry6prwn.js +3 -0
  19. package/dist/cli/chunk-3ztek08w.js +4 -0
  20. package/dist/cli/chunk-49s2zc0h.js +8 -0
  21. package/dist/cli/chunk-4wwx51m5.js +3 -0
  22. package/dist/cli/chunk-5bn91n19.js +3 -0
  23. package/dist/cli/chunk-5c3d1y0r.js +2 -0
  24. package/dist/cli/chunk-5hhvrvgm.js +8 -0
  25. package/dist/cli/chunk-6dn6h640.js +3 -0
  26. package/dist/cli/chunk-6frfschk.js +2 -0
  27. package/dist/cli/chunk-6rkc7efm.js +5 -0
  28. package/dist/cli/chunk-6xppe9bj.js +3 -0
  29. package/dist/cli/chunk-6zwg6me2.js +4 -0
  30. package/dist/cli/chunk-764bsyb0.js +1 -0
  31. package/dist/cli/chunk-79gqh5p2.js +5 -0
  32. package/dist/cli/chunk-7nhdvr5v.js +4 -0
  33. package/dist/cli/chunk-7q67dz4c.js +3 -0
  34. package/dist/cli/chunk-7ydwn358.js +3 -0
  35. package/dist/cli/chunk-86j25g7y.js +7 -0
  36. package/dist/cli/chunk-88qjhytd.js +12 -0
  37. package/dist/cli/chunk-8arm7k43.js +69 -0
  38. package/dist/cli/chunk-8ddjfc2v.js +4 -0
  39. package/dist/cli/chunk-8r1shxz0.js +16 -0
  40. package/dist/cli/chunk-99devyxr.js +2 -0
  41. package/dist/cli/chunk-9c1e5z6a.js +3 -0
  42. package/dist/cli/chunk-9n458sdy.js +2 -0
  43. package/dist/cli/chunk-aq912vvw.js +3 -0
  44. package/dist/cli/chunk-arf294te.js +2 -0
  45. package/dist/cli/chunk-awsawfrm.js +2 -0
  46. package/dist/cli/chunk-aynx3d2a.js +2 -0
  47. package/dist/cli/chunk-b99tf0jr.js +3 -0
  48. package/dist/cli/chunk-bdbgmjt7.js +3 -0
  49. package/dist/cli/chunk-c6mthex1.js +5 -0
  50. package/dist/cli/chunk-cb0pgyq1.js +8 -0
  51. package/dist/cli/chunk-cb2fpajx.js +3 -0
  52. package/dist/cli/chunk-cehhk94g.js +2 -0
  53. package/dist/cli/chunk-d4bqay6d.js +9 -0
  54. package/dist/cli/chunk-dcb59tsn.js +2 -0
  55. package/dist/cli/chunk-df7qe9y7.js +4 -0
  56. package/dist/cli/chunk-dkxhmj9k.js +25 -0
  57. package/dist/cli/chunk-dnb2bmtb.js +3 -0
  58. package/dist/cli/chunk-dqndd97s.js +7 -0
  59. package/dist/cli/chunk-dvcb6mk4.js +3 -0
  60. package/dist/cli/chunk-dw853n4w.js +3 -0
  61. package/dist/cli/chunk-dwsgeq06.js +15 -0
  62. package/dist/cli/chunk-e1g9mvww.js +11 -0
  63. package/dist/cli/chunk-e3ye4kmk.js +2 -0
  64. package/dist/cli/chunk-ef11h2am.js +3 -0
  65. package/dist/cli/chunk-ef39ym8f.js +3 -0
  66. package/dist/cli/chunk-ewdhz3dv.js +2 -0
  67. package/dist/cli/chunk-f6zsej8e.js +3 -0
  68. package/dist/cli/chunk-fe34e0w7.js +28 -0
  69. package/dist/cli/chunk-g1ybdgad.js +25 -0
  70. package/dist/cli/chunk-gbasmvsc.js +4 -0
  71. package/dist/cli/chunk-gjf604bb.js +3 -0
  72. package/dist/cli/chunk-gm3vj0fv.js +5 -0
  73. package/dist/cli/chunk-grh6dvfv.js +5 -0
  74. package/dist/cli/chunk-gwcneg6c.js +2 -0
  75. package/dist/cli/chunk-h5j8b87f.js +2 -0
  76. package/dist/cli/chunk-h79zfebh.js +7 -0
  77. package/dist/cli/chunk-h82kzdsn.js +3 -0
  78. package/dist/cli/chunk-hape5vwj.js +3 -0
  79. package/dist/cli/chunk-hg5x2wr0.js +3 -0
  80. package/dist/cli/chunk-hgq2gtxx.js +4 -0
  81. package/dist/cli/chunk-j4xfsn9g.js +5 -0
  82. package/dist/cli/chunk-j9g1m4dy.js +2 -0
  83. package/dist/cli/chunk-jdbfawrm.js +3 -0
  84. package/dist/cli/chunk-jsss3ze7.js +11 -0
  85. package/dist/cli/chunk-jvssy49v.js +2 -0
  86. package/dist/cli/chunk-jvxh93nz.js +3 -0
  87. package/dist/cli/chunk-jyv82gf0.js +258 -0
  88. package/dist/cli/chunk-k3ksts6g.js +3 -0
  89. package/dist/cli/chunk-kdrrw9tm.js +2 -0
  90. package/dist/cli/chunk-kg11c1py.js +3 -0
  91. package/dist/cli/chunk-kwe61yx7.js +3 -0
  92. package/dist/cli/chunk-mb5m7ex7.js +2 -0
  93. package/dist/cli/chunk-mfqjdpd8.js +2 -0
  94. package/dist/cli/chunk-mjvq39gf.js +4 -0
  95. package/dist/cli/chunk-mrwee8sj.js +3 -0
  96. package/dist/cli/chunk-my20q579.js +3 -0
  97. package/dist/cli/chunk-myfjt6dw.js +7 -0
  98. package/dist/cli/chunk-nceh1q3d.js +12 -0
  99. package/dist/cli/chunk-ncsgawg7.js +2 -0
  100. package/dist/cli/chunk-nm6adny4.js +4 -0
  101. package/dist/cli/chunk-np07kccr.js +4 -0
  102. package/dist/cli/chunk-p56s3ekm.js +3 -0
  103. package/dist/cli/chunk-p5spmf7e.js +2 -0
  104. package/dist/cli/chunk-p7kztgsd.js +4 -0
  105. package/dist/cli/chunk-pdh5w4ht.js +3 -0
  106. package/dist/cli/chunk-pdj8kqa1.js +74 -0
  107. package/dist/cli/chunk-psx9tk3x.js +4 -0
  108. package/dist/cli/chunk-pw6yfj10.js +3 -0
  109. package/dist/cli/chunk-q6579rbz.js +3 -0
  110. package/dist/cli/chunk-q7dr9eh6.js +3 -0
  111. package/dist/cli/chunk-q9x6d0k6.js +3 -0
  112. package/dist/cli/chunk-qkehg2c1.js +3 -0
  113. package/dist/cli/chunk-qtke4x42.js +3 -0
  114. package/dist/cli/chunk-rgcv1wcs.js +4 -0
  115. package/dist/cli/chunk-rxvbm4vt.js +7 -0
  116. package/dist/cli/chunk-ryhrw647.js +3 -0
  117. package/dist/cli/chunk-s76r56rj.js +3 -0
  118. package/dist/cli/chunk-sdyzxayr.js +3 -0
  119. package/dist/cli/chunk-svpn3av1.js +3 -0
  120. package/dist/cli/chunk-v5893dx3.js +2 -0
  121. package/dist/cli/chunk-vapjtyv9.js +7 -0
  122. package/dist/cli/chunk-vkch2spv.js +33 -0
  123. package/dist/cli/chunk-vpzvcxc1.js +3 -0
  124. package/dist/cli/chunk-vws0j65e.js +4 -0
  125. package/dist/cli/chunk-vyb5a2rh.js +4 -0
  126. package/dist/cli/chunk-w298ayjc.js +3 -0
  127. package/dist/cli/chunk-w48zjgxv.js +9 -0
  128. package/dist/cli/chunk-w5xy83ze.js +2 -0
  129. package/dist/cli/chunk-wpgmq5z6.js +106 -0
  130. package/dist/cli/chunk-wqfp8d9w.js +3 -0
  131. package/dist/cli/chunk-x760j10f.js +3 -0
  132. package/dist/cli/chunk-xbc3gcry.js +2 -0
  133. package/dist/cli/chunk-xy8be792.js +3 -0
  134. package/dist/cli/chunk-yhcz2d2r.js +34 -0
  135. package/dist/cli/chunk-yk2ha5yj.js +3 -0
  136. package/dist/cli/chunk-ykvk2546.js +3 -0
  137. package/dist/cli/chunk-ympakh8w.js +6 -0
  138. package/dist/cli/chunk-zb56ck5f.js +3 -0
  139. package/dist/cli/chunk-zwzkve1e.js +2 -0
  140. package/dist/cli/chunk-zy6vgjdp.js +19 -0
  141. package/dist/cli/index.js +15 -845
  142. package/dist/cli/kobe-run.js +1 -853
  143. package/dist/cli/kobe.js +6 -6
  144. package/dist/cli/pty-host-node.mjs +190 -52
  145. package/dist/cli/rove-run.js +1 -853
  146. package/dist/cli/rove.js +6 -6
  147. package/dist/skills/rove/SKILL.md +29 -10
  148. package/dist/skills/rove/references/api-flags.md +35 -13
  149. package/dist/web-ui/assets/{ChatTerminal-BshL3M8G.js → harness-CYGJ4uoR.js} +19 -20
  150. package/dist/web-ui/assets/index-BTJ-X_4s.js +10 -0
  151. package/dist/web-ui/assets/index-Cs2vPLNB.css +1 -0
  152. package/dist/web-ui/index.html +2 -9
  153. package/dist/web-ui/pty-server.mjs +4 -5
  154. package/package.json +2 -2
  155. package/dist/web-ui/assets/AppShell-BGRIs5hW.js +0 -5
  156. package/dist/web-ui/assets/AppShell-HiyATJse.css +0 -1
  157. package/dist/web-ui/assets/Board-Bt3cwp25.js +0 -1
  158. package/dist/web-ui/assets/BoardPeek-BKR-tpS2.js +0 -2
  159. package/dist/web-ui/assets/ChatTranscript-B67_nnd6.js +0 -1
  160. package/dist/web-ui/assets/ConfirmDialog-kyA624OF.js +0 -1
  161. package/dist/web-ui/assets/IssueIntakePanel-BMP8Vc3E.js +0 -1
  162. package/dist/web-ui/assets/IssuePeek-C7afOYax.js +0 -1
  163. package/dist/web-ui/assets/IssuesPage-DwRaBB-m.js +0 -1
  164. package/dist/web-ui/assets/SettingsPage-CC1sDI8j.js +0 -2
  165. package/dist/web-ui/assets/SlideOver-B4sEOPyN.css +0 -1
  166. package/dist/web-ui/assets/SlideOver-Dq0ifiEm.js +0 -173
  167. package/dist/web-ui/assets/ViewToggle-mNsDLJp8.js +0 -1
  168. package/dist/web-ui/assets/WorkspaceTabs-DLzL-Tad.css +0 -1
  169. package/dist/web-ui/assets/WorkspaceTabs-ifAIZ5Fj.js +0 -3
  170. package/dist/web-ui/assets/active-task-gVYiAkLv.js +0 -1
  171. package/dist/web-ui/assets/arrow-left-CavMh90a.js +0 -1
  172. package/dist/web-ui/assets/board-DoArj55z.js +0 -1
  173. package/dist/web-ui/assets/chips-CS9PLtWy.js +0 -1
  174. package/dist/web-ui/assets/createLucideIcon-CuIQtyYV.js +0 -1
  175. package/dist/web-ui/assets/external-link-Du2dMe8S.js +0 -1
  176. package/dist/web-ui/assets/external-store-BCBJewGi.js +0 -1
  177. package/dist/web-ui/assets/harness-ClZMF1wx.js +0 -1
  178. package/dist/web-ui/assets/index-B4xJZEqf.js +0 -10
  179. package/dist/web-ui/assets/index-BnjD9Q-3.css +0 -1
  180. package/dist/web-ui/assets/issues-DwrKXp9c.js +0 -4
  181. package/dist/web-ui/assets/issues-ISAZBras.js +0 -1
  182. package/dist/web-ui/assets/lazy-issue-panels-DvXQJhKy.js +0 -6
  183. package/dist/web-ui/assets/rotate-cw-BXdpzm3v.js +0 -1
  184. package/dist/web-ui/assets/routes-DEEkL5gG.js +0 -1
  185. package/dist/web-ui/assets/store-BLM6nh6E.js +0 -2
  186. package/dist/web-ui/assets/tabs-CMZJhcSB.js +0 -1
  187. package/dist/web-ui/assets/task._taskId-BCIhg4IQ.js +0 -1
  188. package/dist/web-ui/assets/terminal-DOp1a9g6.js +0 -1
  189. package/dist/web-ui/assets/text-match-DW3sD-Rt.js +0 -1
  190. package/dist/web-ui/assets/time-kviT26Tm.js +0 -1
  191. package/dist/web-ui/assets/toast-C4yXq6hM.js +0 -1
  192. package/dist/web-ui/assets/web-transport-Beqz2mQ8.js +0 -1
  193. package/dist/web-ui/assets/worktrees-BGvJOG2b.js +0 -1
  194. /package/dist/web-ui/assets/{ChatTerminal-BrP-ENHg.css → harness-BrP-ENHg.css} +0 -0
package/dist/cli/kobe.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import{join as $1}from"node:path";import{createInterface as J1}from"node:readline/promises";import{pathToFileURL as Q1}from"node:url";import{spawnSync as L}from"node:child_process";import{constants as u,accessSync as l}from"node:fs";import{homedir as t}from"node:os";import{basename as o,delimiter as r,dirname as i,join as Y}from"node:path";import{fileURLToPath as C}from"node:url";var U={$schema:"https://json.schemastore.org/package.json",name:"@sma1lboy/rove",version:"0.9.108",description:"Rove — the agent multiplexer for your terminal. Run coding agents on parallel tasks with isolated worktrees and persistent sessions.",keywords:["terminal","tui","cli","multiplexer","ai-agents","coding-agent","ai-coding-assistant","agentic-ai","parallel-agents","git-worktree","claude-code","codex","llm","developer-tools"],type:"module",packageManager:"bun@1.3.13",bin:{kobe:"dist/cli/kobe.js",rove:"dist/cli/rove.js"},files:["dist/cli","dist/web-ui","dist/skills","dist/*.wav","README.md","LICENSE"],publishConfig:{access:"public"},repository:{type:"git",url:"git+https://github.com/Sma1lboy/rove.git"},homepage:"https://rove.run",bugs:{url:"https://github.com/Sma1lboy/rove/issues"},engines:{bun:">=1.3.11"},scripts:{dev:"ROVE_DEV=1 bun --conditions=browser ./src/cli/rove.ts","dev:kobe":"KOBE_DEV=1 bun --conditions=browser ./src/cli/kobe.ts","dev:mock":"KOBE_DEV=1 bun ./src/tui-react/mock/host.tsx","dev:sandbox":"bun run scripts/dev-sandbox.ts run","dev:sandbox:reset":"bun run scripts/dev-sandbox.ts reset",build:"bun run scripts/build.ts",compile:"bun run scripts/compile.ts",typecheck:"tsc --noEmit","check-i18n":"bun run scripts/check-i18n.ts",test:"bun run test:fast && bun run test:socket","perf:golden":"bun scripts/perf-golden.ts","pty:soak":"bun scripts/pty-soak.ts","test:fast":"vitest run --passWithNoTests --minWorkers=1 --maxWorkers=8","test:socket":"KOBE_INCLUDE_SOCKET=1 vitest run test/daemon --pool forks --minWorkers=1 --maxWorkers=4 --passWithNoTests","test:socket:coverage":"KOBE_INCLUDE_SOCKET=1 KOBE_COVERAGE_DAEMON=1 vitest run test/daemon --coverage --pool forks --minWorkers=1 --maxWorkers=4 --passWithNoTests","test:behavior":"KOBE_INCLUDE_BEHAVIOR=1 vitest run test/behavior --pool forks --minWorkers=1 --maxWorkers=1 --retry=2 --passWithNoTests","test:render":"bun test test/render --coverage --coverage-reporter=text --coverage-reporter=lcov --coverage-dir=coverage-render",coverage:"vitest run --coverage --passWithNoTests",bench:"vitest bench --run",lint:"biome check .",knip:"knip-bun",postinstall:"bun run scripts/check-preview-deps.ts || true",prepublishOnly:"bun run typecheck && bun run build","plugin-sandbox":"bun run scripts/plugin-sandbox.ts"},"//":"biome.json + bunfig.toml live at the monorepo root since they apply repo-wide; bun.lock also lives at root (workspace-shared).",dependencies:{"@ansi-tools/parser":"^1.0.15","@opentui/core":"0.4.3","@opentui/react":"0.4.3","@xterm/addon-serialize":"^0.14.0","@xterm/addon-unicode11":"^0.9.0","@xterm/headless":"^6.0.0","node-pty":"^1.1.0",react:"^19.2.8",ws:"^8.18.0"},devDependencies:{"@biomejs/biome":"1.9.4","@sma1lboy/kobe-daemon":"0.7.17","@tsconfig/bun":"1.0.10","@types/bun":"1.3.14","@types/node":"25.6.2","@types/react":"^19.2.0","@vitest/coverage-v8":"2.1.9",knip:"^6.14.2","react-devtools-core":"^7.0.1",typescript:"5.8.2",vitest:"2.1.9"},trustedDependencies:[]};var x="rove",j="kobe";var E="ROVE_INVOKED_AS";function F(W=process.env){return W[E]===x?x:j}var k="ROVE_BUN",a="ROVE_NO_BUN_BOOTSTRAP",M="ROVE_SKIP_BUN_CHECK",G=U.engines.bun.match(/\d+\.\d+\.\d+/)?.[0]??"0.0.0",T=(W)=>{try{return l(W,u.X_OK),!0}catch{return!1}},n=(W)=>W==="win32"?"bun.exe":"bun",e=(W)=>{let b=L(W,["--version"],{encoding:"utf8",timeout:5000});if(b.error||b.status!==0)return null;return b.stdout?.split(`
3
- `)[0]?.trim()??""};function K(W){return W.trim().match(/^v?(\d+\.\d+\.\d+)/)?.[1]??null}function P(W,b=G){let z=K(W),Q=K(b);if(!z||!Q)return!0;let J=z.split(".").map(Number),$=Q.split(".").map(Number);for(let X=0;X<3;X++){let q=J[X]??0,H=$[X]??0;if(q!==H)return q>H}return!0}function g(W=process.env){return W[M]==="1"}function p(W={}){let b=W.env??process.env,z=W.platform??process.platform,Q=W.home??b.HOME??b.USERPROFILE??t(),J=n(z),$=[],X=b[k]?.trim();if(X)$.push(X);for(let D of(b.PATH??b.Path??"").split(r))if(D)$.push(Y(D,J));let q=b.BUN_INSTALL?.trim();if(q)$.push(Y(q,"bin",J));if(Q)$.push(Y(Q,".bun","bin",J));let H=W.launcherDir;if(H)$.push(Y(H,"..","..","node_modules","bun","bin",J)),$.push(Y(H,"..","..","..","bun","bin",J));return $}function W1(W={}){let b=W.isExecutable??T;return p(W).find((z)=>b(z))??null}function w(W={}){let b=W.isExecutable??T,z=W.bunVersionOf??e;if(g(W.env??process.env))return{bun:W1(W),stale:null,unusable:null};let Q=null,J=null;for(let $ of p(W)){if(!b($))continue;let X=z($);if(X===null){J??=$;continue}if(P(X))return{bun:$,stale:Q,unusable:J};Q??={path:$,version:K(X)??X}}return{bun:null,stale:Q,unusable:J}}function B(W){return i(C(W))}function O(W){return o(C(W)).replace(/\.[cm]?[jt]s$/,"")}function b1(W=process.platform){return W==="win32"?["powershell","-NoProfile","-Command","irm bun.sh/install.ps1 | iex"]:["bash","-c","curl -fsSL https://bun.sh/install | bash"]}function y(W=F(),b=process.platform){let z=b==="win32"?'powershell -c "irm bun.sh/install.ps1 | iex"':"curl -fsSL https://bun.sh/install | bash";return[`${W}: Rove runs on the Bun runtime, and no Bun was found on this machine.`,"","Install Bun, then run this command again:",` ${z}`," npm install -g bun # any platform, if you already have npm","","Or install Bun and Rove together in one step:"," curl -fsSL https://rove.run/install.sh | sh","",`Already have Bun somewhere unusual? Point Rove at it: ${k}=/path/to/bun`,""].join(`
4
- `)}function h(W,b,z=F()){return[`${z}: this machine's Bun is too old for Rove — found ${b} at ${W}, need ${G} or newer.`,"","Rove's terminals are built on Bun's PTY API. An older Bun ignores it silently,","so every terminal and engine tab would open empty and stay empty.","","Upgrade Bun, then run this command again:"," bun upgrade # Bun installed itself (~/.bun)"," brew upgrade bun # Homebrew"," npm install -g bun@latest # npm-managed Bun","",`Have a newer Bun elsewhere? Point Rove at it: ${k}=/path/to/bun`,`Run on this one anyway (unsupported, terminals will be blank): ${M}=1`,""].join(`
5
- `)}function A(W,b=F()){return[`${b}: the Bun at ${W} could not be run — \`${W} --version\` failed or never returned.`,"","Rove skipped it rather than starting through it, which would hang or fail","with a message from that binary instead of this one.","","Reinstall Bun, then run this command again:"," curl -fsSL https://bun.sh/install | bash","",`Have a working Bun elsewhere? Point Rove at it: ${k}=/path/to/bun`,""].join(`
6
- `)}function f(W=process.env,b=process.stdin,z=process.stdout){if(W[a]==="1")return!1;if(W.CI==="true"||W.CI==="1")return!1;return Boolean(b.isTTY&&z.isTTY)}function I(W={},b=L){let z=W.platform??process.platform,[Q,...J]=b1(z);if(!Q)return null;let $=b(Q,J,{stdio:"inherit"});if($.error||$.status!==0)return null;return w(W).bun}function z1(W){if(typeof W.status==="number")return W.status;if(W.signal)return 128+(Object.hasOwn(V,W.signal)?V[W.signal]:0);return 1}var V={SIGINT:2,SIGQUIT:3,SIGKILL:9,SIGTERM:15};function R(W,b,z,Q=L){let J=Q(W,[b,...z],{stdio:"inherit"});if(J.error)return process.stderr.write(`rove: could not start Bun at ${W}: ${J.error.message}
7
- `),1;return z1(J)}var N=B(import.meta.url),Z=O(import.meta.url),c=$1(N,`${Z}-run.js`);async function X1(W){let b=J1({input:process.stdin,output:process.stdout});try{let z=(await b.question(W)).trim().toLowerCase();return z===""||z==="y"||z==="yes"}finally{b.close()}}async function Z1(){let W={launcherDir:N},b=w(W);if(b.bun||!f())return b;let z=b.stale?`${Z}: Bun ${b.stale.version} is too old for Rove (needs ${G}+). Install the latest Bun now? [Y/n] `:`${Z}: Rove runs on the Bun runtime, and none is installed. Install Bun now? [Y/n] `;if(!await X1(z))return b;return{bun:I(W),stale:b.stale,unusable:b.unusable}}var v=globalThis.Bun;if(v){let W=v.version;if(W&&!g()&&!P(W))process.stderr.write(h(process.execPath,W,Z)),process.exit(1);await import(Q1(c).href)}else{let{bun:W,stale:b,unusable:z}=await Z1();if(!W){let Q=b?h(b.path,b.version,Z):z?A(z,Z):y(Z);process.stderr.write(Q),process.exit(1)}process.exit(R(W,c,process.argv.slice(2)))}
2
+ import{join as z1}from"node:path";import{createInterface as $1}from"node:readline/promises";import{pathToFileURL as J1}from"node:url";import{spawnSync as L}from"node:child_process";import{constants as m,accessSync as u}from"node:fs";import{homedir as l}from"node:os";import{basename as t,delimiter as o,dirname as r,join as Y}from"node:path";import{fileURLToPath as T}from"node:url";var h={$schema:"https://json.schemastore.org/package.json",name:"@sma1lboy/rove",version:"0.9.147",description:"Rove — the agent multiplexer for your terminal. Run coding agents on parallel tasks with isolated worktrees and persistent sessions.",keywords:["terminal","tui","cli","multiplexer","ai-agents","coding-agent","ai-coding-assistant","agentic-ai","parallel-agents","git-worktree","claude-code","codex","llm","developer-tools"],type:"module",packageManager:"bun@1.3.13",bin:{kobe:"dist/cli/kobe.js",rove:"dist/cli/rove.js"},files:["dist/cli","dist/web-ui","dist/skills","dist/*.wav","README.md","LICENSE"],publishConfig:{access:"public"},repository:{type:"git",url:"git+https://github.com/Sma1lboy/rove.git"},homepage:"https://rove.run",bugs:{url:"https://github.com/Sma1lboy/rove/issues"},engines:{bun:">=1.3.11"},scripts:{dev:"ROVE_DEV=1 bun --conditions=browser ./src/cli/rove.ts","dev:kobe":"KOBE_DEV=1 bun --conditions=browser ./src/cli/kobe.ts","dev:mock":"KOBE_DEV=1 bun ./src/tui-react/mock/host.tsx","dev:sandbox":"bun run scripts/dev-sandbox.ts run","dev:sandbox:reset":"bun run scripts/dev-sandbox.ts reset",build:"bun run scripts/build.ts",compile:"bun run scripts/compile.ts",typecheck:"tsc --noEmit","check-i18n":"bun run scripts/check-i18n.ts",test:"bun run test:fast && bun run test:socket","perf:golden":"bun scripts/perf-golden.ts","pty:soak":"bun scripts/pty-soak.ts","test:fast":"vitest run --passWithNoTests --minWorkers=1 --maxWorkers=8","test:socket":"KOBE_INCLUDE_SOCKET=1 vitest run test/daemon --pool forks --minWorkers=1 --maxWorkers=4 --passWithNoTests","test:socket:coverage":"KOBE_INCLUDE_SOCKET=1 KOBE_COVERAGE_DAEMON=1 vitest run test/daemon --coverage --pool forks --minWorkers=1 --maxWorkers=4 --passWithNoTests","test:behavior":"KOBE_INCLUDE_BEHAVIOR=1 vitest run test/behavior --pool forks --minWorkers=1 --maxWorkers=1 --retry=2 --passWithNoTests","test:render":"bun test test/render --coverage --coverage-reporter=text --coverage-reporter=lcov --coverage-dir=coverage-render",coverage:"vitest run --coverage --passWithNoTests",bench:"vitest bench --run",lint:"biome check .",knip:"knip-bun",postinstall:"bun run scripts/check-preview-deps.ts || true",prepublishOnly:"bun run typecheck && bun run build","plugin-sandbox":"bun run scripts/plugin-sandbox.ts"},"//":"biome.json + bunfig.toml live at the monorepo root since they apply repo-wide; bun.lock also lives at root (workspace-shared).",dependencies:{"@ansi-tools/parser":"^1.0.15","@opentui/core":"0.4.3","@opentui/react":"0.4.3","@xterm/addon-serialize":"^0.14.0","@xterm/addon-unicode11":"^0.9.0","@xterm/headless":"^6.0.0","node-pty":"^1.1.0",react:"^19.2.8",ws:"^8.18.0"},devDependencies:{"@biomejs/biome":"1.9.4","@sma1lboy/kobe-daemon":"0.7.18","@tsconfig/bun":"1.0.10","@types/bun":"1.3.14","@types/node":"25.6.2","@types/react":"^19.2.0","@vitest/coverage-v8":"2.1.9",knip:"^6.14.2","react-devtools-core":"^7.0.1",typescript:"5.8.2",vitest:"2.1.9"},trustedDependencies:[]};var U="rove",C="kobe";var V=C;var k=U;var E="ROVE_INVOKED_AS";function x(W=process.env){return W[E]===k?k:V}var F="ROVE_BUN",i="ROVE_NO_BUN_BOOTSTRAP",O="ROVE_SKIP_BUN_CHECK",G=h.engines.bun.match(/\d+\.\d+\.\d+/)?.[0]??"0.0.0",B=(W)=>{try{return u(W,m.X_OK),!0}catch{return!1}},a=(W)=>W==="win32"?"bun.exe":"bun",n=(W)=>{let b=L(W,["--version"],{encoding:"utf8",timeout:5000});if(b.error||b.status!==0)return null;return b.stdout?.split(`
3
+ `)[0]?.trim()??""};function K(W){return W.trim().match(/^v?(\d+\.\d+\.\d+)/)?.[1]??null}function j(W,b=G){let z=K(W),Q=K(b);if(!z||!Q)return!0;let J=z.split(".").map(Number),$=Q.split(".").map(Number);for(let X=0;X<3;X++){let q=J[X]??0,H=$[X]??0;if(q!==H)return q>H}return!0}function P(W=process.env){return W[O]==="1"}function p(W={}){let b=W.env??process.env,z=W.platform??process.platform,Q=W.home??b.HOME??b.USERPROFILE??l(),J=a(z),$=[],X=b[F]?.trim();if(X)$.push(X);for(let w of(b.PATH??b.Path??"").split(o))if(w)$.push(Y(w,J));let q=b.BUN_INSTALL?.trim();if(q)$.push(Y(q,"bin",J));if(Q)$.push(Y(Q,".bun","bin",J));let H=W.launcherDir;if(H)$.push(Y(H,"..","..","node_modules","bun","bin",J)),$.push(Y(H,"..","..","..","bun","bin",J));return $}function e(W={}){let b=W.isExecutable??B;return p(W).find((z)=>b(z))??null}function D(W={}){let b=W.isExecutable??B,z=W.bunVersionOf??n;if(P(W.env??process.env))return{bun:e(W),stale:null,unusable:null};let Q=null,J=null;for(let $ of p(W)){if(!b($))continue;let X=z($);if(X===null){J??=$;continue}if(j(X))return{bun:$,stale:Q,unusable:J};Q??={path:$,version:K(X)??X}}return{bun:null,stale:Q,unusable:J}}function A(W){return r(T(W))}function R(W){return t(T(W)).replace(/\.[cm]?[jt]s$/,"")}function W1(W=process.platform){return W==="win32"?["powershell","-NoProfile","-Command","irm bun.sh/install.ps1 | iex"]:["bash","-c","curl -fsSL https://bun.sh/install | bash"]}function y(W=x(),b=process.platform){let z=b==="win32"?'powershell -c "irm bun.sh/install.ps1 | iex"':"curl -fsSL https://bun.sh/install | bash";return[`${W}: Rove runs on the Bun runtime, and no Bun was found on this machine.`,"","Install Bun, then run this command again:",` ${z}`," npm install -g bun # any platform, if you already have npm","","Or install Bun and Rove together in one step:"," curl -fsSL https://rove.run/install.sh | sh","",`Already have Bun somewhere unusual? Point Rove at it: ${F}=/path/to/bun`,""].join(`
4
+ `)}function g(W,b,z=x()){return[`${z}: this machine's Bun is too old for Rove — found ${b} at ${W}, need ${G} or newer.`,"","Rove's terminals are built on Bun's PTY API. An older Bun ignores it silently,","so every terminal and engine tab would open empty and stay empty.","","Upgrade Bun, then run this command again:"," bun upgrade # Bun installed itself (~/.bun)"," brew upgrade bun # Homebrew"," npm install -g bun@latest # npm-managed Bun","",`Have a newer Bun elsewhere? Point Rove at it: ${F}=/path/to/bun`,`Run on this one anyway (unsupported, terminals will be blank): ${O}=1`,""].join(`
5
+ `)}function I(W,b=x()){return[`${b}: the Bun at ${W} could not be run — \`${W} --version\` failed or never returned.`,"","Rove skipped it rather than starting through it, which would hang or fail","with a message from that binary instead of this one.","","Reinstall Bun, then run this command again:"," curl -fsSL https://bun.sh/install | bash","",`Have a working Bun elsewhere? Point Rove at it: ${F}=/path/to/bun`,""].join(`
6
+ `)}function f(W=process.env,b=process.stdin,z=process.stdout){if(W[i]==="1")return!1;if(W.CI==="true"||W.CI==="1")return!1;return Boolean(b.isTTY&&z.isTTY)}function c(W={},b=L){let z=W.platform??process.platform,[Q,...J]=W1(z);if(!Q)return null;let $=b(Q,J,{stdio:"inherit"});if($.error||$.status!==0)return null;return D(W).bun}function b1(W){if(typeof W.status==="number")return W.status;if(W.signal)return 128+(Object.hasOwn(M,W.signal)?M[W.signal]:0);return 1}var M={SIGINT:2,SIGQUIT:3,SIGKILL:9,SIGTERM:15};function _(W,b,z,Q=L){let J=Q(W,[b,...z],{stdio:"inherit"});if(J.error)return process.stderr.write(`rove: could not start Bun at ${W}: ${J.error.message}
7
+ `),1;return b1(J)}var S=A(import.meta.url),Z=R(import.meta.url),N=z1(S,`${Z}-run.js`);async function Q1(W){let b=$1({input:process.stdin,output:process.stdout});try{let z=(await b.question(W)).trim().toLowerCase();return z===""||z==="y"||z==="yes"}finally{b.close()}}async function X1(){let W={launcherDir:S},b=D(W);if(b.bun||!f())return b;let z=b.stale?`${Z}: Bun ${b.stale.version} is too old for Rove (needs ${G}+). Install the latest Bun now? [Y/n] `:`${Z}: Rove runs on the Bun runtime, and none is installed. Install Bun now? [Y/n] `;if(!await Q1(z))return b;return{bun:c(W),stale:b.stale,unusable:b.unusable}}var v=globalThis.Bun;if(v){let W=v.version;if(W&&!P()&&!j(W))process.stderr.write(g(process.execPath,W,Z)),process.exit(1);await import(J1(N).href)}else{let{bun:W,stale:b,unusable:z}=await X1();if(!W){let Q=b?g(b.path,b.version,Z):z?I(z,Z):y(Z);process.stderr.write(Q),process.exit(1)}process.exit(_(W,N,process.argv.slice(2)))}
@@ -32,6 +32,14 @@ var COMPAT_STATE_DIR_BASENAME = ".kobe";
32
32
  function readRoveEnv(suffix, env = process.env) {
33
33
  return env[`${ROVE_ENV_PREFIX}${suffix}`] ?? env[`${LEGACY_KOBE_ENV_PREFIX}${suffix}`];
34
34
  }
35
+ function readRoveHomeDirEnv(env = process.env) {
36
+ for (const key of [`${ROVE_ENV_PREFIX}HOME_DIR`, `${LEGACY_KOBE_ENV_PREFIX}HOME_DIR`]) {
37
+ const value = env[key]?.trim();
38
+ if (value)
39
+ return value;
40
+ }
41
+ return;
42
+ }
35
43
 
36
44
  // ../kobe-daemon/src/daemon/paths.ts
37
45
  function stateDirs(homeDir) {
@@ -68,12 +76,7 @@ function legacyPtyHostPidPath(homeDir) {
68
76
  return legacyRuntimePath(homeDir, "pty.pid");
69
77
  }
70
78
  function runtimeDataPath(homeDir, name) {
71
- const { canonical, legacy } = stateDirs(homeDir);
72
- const canonicalPath = join(canonical, name);
73
- if (existsSync2(canonicalPath))
74
- return canonicalPath;
75
- const legacyPath = join(legacy, name);
76
- return existsSync2(legacyPath) ? legacyPath : canonicalPath;
79
+ return join(stateDirs(homeDir).canonical, name);
77
80
  }
78
81
  var SOCKET_PATH_SAFETY_LIMIT = 100;
79
82
  function shortHomeTag(homeDir) {
@@ -90,7 +93,7 @@ function fitSocketPath(naturalPath, homeDir, role, pidTag) {
90
93
  throw new Error(`daemon socket path exceeds ${SOCKET_PATH_SAFETY_LIMIT} bytes even after fallback: ${fallback}`);
91
94
  }
92
95
  function resolveDaemonHomeDir(homeDir) {
93
- const explicit = homeDir ?? readRoveEnv("HOME_DIR");
96
+ const explicit = homeDir ?? readRoveHomeDirEnv();
94
97
  return explicit && explicit.length > 0 ? explicit : homedir();
95
98
  }
96
99
  function isWindowsPipePath(path) {
@@ -103,7 +106,7 @@ function defaultPtyHostSocketPath(homeDir, platform = process.platform) {
103
106
  const override = readRoveEnv("PTY_SOCKET_PATH");
104
107
  if (override && override.length > 0)
105
108
  return override;
106
- const explicit = homeDir ?? readRoveEnv("HOME_DIR");
109
+ const explicit = homeDir ?? readRoveHomeDirEnv();
107
110
  if (platform === "win32")
108
111
  return windowsPipePath(explicit || homedir(), "pty");
109
112
  if (explicit && explicit.length > 0) {
@@ -116,19 +119,19 @@ function defaultPtyHostSocketPath(homeDir, platform = process.platform) {
116
119
  const home = homedir();
117
120
  return fitSocketPath(runtimePath(home, "pty.sock", "pty.pid"), home, "pty");
118
121
  }
119
- function defaultPtyHostPidPath(homeDir = readRoveEnv("HOME_DIR") ?? homedir()) {
122
+ function defaultPtyHostPidPath(homeDir = readRoveHomeDirEnv() ?? homedir()) {
120
123
  const override = readRoveEnv("PTY_PID_PATH");
121
124
  if (override && override.length > 0)
122
125
  return override;
123
126
  return runtimePath(homeDir, "pty.pid", "pty.pid");
124
127
  }
125
- function defaultPtyHostLogPath(homeDir = readRoveEnv("HOME_DIR") ?? homedir()) {
128
+ function defaultPtyHostLogPath(homeDir = readRoveHomeDirEnv() ?? homedir()) {
126
129
  return join(homeDir, ROVE_STATE_DIR_BASENAME, "pty.log");
127
130
  }
128
- function defaultPtyExitsPath(homeDir = readRoveEnv("HOME_DIR") ?? homedir()) {
131
+ function defaultPtyExitsPath(homeDir = readRoveHomeDirEnv() ?? homedir()) {
129
132
  return runtimeDataPath(homeDir, "pty-exits.json");
130
133
  }
131
- function defaultPtyFreezeDir(homeDir = readRoveEnv("HOME_DIR") ?? homedir()) {
134
+ function defaultPtyFreezeDir(homeDir = readRoveHomeDirEnv() ?? homedir()) {
132
135
  return runtimeDataPath(homeDir, "pty-sessions");
133
136
  }
134
137
 
@@ -288,18 +291,19 @@ class ClientWriter {
288
291
  }
289
292
 
290
293
  // ../kobe-daemon/src/daemon/compat-link.ts
291
- import { lstat, mkdir, symlink, unlink } from "node:fs/promises";
294
+ import { lstat, symlink, unlink } from "node:fs/promises";
292
295
  import { dirname } from "node:path";
293
296
  async function linkLegacyRuntimePath(canonical, legacy) {
294
297
  if (canonical === legacy)
295
298
  return false;
296
299
  try {
300
+ if (!await lstat(dirname(legacy)).catch(() => null))
301
+ return false;
297
302
  const existing = await lstat(legacy).catch(() => null);
298
303
  if (existing && !existing.isSymbolicLink())
299
304
  return false;
300
305
  if (existing)
301
306
  await unlink(legacy).catch(() => {});
302
- await mkdir(dirname(legacy), { recursive: true });
303
307
  await symlink(canonical, legacy);
304
308
  return true;
305
309
  } catch {
@@ -340,6 +344,30 @@ function requireString(payload, key) {
340
344
  throw new Error(`${key} is required`);
341
345
  return value;
342
346
  }
347
+
348
+ // ../kobe-daemon/src/daemon/owner-only.ts
349
+ import { chmod, mkdir } from "node:fs/promises";
350
+ import { join as join2 } from "node:path";
351
+ var OWNER_ONLY_DIR_MODE = 448;
352
+ var OWNER_ONLY_FILE_MODE = 384;
353
+ async function tighten(path, mode) {
354
+ try {
355
+ await chmod(path, mode);
356
+ } catch {}
357
+ }
358
+ function tightenDirPermissions(dir) {
359
+ return tighten(dir, OWNER_ONLY_DIR_MODE);
360
+ }
361
+ function tightenFilePermissions(file) {
362
+ return tighten(file, OWNER_ONLY_FILE_MODE);
363
+ }
364
+ async function ensureOwnerOnlyDir(dir) {
365
+ await mkdir(dir, { recursive: true, mode: OWNER_ONLY_DIR_MODE });
366
+ await tightenDirPermissions(dir);
367
+ }
368
+ function ensureOwnerOnlyStateDir(homeDir) {
369
+ return ensureOwnerOnlyDir(join2(homeDir, ROVE_STATE_DIR_BASENAME));
370
+ }
343
371
  // ../kobe-plugin-sdk/dist/contract.js
344
372
  var DAEMON_CHANNELS = [
345
373
  "task.snapshot",
@@ -366,9 +394,10 @@ var DAEMON_CHANNELS = [
366
394
  // ../kobe-daemon/src/daemon/channels.ts
367
395
  var CHANNEL_NAMES = DAEMON_CHANNELS;
368
396
  var CHANNEL_NAME_SET = new Set(CHANNEL_NAMES);
397
+ // ../kobe-daemon/src/daemon/protocol-compat.ts
398
+ var DAEMON_PROTOCOL_VERSION = 4;
369
399
 
370
400
  // ../kobe-daemon/src/daemon/protocol.ts
371
- var DAEMON_PROTOCOL_VERSION = 4;
372
401
  var BLOCKING_RPCS = new Set([
373
402
  "ui.prompt",
374
403
  "task.land",
@@ -388,8 +417,47 @@ function frameToLine(frame) {
388
417
  `;
389
418
  }
390
419
 
420
+ // ../kobe-daemon/src/daemon/pty-data-migration.ts
421
+ import { existsSync as existsSync3, lstatSync, mkdirSync, renameSync as renameSync2, symlinkSync } from "node:fs";
422
+ import { homedir as homedir2 } from "node:os";
423
+ import { join as join3 } from "node:path";
424
+ var PTY_HOST_DATA_ENTRIES = ["pty-exits.json", "pty-sessions"];
425
+ function lstatIfExists(path) {
426
+ try {
427
+ return lstatSync(path);
428
+ } catch {
429
+ return;
430
+ }
431
+ }
432
+ function migrateLegacyPtyHostData(homeDir = readRoveHomeDirEnv() ?? homedir2()) {
433
+ const canonicalDir = join3(homeDir, ROVE_STATE_DIR_BASENAME);
434
+ const legacyDir = join3(homeDir, COMPAT_STATE_DIR_BASENAME);
435
+ if (canonicalDir === legacyDir)
436
+ return [];
437
+ const moved = [];
438
+ for (const name of PTY_HOST_DATA_ENTRIES) {
439
+ const canonical = join3(canonicalDir, name);
440
+ const legacy = join3(legacyDir, name);
441
+ try {
442
+ if (existsSync3(canonical))
443
+ continue;
444
+ const stat = lstatIfExists(legacy);
445
+ if (!stat || stat.isSymbolicLink())
446
+ continue;
447
+ mkdirSync(canonicalDir, { recursive: true });
448
+ renameSync2(legacy, canonical);
449
+ moved.push(name);
450
+ try {
451
+ symlinkSync(canonical, legacy);
452
+ } catch {}
453
+ } catch {}
454
+ }
455
+ return moved;
456
+ }
457
+
391
458
  // ../kobe-daemon/src/daemon/pty-exit-store.ts
392
- import { mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
459
+ import { randomUUID } from "node:crypto";
460
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync as renameSync3, writeFileSync } from "node:fs";
393
461
  import { dirname as dirname2 } from "node:path";
394
462
 
395
463
  // ../kobe-daemon/src/daemon/terminal-rows.ts
@@ -422,14 +490,34 @@ function plainTail(raw) {
422
490
  lines.pop();
423
491
  return lines.slice(-TAIL_LINES);
424
492
  }
425
- function readPtyExitRecords(path = defaultPtyExitsPath()) {
493
+ function engineExitCodeFromTail(tail) {
494
+ for (let i = tail.length - 1;i >= 0; i--) {
495
+ const m = ENGINE_EXIT_BANNER.exec(tail[i] ?? "");
496
+ if (m?.[1])
497
+ return Number.parseInt(m[1], 10);
498
+ }
499
+ return null;
500
+ }
501
+ var ENGINE_EXIT_BANNER = /Engine exited \(code (\d+)\)/;
502
+ function readPtyExitStore(path = defaultPtyExitsPath()) {
503
+ const read = (status, records = {}) => ({
504
+ records,
505
+ status,
506
+ readable: status === "ok" || status === "absent"
507
+ });
508
+ let raw;
509
+ try {
510
+ raw = readFileSync2(path, "utf8");
511
+ } catch (err) {
512
+ return read(err.code === "ENOENT" ? "absent" : "unreadable");
513
+ }
426
514
  try {
427
- const parsed = JSON.parse(readFileSync2(path, "utf8"));
515
+ const parsed = JSON.parse(raw);
428
516
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
429
- return {};
430
- return parsed;
517
+ return read("unparsable");
518
+ return read("ok", parsed);
431
519
  } catch {
432
- return {};
520
+ return read("unparsable");
433
521
  }
434
522
  }
435
523
  function recordPtyExit(info, path = defaultPtyExitsPath()) {
@@ -437,27 +525,33 @@ function recordPtyExit(info, path = defaultPtyExitsPath()) {
437
525
  return;
438
526
  if (info.exit.code === 0 && info.exit.signal === null)
439
527
  return;
528
+ const tail = plainTail(info.tail);
440
529
  writeRecord(info.key, {
441
530
  key: info.key,
442
531
  pid: info.pid,
443
- code: info.exit.code,
532
+ code: info.exit.code ?? engineExitCodeFromTail(tail),
444
533
  signal: info.exit.signal,
445
534
  at: info.exit.at,
446
- tail: plainTail(info.tail),
535
+ tail,
447
536
  layer: "pty"
448
537
  }, path);
449
538
  }
450
539
  function writeRecord(storeKey, record, path) {
451
- const records = readPtyExitRecords(path);
540
+ const store = readPtyExitStore(path);
541
+ if (store.status === "unreadable")
542
+ throw new Error(`pty-exits store is unreadable (${path}) — refusing to overwrite it`);
543
+ const records = store.records;
452
544
  records[storeKey] = record;
453
545
  const newest = Object.entries(records).sort(([, a], [, b]) => a.at < b.at ? 1 : -1).slice(0, MAX_RECORDS);
454
- mkdirSync(dirname2(path), { recursive: true });
455
- writeFileSync(path, JSON.stringify(Object.fromEntries(newest), null, 2), { encoding: "utf8", mode: 384 });
546
+ mkdirSync2(dirname2(path), { recursive: true });
547
+ const tmp = `${path}.tmp-${process.pid}-${randomUUID()}`;
548
+ writeFileSync(tmp, JSON.stringify(Object.fromEntries(newest), null, 2), { encoding: "utf8", mode: 384 });
549
+ renameSync3(tmp, path);
456
550
  }
457
551
 
458
552
  // ../kobe-daemon/src/daemon/pty-freeze-store.ts
459
- import { chmodSync, mkdirSync as mkdirSync2, readFileSync as readFileSync3, readdirSync, renameSync as renameSync2, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
460
- import { join as join2 } from "node:path";
553
+ import { chmodSync, mkdirSync as mkdirSync3, readFileSync as readFileSync3, readdirSync, renameSync as renameSync4, rmSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "node:fs";
554
+ import { join as join4 } from "node:path";
461
555
  import { StringDecoder } from "node:string_decoder";
462
556
 
463
557
  // ../kobe-daemon/src/daemon/terminal-colors.ts
@@ -567,7 +661,7 @@ function thawSession(record, cap) {
567
661
  };
568
662
  }
569
663
  function recordFile(dir, key) {
570
- return join2(dir, `${encodeURIComponent(key)}.json`);
664
+ return join4(dir, `${encodeURIComponent(key)}.json`);
571
665
  }
572
666
  function parseRecord(raw) {
573
667
  try {
@@ -588,43 +682,64 @@ function parseRecord(raw) {
588
682
  }
589
683
  }
590
684
  var FREEZE_TTL_MS = 14 * 24 * 60 * 60 * 1000;
591
- var FREEZE_MAX_RECORDS = 64;
685
+ var FREEZE_RESTORE_MAX_BYTES = 64 * 1024 * 1024;
592
686
  function updatedAtMs(record) {
593
687
  const t = Date.parse(record.updatedAt);
594
688
  return Number.isFinite(t) ? t : 0;
595
689
  }
596
- function loadFrozenSessions(dir = defaultPtyFreezeDir(), now = Date.now()) {
690
+ function loadFrozenSessions(dir = defaultPtyFreezeDir(), now = Date.now(), report) {
597
691
  let names;
598
692
  try {
599
693
  names = readdirSync(dir);
600
694
  } catch {
601
695
  return [];
602
696
  }
603
- const kept = [];
604
- const stale = [];
697
+ const entries = [];
605
698
  for (const name of names) {
606
699
  if (!name.endsWith(".json"))
607
700
  continue;
701
+ try {
702
+ const st = statSync2(join4(dir, name));
703
+ entries.push({ name, size: st.size, mtimeMs: st.mtimeMs });
704
+ } catch {}
705
+ }
706
+ entries.sort((a, b) => b.mtimeMs - a.mtimeMs);
707
+ const kept = [];
708
+ const stale = [];
709
+ let bytesRead = 0;
710
+ let deferred = 0;
711
+ let unreadable = 0;
712
+ for (const entry of entries) {
713
+ if (now - entry.mtimeMs > FREEZE_TTL_MS) {
714
+ stale.push(entry.name);
715
+ continue;
716
+ }
717
+ if (bytesRead + entry.size > FREEZE_RESTORE_MAX_BYTES && kept.length > 0) {
718
+ deferred++;
719
+ continue;
720
+ }
608
721
  let record = null;
609
722
  try {
610
- record = parseRecord(readFileSync3(join2(dir, name), "utf8"));
723
+ record = parseRecord(readFileSync3(join4(dir, entry.name), "utf8"));
611
724
  } catch {}
612
- if (!record)
725
+ bytesRead += entry.size;
726
+ if (!record) {
727
+ unreadable++;
613
728
  continue;
729
+ }
614
730
  if (now - updatedAtMs(record) > FREEZE_TTL_MS)
615
- stale.push(name);
731
+ stale.push(entry.name);
616
732
  else
617
- kept.push({ name, record });
733
+ kept.push(record);
618
734
  }
619
- kept.sort((a, b) => updatedAtMs(b.record) - updatedAtMs(a.record));
620
- for (const over of kept.splice(FREEZE_MAX_RECORDS))
621
- stale.push(over.name);
622
735
  for (const name of stale) {
623
736
  try {
624
- rmSync(join2(dir, name), { force: true });
737
+ rmSync(join4(dir, name), { force: true });
625
738
  } catch {}
626
739
  }
627
- return kept.map((entry) => entry.record);
740
+ kept.sort((a, b) => updatedAtMs(b) - updatedAtMs(a));
741
+ report?.({ restored: kept.length, bytesRead, deferred, expired: stale.length, unreadable });
742
+ return kept;
628
743
  }
629
744
  var DIR_MODE = 448;
630
745
  var FILE_MODE = 384;
@@ -642,7 +757,7 @@ function tightenExistingPermissions(dir) {
642
757
  if (!name.endsWith(".json"))
643
758
  continue;
644
759
  try {
645
- chmodSync(join2(dir, name), FILE_MODE);
760
+ chmodSync(join4(dir, name), FILE_MODE);
646
761
  } catch {}
647
762
  }
648
763
  }
@@ -651,11 +766,11 @@ function fileFreezeSink(dir = defaultPtyFreezeDir()) {
651
766
  return {
652
767
  save(record) {
653
768
  try {
654
- mkdirSync2(dir, { recursive: true, mode: DIR_MODE });
769
+ mkdirSync3(dir, { recursive: true, mode: DIR_MODE });
655
770
  const target = recordFile(dir, record.key);
656
771
  const staging = `${target}.${process.pid}.tmp`;
657
772
  writeFileSync2(staging, JSON.stringify(record), { encoding: "utf8", mode: FILE_MODE });
658
- renameSync2(staging, target);
773
+ renameSync4(staging, target);
659
774
  } catch {}
660
775
  },
661
776
  drop(key) {
@@ -672,7 +787,7 @@ function clearFrozenSessions(dir = defaultPtyFreezeDir()) {
672
787
  }
673
788
 
674
789
  // ../kobe-daemon/src/daemon/platform-shell.js
675
- import { existsSync as existsSync3 } from "node:fs";
790
+ import { existsSync as existsSync4 } from "node:fs";
676
791
  function windowsBashCandidates(env) {
677
792
  const roots = [env.ProgramFiles, env["ProgramFiles(x86)"], env.LOCALAPPDATA && `${env.LOCALAPPDATA}\\Programs`];
678
793
  return roots.filter(Boolean).map((root) => `${root}\\Git\\bin\\bash.exe`);
@@ -686,7 +801,7 @@ function resolveLoginShell(options = {}) {
686
801
  const fallback = options.fallback ?? "/bin/bash";
687
802
  const platform = options.platform ?? process.platform;
688
803
  const env = options.env ?? process.env;
689
- const exists = options.exists ?? existsSync3;
804
+ const exists = options.exists ?? existsSync4;
690
805
  const cacheable = platform === process.platform && env === process.env && options.exists === undefined;
691
806
  if (cacheable) {
692
807
  const hit = cache.get(fallback);
@@ -1323,6 +1438,20 @@ class PtyHost {
1323
1438
  }
1324
1439
  }
1325
1440
 
1441
+ // ../kobe-daemon/src/daemon/socket-guard.ts
1442
+ async function listenOnUnixSocket(server, socketPath) {
1443
+ await new Promise((resolve, reject) => {
1444
+ const evented = server;
1445
+ evented.once("error", reject);
1446
+ server.listen(socketPath, () => {
1447
+ evented.removeListener("error", reject);
1448
+ resolve();
1449
+ });
1450
+ });
1451
+ if (!isWindowsPipePath(socketPath))
1452
+ await tightenFilePermissions(socketPath);
1453
+ }
1454
+
1326
1455
  // ../kobe-daemon/src/daemon/pty-server.ts
1327
1456
  var DEFAULT_IDLE_EXIT_MS = 60000;
1328
1457
  var DEFAULT_ORPHAN_CHECK_MS = 30000;
@@ -1341,6 +1470,7 @@ function resolveIdleExitMs() {
1341
1470
  return Number.isFinite(n) && n >= 0 ? n : DEFAULT_IDLE_EXIT_MS;
1342
1471
  }
1343
1472
  async function startPtyHostServer(options = {}) {
1473
+ migrateLegacyPtyHostData();
1344
1474
  const socketPath = options.socketPath ?? defaultPtyHostSocketPath();
1345
1475
  const pidPath = options.pidPath ?? defaultPtyHostPidPath();
1346
1476
  const freezeDir = options.freezeDir ?? defaultPtyFreezeDir();
@@ -1411,8 +1541,19 @@ async function startPtyHostServer(options = {}) {
1411
1541
  driver: options.driver,
1412
1542
  log
1413
1543
  });
1414
- ptys.restoreFrozen(loadFrozenSessions(freezeDir));
1544
+ ptys.restoreFrozen(loadFrozenSessions(freezeDir, Date.now(), (s) => {
1545
+ const mib = (bytes) => `${Math.round(bytes / (1024 * 1024))}MB`;
1546
+ const notes = [`restored ${s.restored} (${mib(s.bytesRead)})`];
1547
+ if (s.deferred > 0)
1548
+ notes.push(`${s.deferred} left unread past the ${mib(FREEZE_RESTORE_MAX_BYTES)} budget`);
1549
+ if (s.expired > 0)
1550
+ notes.push(`${s.expired} deleted past the ${FREEZE_TTL_MS / 86400000}-day TTL`);
1551
+ if (s.unreadable > 0)
1552
+ notes.push(`${s.unreadable} unreadable`);
1553
+ log("freeze", notes.join(", "));
1554
+ }));
1415
1555
  const pipeSocket = isWindowsPipePath(socketPath);
1556
+ await ensureOwnerOnlyStateDir(resolveDaemonHomeDir());
1416
1557
  if (!pipeSocket)
1417
1558
  await mkdir2(dirname3(socketPath), { recursive: true });
1418
1559
  await mkdir2(dirname3(pidPath), { recursive: true });
@@ -1560,10 +1701,7 @@ async function startPtyHostServer(options = {}) {
1560
1701
  `);
1561
1702
  }
1562
1703
  }
1563
- await new Promise((resolve, reject) => {
1564
- server.once("error", reject);
1565
- server.listen(socketPath, () => resolve());
1566
- });
1704
+ await listenOnUnixSocket(server, socketPath);
1567
1705
  await writeFile(pidPath, `${process.pid}
1568
1706
  `, "utf8");
1569
1707
  if (!pipeSocket) {