@sma1lboy/rove 0.9.238 → 0.9.240

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 (237) hide show
  1. package/README.md +2 -2
  2. package/dist/cli/chunk-0mj358p5.js +3 -0
  3. package/dist/cli/{chunk-nntg9d85.js → chunk-0y4qgnby.js} +1 -1
  4. package/dist/cli/chunk-1brfxbvg.js +4 -0
  5. package/dist/cli/chunk-21fm5tks.js +2 -0
  6. package/dist/cli/{chunk-bkbmqec2.js → chunk-28zg6p31.js} +1 -1
  7. package/dist/cli/chunk-2andp9gs.js +2 -0
  8. package/dist/cli/{chunk-r2pztgm6.js → chunk-2bgmby1d.js} +1 -1
  9. package/dist/cli/chunk-2vgc5cyj.js +2 -0
  10. package/dist/cli/chunk-2x9yc5nx.js +2 -0
  11. package/dist/cli/chunk-30kjq5m7.js +2 -0
  12. package/dist/cli/{chunk-b503z32q.js → chunk-31y92jm8.js} +3 -3
  13. package/dist/cli/{chunk-mkksdcsw.js → chunk-3342qzve.js} +1 -1
  14. package/dist/cli/{chunk-0wgheepg.js → chunk-34261s0m.js} +3 -3
  15. package/dist/cli/chunk-3aqhytyy.js +3 -0
  16. package/dist/cli/chunk-3c58659p.js +2 -0
  17. package/dist/cli/chunk-3d1hjrq0.js +2 -0
  18. package/dist/cli/{chunk-pzbrj3gd.js → chunk-3vyk7bzb.js} +1 -1
  19. package/dist/cli/chunk-41x1hrqe.js +3 -0
  20. package/dist/cli/chunk-4dv0mgan.js +2 -0
  21. package/dist/cli/{chunk-mhg40es1.js → chunk-4hestsjp.js} +1 -1
  22. package/dist/cli/{chunk-77w83wgh.js → chunk-4tx2kayk.js} +1 -1
  23. package/dist/cli/{chunk-aj7fkrha.js → chunk-4xj52nx1.js} +1 -1
  24. package/dist/cli/{chunk-s1p650mm.js → chunk-53cyns0f.js} +1 -1
  25. package/dist/cli/chunk-576r5nht.js +5 -0
  26. package/dist/cli/{chunk-1bm6sryf.js → chunk-5nsvp4h7.js} +1 -1
  27. package/dist/cli/{chunk-zyd6k6ak.js → chunk-7q43tysm.js} +1 -1
  28. package/dist/cli/chunk-801h8bze.js +4 -0
  29. package/dist/cli/chunk-80ydqr3s.js +3 -0
  30. package/dist/cli/{chunk-cv1kaq8a.js → chunk-85zr7kph.js} +5 -5
  31. package/dist/cli/{chunk-zwgns5ce.js → chunk-8tadwbx9.js} +1 -1
  32. package/dist/cli/chunk-96cjpssp.js +3 -0
  33. package/dist/cli/{chunk-bs7v83x0.js → chunk-9aydqazr.js} +1 -1
  34. package/dist/cli/{chunk-7txq91d9.js → chunk-9jsm6h5y.js} +1 -1
  35. package/dist/cli/{chunk-pz907kve.js → chunk-9w5g7frx.js} +1 -1
  36. package/dist/cli/chunk-a0ave3g6.js +3 -0
  37. package/dist/cli/{chunk-vnpqemb0.js → chunk-a7kq09ke.js} +1 -1
  38. package/dist/cli/chunk-abyk3w2j.js +2 -0
  39. package/dist/cli/chunk-adkva3ep.js +6 -0
  40. package/dist/cli/{chunk-nvqdmx7q.js → chunk-ag3txfg2.js} +1 -1
  41. package/dist/cli/{chunk-2rg6aw59.js → chunk-atmaakwp.js} +1 -1
  42. package/dist/cli/chunk-axsm0gn5.js +3 -0
  43. package/dist/cli/chunk-b0ssvpht.js +3 -0
  44. package/dist/cli/chunk-b39sdtt0.js +3 -0
  45. package/dist/cli/{chunk-grt1e2s8.js → chunk-bb5zqmea.js} +2 -2
  46. package/dist/cli/{chunk-g56km26e.js → chunk-bn73kd6z.js} +1 -1
  47. package/dist/cli/chunk-c2m55b72.js +4 -0
  48. package/dist/cli/{chunk-ch0ehc06.js → chunk-cbc1h345.js} +1 -1
  49. package/dist/cli/{chunk-dnbh8hsm.js → chunk-cbjh43yc.js} +1 -1
  50. package/dist/cli/chunk-cst86kt3.js +2 -0
  51. package/dist/cli/chunk-des00203.js +2 -0
  52. package/dist/cli/chunk-dgs0m8r4.js +3 -0
  53. package/dist/cli/chunk-dkgxz9hf.js +5 -0
  54. package/dist/cli/{chunk-mb1e73jm.js → chunk-ekd274v3.js} +2 -2
  55. package/dist/cli/{chunk-gqphk56d.js → chunk-ekg3h5z3.js} +7 -7
  56. package/dist/cli/chunk-f1nf99hj.js +2 -0
  57. package/dist/cli/chunk-fert44x2.js +2 -0
  58. package/dist/cli/chunk-ff1h2qxd.js +2 -0
  59. package/dist/cli/{chunk-cv6r89jp.js → chunk-ffs26p85.js} +1 -1
  60. package/dist/cli/{chunk-hp32tbes.js → chunk-fg62y5fm.js} +1 -1
  61. package/dist/cli/{chunk-s9a974w9.js → chunk-fvmee0tr.js} +1 -1
  62. package/dist/cli/{chunk-8h0khkap.js → chunk-fwqd5ta2.js} +2 -2
  63. package/dist/cli/{chunk-acw32w13.js → chunk-g0eygcnq.js} +6 -5
  64. package/dist/cli/chunk-g4c4ffqp.js +20 -0
  65. package/dist/cli/{chunk-k7g060qr.js → chunk-g4j41mky.js} +2 -2
  66. package/dist/cli/{chunk-j6kataan.js → chunk-g5c5jytr.js} +1 -1
  67. package/dist/cli/{chunk-dqndd97s.js → chunk-gkdfct7t.js} +2 -2
  68. package/dist/cli/chunk-gvrtrvnz.js +96 -0
  69. package/dist/cli/chunk-h2m7em1e.js +3 -0
  70. package/dist/cli/chunk-h33m6cc6.js +8 -0
  71. package/dist/cli/{chunk-ynbfqbn9.js → chunk-h3fckxn3.js} +1 -1
  72. package/dist/cli/chunk-h7y9v8k1.js +4 -0
  73. package/dist/cli/{chunk-32mv1yy6.js → chunk-h9cg0j3a.js} +1 -1
  74. package/dist/cli/{chunk-h4qv19v8.js → chunk-hbn1t161.js} +1 -1
  75. package/dist/cli/{chunk-3es6spp4.js → chunk-hq9tbzqa.js} +2 -2
  76. package/dist/cli/{chunk-snjte1rd.js → chunk-hr2xky15.js} +1 -1
  77. package/dist/cli/chunk-jxqs4y9j.js +10 -0
  78. package/dist/cli/{chunk-s0snbj93.js → chunk-kb6xw0fw.js} +1 -1
  79. package/dist/cli/{chunk-vyb5a2rh.js → chunk-kg9kmwt9.js} +1 -1
  80. package/dist/cli/chunk-ktzbg4jg.js +2 -0
  81. package/dist/cli/chunk-kz4txj2e.js +8 -0
  82. package/dist/cli/{chunk-bhqracn8.js → chunk-m19q29wk.js} +1 -1
  83. package/dist/cli/chunk-m1ekbqyz.js +2 -0
  84. package/dist/cli/chunk-m4f5h05c.js +10 -0
  85. package/dist/cli/chunk-m5gx53yr.js +3 -0
  86. package/dist/cli/chunk-m6q44xc0.js +2 -0
  87. package/dist/cli/{chunk-hq2n27y1.js → chunk-mw0ysrpd.js} +1 -1
  88. package/dist/cli/{chunk-2cwb64fj.js → chunk-n5dea05t.js} +1 -1
  89. package/dist/cli/chunk-nc46gddg.js +3 -0
  90. package/dist/cli/{chunk-jhbc9nx2.js → chunk-nckmga7j.js} +1 -1
  91. package/dist/cli/chunk-nem2x959.js +2 -0
  92. package/dist/cli/chunk-ng12dprz.js +2 -0
  93. package/dist/cli/chunk-npwzmmv9.js +3 -0
  94. package/dist/cli/chunk-nzh30nty.js +4 -0
  95. package/dist/cli/{chunk-gqrh2gp1.js → chunk-p024scd4.js} +1 -1
  96. package/dist/cli/chunk-p18ehv9y.js +7 -0
  97. package/dist/cli/{chunk-3sg5v7cg.js → chunk-p83wf4zw.js} +2 -2
  98. package/dist/cli/{chunk-s278gjet.js → chunk-pdvyfz3n.js} +1 -1
  99. package/dist/cli/chunk-pe6wnsww.js +2 -0
  100. package/dist/cli/chunk-prqeddy7.js +3 -0
  101. package/dist/cli/chunk-pvm3fc44.js +2 -0
  102. package/dist/cli/{chunk-2yd128k1.js → chunk-q5eee34y.js} +6 -6
  103. package/dist/cli/{chunk-z5fnv7nk.js → chunk-q7zzycy7.js} +25 -23
  104. package/dist/cli/chunk-qvfdqrbf.js +3 -0
  105. package/dist/cli/{chunk-2xsem13q.js → chunk-qzsahxd9.js} +1 -1
  106. package/dist/cli/chunk-rqyaf0dw.js +4 -0
  107. package/dist/cli/chunk-s57vy3k3.js +2 -0
  108. package/dist/cli/{chunk-g2advs8t.js → chunk-sak1tphp.js} +1 -1
  109. package/dist/cli/{chunk-vdcn6h48.js → chunk-snhcvjdb.js} +1 -1
  110. package/dist/cli/{chunk-bav389wj.js → chunk-svm809bx.js} +1 -1
  111. package/dist/cli/chunk-szrfp0jq.js +2 -0
  112. package/dist/cli/chunk-t8v9rpdx.js +2 -0
  113. package/dist/cli/{chunk-j4xfsn9g.js → chunk-tbrkck2f.js} +1 -1
  114. package/dist/cli/chunk-tdhk4dz8.js +4 -0
  115. package/dist/cli/chunk-thsmzarw.js +3 -0
  116. package/dist/cli/{chunk-t3wwsz29.js → chunk-tvk46rfs.js} +1 -1
  117. package/dist/cli/{chunk-4d54zgc7.js → chunk-twyda1p8.js} +2 -2
  118. package/dist/cli/{chunk-s1e0e9hc.js → chunk-vej49hnt.js} +2 -2
  119. package/dist/cli/chunk-w2sp489k.js +3 -0
  120. package/dist/cli/{chunk-0wh2ff77.js → chunk-whbm14qr.js} +2 -2
  121. package/dist/cli/chunk-wp9bygh4.js +2 -0
  122. package/dist/cli/{chunk-ww4fbx8h.js → chunk-wtb8cycx.js} +2 -2
  123. package/dist/cli/chunk-x9s14ppv.js +4 -0
  124. package/dist/cli/{chunk-hk8mc43r.js → chunk-xckpezd9.js} +5 -5
  125. package/dist/cli/{chunk-dq2mh3b3.js → chunk-xv8fhxxm.js} +1 -1
  126. package/dist/cli/{chunk-85pk7c74.js → chunk-xx166cdv.js} +1 -1
  127. package/dist/cli/{chunk-ssfj9td0.js → chunk-y8tysjc2.js} +15 -13
  128. package/dist/cli/{chunk-y3k1accw.js → chunk-yexx8nec.js} +1 -1
  129. package/dist/cli/{chunk-9prawccj.js → chunk-yt15y085.js} +1 -1
  130. package/dist/cli/{chunk-n0mg67wn.js → chunk-ytxk6zqc.js} +1 -1
  131. package/dist/cli/chunk-yvjgwnb9.js +2 -0
  132. package/dist/cli/{chunk-n24dmy2r.js → chunk-yxt1y0n3.js} +9 -9
  133. package/dist/cli/chunk-ze0htjr5.js +2 -0
  134. package/dist/cli/chunk-zeb0sttx.js +2 -0
  135. package/dist/cli/chunk-znjcesdr.js +5 -0
  136. package/dist/cli/chunk-zsfyj36g.js +3 -0
  137. package/dist/cli/chunk-zt49bbg9.js +3 -0
  138. package/dist/cli/chunk-zt7wfnva.js +3 -0
  139. package/dist/cli/{chunk-6rvzzrd7.js → chunk-zx2n95qe.js} +1 -1
  140. package/dist/cli/index.js +12 -10
  141. package/dist/cli/pty-host-node.mjs +880 -169
  142. package/dist/cli/rove-run.js +1 -1
  143. package/dist/cli/rove.js +6 -6
  144. package/dist/skills/rove/SKILL.md +1 -1
  145. package/dist/skills/rove/references/api-flags.md +5 -5
  146. package/dist/web-ui/assets/{harness-CQ-1WI4q.js → harness-BHiciz5A.js} +2 -2
  147. package/dist/web-ui/assets/{index-_ttAn6pY.js → index-BkxJISAI.js} +2 -2
  148. package/dist/web-ui/assets/{index-C4dLQEqf.css → index-nMJ42IGm.css} +1 -1
  149. package/dist/web-ui/index.html +2 -2
  150. package/dist/web-ui/origin-policy.mjs +1 -1
  151. package/dist/web-ui/pty-server.mjs +24 -10
  152. package/dist/web-ui/pty-session-lifecycle.mjs +9 -13
  153. package/package.json +6 -8
  154. package/dist/cli/chunk-05jej26n.js +0 -3
  155. package/dist/cli/chunk-079cyb2e.js +0 -4
  156. package/dist/cli/chunk-1265fmzk.js +0 -3
  157. package/dist/cli/chunk-1da3bqjc.js +0 -4
  158. package/dist/cli/chunk-1tx1s5v6.js +0 -2
  159. package/dist/cli/chunk-507zn8rs.js +0 -3
  160. package/dist/cli/chunk-53afdybj.js +0 -3
  161. package/dist/cli/chunk-544bwsss.js +0 -4
  162. package/dist/cli/chunk-67ev8k22.js +0 -2
  163. package/dist/cli/chunk-6s5mev2n.js +0 -4
  164. package/dist/cli/chunk-6x5vzzpx.js +0 -3
  165. package/dist/cli/chunk-7s1kx9ez.js +0 -2
  166. package/dist/cli/chunk-85mk6j0s.js +0 -3
  167. package/dist/cli/chunk-8hbtca1g.js +0 -3
  168. package/dist/cli/chunk-8pa1ph1s.js +0 -7
  169. package/dist/cli/chunk-9ek7xm94.js +0 -8
  170. package/dist/cli/chunk-9k47xr7m.js +0 -2
  171. package/dist/cli/chunk-9mtxxsb3.js +0 -2
  172. package/dist/cli/chunk-a1k2z44b.js +0 -2
  173. package/dist/cli/chunk-a3yyrabb.js +0 -2
  174. package/dist/cli/chunk-ahnk6zx3.js +0 -2
  175. package/dist/cli/chunk-bc18gat5.js +0 -4
  176. package/dist/cli/chunk-be757xzj.js +0 -3
  177. package/dist/cli/chunk-bw73t1zw.js +0 -5
  178. package/dist/cli/chunk-c2tevj9f.js +0 -2
  179. package/dist/cli/chunk-d1agt94n.js +0 -8
  180. package/dist/cli/chunk-dc31fzrm.js +0 -2
  181. package/dist/cli/chunk-dw853n4w.js +0 -3
  182. package/dist/cli/chunk-eawt4n4g.js +0 -2
  183. package/dist/cli/chunk-egx76ah2.js +0 -5
  184. package/dist/cli/chunk-ex1a2djb.js +0 -2
  185. package/dist/cli/chunk-f5e3dcxe.js +0 -2
  186. package/dist/cli/chunk-fwb72vzc.js +0 -2
  187. package/dist/cli/chunk-fxs82dsx.js +0 -4
  188. package/dist/cli/chunk-g3r1dwy9.js +0 -3
  189. package/dist/cli/chunk-ge30p4s5.js +0 -3
  190. package/dist/cli/chunk-gmgsxdrf.js +0 -10
  191. package/dist/cli/chunk-he76emdm.js +0 -2
  192. package/dist/cli/chunk-hgx20x94.js +0 -2
  193. package/dist/cli/chunk-hnfp317x.js +0 -2
  194. package/dist/cli/chunk-hq4b3fca.js +0 -3
  195. package/dist/cli/chunk-janvvpv5.js +0 -8
  196. package/dist/cli/chunk-jvxh93nz.js +0 -3
  197. package/dist/cli/chunk-k6nxyxyh.js +0 -3
  198. package/dist/cli/chunk-k8tspshr.js +0 -2
  199. package/dist/cli/chunk-md8k7c5w.js +0 -2
  200. package/dist/cli/chunk-mppda1wq.js +0 -3
  201. package/dist/cli/chunk-ppwpjm39.js +0 -3
  202. package/dist/cli/chunk-pvw2y93q.js +0 -25
  203. package/dist/cli/chunk-pw6yfj10.js +0 -3
  204. package/dist/cli/chunk-q4nehkj9.js +0 -2
  205. package/dist/cli/chunk-qagek3wg.js +0 -5
  206. package/dist/cli/chunk-r8xqvg26.js +0 -2
  207. package/dist/cli/chunk-rz8zs7j8.js +0 -3
  208. package/dist/cli/chunk-sb0ns0c7.js +0 -3
  209. package/dist/cli/chunk-tcj36cqg.js +0 -2
  210. package/dist/cli/chunk-vejgnmcp.js +0 -2
  211. package/dist/cli/chunk-vzqrj32e.js +0 -2
  212. package/dist/cli/chunk-wpsbyq4s.js +0 -2
  213. package/dist/cli/chunk-wtqxv2rv.js +0 -39
  214. package/dist/cli/chunk-y05d79az.js +0 -2
  215. package/dist/cli/chunk-y4048296.js +0 -4
  216. package/dist/cli/chunk-y8fqdnh3.js +0 -3
  217. package/dist/cli/chunk-ygd572ee.js +0 -4
  218. package/dist/cli/chunk-yj5pjxx2.js +0 -2
  219. package/dist/cli/chunk-zja9chrd.js +0 -4
  220. package/dist/cli/chunk-zwej00t0.js +0 -5
  221. package/dist/cli/kobe-run.js +0 -3
  222. package/dist/cli/kobe.js +0 -7
  223. package/dist/completions/kobe.bash +0 -25
  224. package/dist/completions/kobe.fish +0 -27
  225. package/dist/completions/kobe.zsh +0 -35
  226. /package/dist/cli/{chunk-hqbmzz4t.js → chunk-0422g8ac.js} +0 -0
  227. /package/dist/cli/{chunk-khj0apc7.js → chunk-2tfnscxk.js} +0 -0
  228. /package/dist/cli/{chunk-7q67dz4c.js → chunk-4250pw6r.js} +0 -0
  229. /package/dist/cli/{chunk-xsrcwcjc.js → chunk-5mhvjytn.js} +0 -0
  230. /package/dist/cli/{chunk-ef39ym8f.js → chunk-7bgbansg.js} +0 -0
  231. /package/dist/cli/{chunk-nh4wvyz3.js → chunk-atn06ncf.js} +0 -0
  232. /package/dist/cli/{chunk-h4n8ffqy.js → chunk-dmzg9zdc.js} +0 -0
  233. /package/dist/cli/{chunk-w25b7xxz.js → chunk-etfh8f4z.js} +0 -0
  234. /package/dist/cli/{chunk-hape5vwj.js → chunk-hpay0ax8.js} +0 -0
  235. /package/dist/cli/{chunk-f10n6063.js → chunk-pn5ygqn6.js} +0 -0
  236. /package/dist/cli/{chunk-rcyasw34.js → chunk-rxqdw6s5.js} +0 -0
  237. /package/dist/cli/{chunk-exfyqsdd.js → chunk-vjvkkrzc.js} +0 -0
@@ -1,7 +1,20 @@
1
1
  import { createRequire } from "node:module";
2
2
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
3
 
4
- // ../kobe-daemon/src/daemon/log-rotate.ts
4
+ // ../rove-daemon/src/daemon/pty-host-node-entry.ts
5
+ import { join as join9 } from "node:path";
6
+
7
+ // ../rove-daemon/src/compat-env.ts
8
+ var ROVE_ENV_PREFIX = "ROVE_";
9
+ var ROVE_STATE_DIR_BASENAME = ".rove";
10
+ function readRoveEnv(suffix, env = process.env) {
11
+ return env[`${ROVE_ENV_PREFIX}${suffix}`]?.trim() || undefined;
12
+ }
13
+ function readRoveHomeDirEnv(env = process.env) {
14
+ return readRoveEnv("HOME_DIR", env);
15
+ }
16
+
17
+ // ../rove-daemon/src/daemon/log-rotate.ts
5
18
  import { existsSync, renameSync, statSync } from "node:fs";
6
19
  var DEFAULT_LOG_ROTATE_CAP_BYTES = 10 * 1024 * 1024;
7
20
  function shouldRotateLog(sizeBytes, capBytes = DEFAULT_LOG_ROTATE_CAP_BYTES) {
@@ -18,32 +31,35 @@ function rotateLogIfNeeded(path, capBytes = DEFAULT_LOG_ROTATE_CAP_BYTES) {
18
31
  } catch {}
19
32
  }
20
33
 
21
- // ../kobe-daemon/src/daemon/paths.ts
34
+ // ../rove-daemon/src/daemon/paths.ts
22
35
  import { createHash } from "node:crypto";
23
36
  import { existsSync as existsSync2, readFileSync } from "node:fs";
24
37
  import { homedir, tmpdir } from "node:os";
25
- import { join } from "node:path";
38
+ import { join as join2 } from "node:path";
26
39
 
27
- // ../kobe-daemon/src/compat-env.ts
28
- var ROVE_ENV_PREFIX = "ROVE_";
29
- var LEGACY_KOBE_ENV_PREFIX = "KOBE_";
30
- var ROVE_STATE_DIR_BASENAME = ".rove";
31
- var COMPAT_STATE_DIR_BASENAME = ".kobe";
32
- function readRoveEnv(suffix, env = process.env) {
33
- for (const key of [`${ROVE_ENV_PREFIX}${suffix}`, `${LEGACY_KOBE_ENV_PREFIX}${suffix}`]) {
34
- const value = env[key]?.trim();
35
- if (value)
36
- return value;
37
- }
38
- return;
40
+ // ../rove-daemon/src/daemon/pre-rename-runtime.ts
41
+ import { basename, dirname, join } from "node:path";
42
+ var LEGACY_PRE_RENAME_RUNTIME_NAME = "kobe";
43
+ function preRenameStateDir(home) {
44
+ return join(home, `.${LEGACY_PRE_RENAME_RUNTIME_NAME}`);
39
45
  }
40
- function readRoveHomeDirEnv(env = process.env) {
41
- return readRoveEnv("HOME_DIR", env);
46
+ function preRenameRuntimePaths(canonical) {
47
+ const name = basename(canonical);
48
+ if (canonical.startsWith("\\\\.\\pipe\\rove-") && canonical.endsWith("-pty")) {
49
+ return [canonical.replace("\\pipe\\rove-", `\\pipe\\${LEGACY_PRE_RENAME_RUNTIME_NAME}-`)];
50
+ }
51
+ if (/^rove(?:-[a-f0-9]{8})?-(?:pty|daemon)(?:-\d+)?\.sock$/.test(name) || name === "rove.sock") {
52
+ return [join(dirname(canonical), name.replace(/^rove/, LEGACY_PRE_RENAME_RUNTIME_NAME))];
53
+ }
54
+ if (["pty.sock", "pty.pid", "daemon.sock", "daemon.pid"].includes(name) && basename(dirname(canonical)) === ".rove") {
55
+ return [join(preRenameStateDir(dirname(dirname(canonical))), name)];
56
+ }
57
+ return [];
42
58
  }
43
59
 
44
- // ../kobe-daemon/src/daemon/paths.ts
60
+ // ../rove-daemon/src/daemon/paths.ts
45
61
  function stateDirs(homeDir) {
46
- return { canonical: join(homeDir, ROVE_STATE_DIR_BASENAME), legacy: join(homeDir, COMPAT_STATE_DIR_BASENAME) };
62
+ return { canonical: join2(homeDir, ROVE_STATE_DIR_BASENAME), legacy: preRenameStateDir(homeDir) };
47
63
  }
48
64
  function pidIsLive(pidPath) {
49
65
  try {
@@ -58,25 +74,16 @@ function pidIsLive(pidPath) {
58
74
  }
59
75
  function runtimePath(homeDir, name, pidName) {
60
76
  const { canonical, legacy } = stateDirs(homeDir);
61
- const canonicalPath = join(canonical, name);
62
- if (existsSync2(canonicalPath))
77
+ const canonicalPath = join2(canonical, name);
78
+ if (pidIsLive(join2(canonical, pidName)))
63
79
  return canonicalPath;
64
- const legacyPath = join(legacy, name);
65
- if (existsSync2(legacyPath) && pidIsLive(join(legacy, pidName)))
80
+ const legacyPath = join2(legacy, name);
81
+ if (existsSync2(legacyPath) && pidIsLive(join2(legacy, pidName)))
66
82
  return legacyPath;
67
83
  return canonicalPath;
68
84
  }
69
- function legacyRuntimePath(homeDir, name) {
70
- return join(homeDir, COMPAT_STATE_DIR_BASENAME, name);
71
- }
72
- function legacyPtyHostSocketPath(homeDir) {
73
- return legacyRuntimePath(homeDir, "pty.sock");
74
- }
75
- function legacyPtyHostPidPath(homeDir) {
76
- return legacyRuntimePath(homeDir, "pty.pid");
77
- }
78
85
  function runtimeDataPath(homeDir, name) {
79
- return join(stateDirs(homeDir).canonical, name);
86
+ return join2(stateDirs(homeDir).canonical, name);
80
87
  }
81
88
  var SOCKET_PATH_SAFETY_LIMIT = 100;
82
89
  function shortHomeTag(homeDir) {
@@ -87,20 +94,38 @@ function fitSocketPath(naturalPath, homeDir, role, pidTag) {
87
94
  return naturalPath;
88
95
  const tag = shortHomeTag(homeDir);
89
96
  const suffix = pidTag === undefined ? "" : `-${pidTag}`;
90
- const fallback = join(tmpdir(), `kobe-${tag}-${role}${suffix}.sock`);
97
+ const fallback = join2(tmpdir(), `rove-${tag}-${role}${suffix}.sock`);
91
98
  if (Buffer.byteLength(fallback, "utf8") <= SOCKET_PATH_SAFETY_LIMIT)
92
99
  return fallback;
93
100
  throw new Error(`daemon socket path exceeds ${SOCKET_PATH_SAFETY_LIMIT} bytes even after fallback: ${fallback}`);
94
101
  }
102
+ function defaultDaemonSocketPath(homeDir) {
103
+ const override = readRoveEnv("DAEMON_SOCKET_PATH");
104
+ if (override)
105
+ return override;
106
+ const explicit = homeDir ?? readRoveHomeDirEnv();
107
+ if (explicit && explicit.length > 0) {
108
+ return fitSocketPath(join2(explicit, ROVE_STATE_DIR_BASENAME, "daemon.sock"), explicit, "daemon");
109
+ }
110
+ const runtimeDir = process.env.XDG_RUNTIME_DIR;
111
+ if (runtimeDir && runtimeDir.length > 0) {
112
+ return fitSocketPath(join2(runtimeDir, "rove.sock"), runtimeDir, "daemon");
113
+ }
114
+ const home = homedir();
115
+ return fitSocketPath(join2(home, ROVE_STATE_DIR_BASENAME, "daemon.sock"), home, "daemon");
116
+ }
95
117
  function resolveDaemonHomeDir(homeDir) {
96
118
  const explicit = homeDir ?? readRoveHomeDirEnv();
97
119
  return explicit && explicit.length > 0 ? explicit : homedir();
98
120
  }
121
+ function defaultClientLogPath(homeDir = readRoveHomeDirEnv() ?? homedir()) {
122
+ return join2(homeDir, ROVE_STATE_DIR_BASENAME, "client.log");
123
+ }
99
124
  function isWindowsPipePath(path) {
100
125
  return path.startsWith("\\\\.\\pipe\\") || path.startsWith("//./pipe/");
101
126
  }
102
127
  function windowsPipePath(homeDir, role) {
103
- return `\\\\.\\pipe\\kobe-${shortHomeTag(homeDir)}-${role}`;
128
+ return `\\\\.\\pipe\\rove-${shortHomeTag(homeDir)}-${role}`;
104
129
  }
105
130
  function defaultPtyHostSocketPath(homeDir, platform = process.platform) {
106
131
  const override = readRoveEnv("PTY_SOCKET_PATH");
@@ -110,14 +135,14 @@ function defaultPtyHostSocketPath(homeDir, platform = process.platform) {
110
135
  if (platform === "win32")
111
136
  return windowsPipePath(explicit || homedir(), "pty");
112
137
  if (explicit && explicit.length > 0) {
113
- return fitSocketPath(runtimePath(explicit, "pty.sock", "pty.pid"), explicit, "pty");
138
+ return fitSocketPath(join2(explicit, ROVE_STATE_DIR_BASENAME, "pty.sock"), explicit, "pty");
114
139
  }
115
140
  const runtimeDir = process.env.XDG_RUNTIME_DIR;
116
141
  if (runtimeDir && runtimeDir.length > 0) {
117
- return fitSocketPath(join(runtimeDir, "kobe-pty.sock"), runtimeDir, "pty");
142
+ return fitSocketPath(join2(runtimeDir, "rove-pty.sock"), runtimeDir, "pty");
118
143
  }
119
144
  const home = homedir();
120
- return fitSocketPath(runtimePath(home, "pty.sock", "pty.pid"), home, "pty");
145
+ return fitSocketPath(join2(home, ROVE_STATE_DIR_BASENAME, "pty.sock"), home, "pty");
121
146
  }
122
147
  function defaultPtyHostPidPath(homeDir = readRoveHomeDirEnv() ?? homedir()) {
123
148
  const override = readRoveEnv("PTY_PID_PATH");
@@ -126,7 +151,7 @@ function defaultPtyHostPidPath(homeDir = readRoveHomeDirEnv() ?? homedir()) {
126
151
  return runtimePath(homeDir, "pty.pid", "pty.pid");
127
152
  }
128
153
  function defaultPtyHostLogPath(homeDir = readRoveHomeDirEnv() ?? homedir()) {
129
- return join(homeDir, ROVE_STATE_DIR_BASENAME, "pty.log");
154
+ return join2(homeDir, ROVE_STATE_DIR_BASENAME, "pty.log");
130
155
  }
131
156
  function defaultPtyExitsPath(homeDir = readRoveHomeDirEnv() ?? homedir()) {
132
157
  return runtimeDataPath(homeDir, "pty-exits.json");
@@ -135,10 +160,10 @@ function defaultPtyFreezeDir(homeDir = readRoveHomeDirEnv() ?? homedir()) {
135
160
  return runtimeDataPath(homeDir, "pty-sessions");
136
161
  }
137
162
 
138
- // ../kobe-daemon/src/daemon/process-tree.ts
163
+ // ../rove-daemon/src/daemon/process-tree.ts
139
164
  import { execFile } from "node:child_process";
140
165
  import { existsSync as existsSync3 } from "node:fs";
141
- import { dirname, join as join2 } from "node:path";
166
+ import { dirname as dirname2, join as join3 } from "node:path";
142
167
  var TASKKILL_TIMEOUT_MS = 5000;
143
168
  var MSYS_PS_TIMEOUT_MS = 5000;
144
169
  function parseMsysPs(output) {
@@ -183,8 +208,8 @@ function msysDescendantWinpids(rows, rootWinpid) {
183
208
  function msysPsFor(shellFile) {
184
209
  if (!shellFile || !/bash(\.exe)?$/i.test(shellFile))
185
210
  return null;
186
- const dir = dirname(shellFile);
187
- for (const candidate of [join2(dir, "ps.exe"), join2(dir, "..", "usr", "bin", "ps.exe")]) {
211
+ const dir = dirname2(shellFile);
212
+ for (const candidate of [join3(dir, "ps.exe"), join3(dir, "..", "usr", "bin", "ps.exe")]) {
188
213
  if (existsSync3(candidate))
189
214
  return candidate;
190
215
  }
@@ -210,7 +235,174 @@ async function taskkillProcessTree(pid, shellFile) {
210
235
  });
211
236
  }
212
237
 
213
- // ../kobe-daemon/src/daemon/pty-driver.ts
238
+ // ../rove-daemon/src/daemon/win-pty-job.ts
239
+ import { execFile as execFile2 } from "node:child_process";
240
+ import { createHash as createHash2, randomUUID } from "node:crypto";
241
+ import { existsSync as existsSync4, mkdirSync, renameSync as renameSync2, rmSync, writeFileSync } from "node:fs";
242
+ import { join as join4, resolve } from "node:path";
243
+ var PTY_JOB_ENV = "ROVE_PTY_JOB";
244
+ var PTY_JOB_OWNER_ENV = "ROVE_PTY_JOB_OWNER";
245
+ var PTY_JOB_LAUNCHER_CS = `
246
+ using System;
247
+ using System.Runtime.InteropServices;
248
+ using System.Text;
249
+ static class RovePtyJob {
250
+ [StructLayout(LayoutKind.Sequential)] struct BASIC { public long PerProcessUserTimeLimit, PerJobUserTimeLimit; public uint LimitFlags; public UIntPtr MinimumWorkingSetSize, MaximumWorkingSetSize; public uint ActiveProcessLimit; public UIntPtr Affinity; public uint PriorityClass, SchedulingClass; }
251
+ [StructLayout(LayoutKind.Sequential)] struct IO { public ulong R, W, O, RB, WB, OB; }
252
+ [StructLayout(LayoutKind.Sequential)] struct EXT { public BASIC Basic; public IO Io; public UIntPtr ProcessMemoryLimit, JobMemoryLimit, PeakProcessMemoryUsed, PeakJobMemoryUsed; }
253
+ [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] struct SI { public int cb; public string lpReserved, lpDesktop, lpTitle; public int dwX, dwY, dwXSize, dwYSize, dwXCountChars, dwYCountChars, dwFillAttribute, dwFlags; public short wShowWindow, cbReserved2; public IntPtr lpReserved2, hStdInput, hStdOutput, hStdError; }
254
+ [StructLayout(LayoutKind.Sequential)] struct PI { public IntPtr hProcess, hThread; public int dwProcessId, dwThreadId; }
255
+ delegate bool CtrlHandler(uint type);
256
+ [DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)] static extern IntPtr CreateJobObject(IntPtr sa, string name);
257
+ [DllImport("kernel32.dll", SetLastError=true)] static extern bool SetInformationJobObject(IntPtr job, int cls, ref EXT info, int len);
258
+ [DllImport("kernel32.dll", SetLastError=true)] static extern bool AssignProcessToJobObject(IntPtr job, IntPtr proc);
259
+ [DllImport("kernel32.dll")] static extern IntPtr GetCurrentProcess();
260
+ [DllImport("kernel32.dll", CharSet=CharSet.Unicode)] static extern IntPtr GetCommandLine();
261
+ [DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)] static extern bool CreateProcess(string app, StringBuilder cmd, IntPtr pa, IntPtr ta, bool inherit, uint flags, IntPtr env, string cwd, ref SI si, out PI pi);
262
+ [DllImport("kernel32.dll")] static extern uint WaitForSingleObject(IntPtr h, uint ms);
263
+ [DllImport("kernel32.dll")] static extern bool GetExitCodeProcess(IntPtr h, out uint code);
264
+ [DllImport("kernel32.dll")] static extern bool SetConsoleCtrlHandler(CtrlHandler h, bool add);
265
+ [DllImport("kernel32.dll")] static extern bool SetProcessWorkingSetSize(IntPtr p, IntPtr min, IntPtr max);
266
+ static CtrlHandler keep;
267
+ static int SkipToken(string s, int i) {
268
+ if (i < s.Length && s[i] == '"') { int end = s.IndexOf('"', i + 1); return end < 0 ? s.Length : end + 1; }
269
+ while (i < s.Length && s[i] != ' ' && s[i] != '\\t') i++;
270
+ return i;
271
+ }
272
+ static int SkipSpace(string s, int i) { while (i < s.Length && (s[i] == ' ' || s[i] == '\\t')) i++; return i; }
273
+ static int Main() {
274
+ string raw = Marshal.PtrToStringUni(GetCommandLine());
275
+ string rest = raw.Substring(SkipSpace(raw, SkipToken(raw, 0)));
276
+ if (rest.Length == 0) { Console.Error.WriteLine("rove-pty-job: usage: <command line> (job name in ${PTY_JOB_ENV})"); return 2; }
277
+ string name = Environment.GetEnvironmentVariable("${PTY_JOB_ENV}");
278
+ IntPtr job = String.IsNullOrEmpty(name) ? IntPtr.Zero : CreateJobObject(IntPtr.Zero, name);
279
+ if (job != IntPtr.Zero) {
280
+ EXT info = new EXT();
281
+ info.Basic.LimitFlags = 0x2000; // KILL_ON_JOB_CLOSE; no BREAKAWAY_OK (see module doc)
282
+ SetInformationJobObject(job, 9, ref info, Marshal.SizeOf(typeof(EXT)));
283
+ AssignProcessToJobObject(job, GetCurrentProcess());
284
+ }
285
+ keep = delegate (uint type) { return type <= 1; }; // CTRL_C, CTRL_BREAK: the shell's to handle
286
+ SetConsoleCtrlHandler(keep, true);
287
+ SI si = new SI(); si.cb = Marshal.SizeOf(typeof(SI));
288
+ PI pi;
289
+ if (!CreateProcess(null, new StringBuilder(rest), IntPtr.Zero, IntPtr.Zero, false, 0, IntPtr.Zero, null, ref si, out pi)) {
290
+ Console.Error.WriteLine("rove-pty-job: could not start " + rest + " (error " + Marshal.GetLastWin32Error() + ")");
291
+ return 1;
292
+ }
293
+ // From here it only waits: hand its pages back (14 MB resident → 2 MB per tab, measured).
294
+ SetProcessWorkingSetSize(GetCurrentProcess(), (IntPtr)(-1), (IntPtr)(-1));
295
+ WaitForSingleObject(pi.hProcess, 0xFFFFFFFF);
296
+ uint code;
297
+ GetExitCodeProcess(pi.hProcess, out code);
298
+ GC.KeepAlive(keep);
299
+ return (int)code;
300
+ }
301
+ }
302
+ `;
303
+ var COMPILE_TIMEOUT_MS = 30000;
304
+ var SELF_TEST_TIMEOUT_MS = 1e4;
305
+ var SELF_TEST_EXIT_CODE = 7;
306
+ function run(file, args, timeout, env) {
307
+ return new Promise((done) => {
308
+ try {
309
+ const child = execFile2(file, [...args], { windowsHide: true, timeout, env }, (err, stdout, stderr) => {
310
+ const output = `${stdout}${stderr}`.trim() || err?.message || "";
311
+ done({ code: child.exitCode, output });
312
+ });
313
+ } catch (err) {
314
+ done({ code: null, output: err.message });
315
+ }
316
+ });
317
+ }
318
+ function findCsc(env = process.env) {
319
+ const root = env.SystemRoot || env.windir || "C:\\Windows";
320
+ for (const framework of ["Framework64", "Framework"]) {
321
+ const csc = join4(root, "Microsoft.NET", framework, "v4.0.30319", "csc.exe");
322
+ if (existsSync4(csc))
323
+ return csc;
324
+ }
325
+ return null;
326
+ }
327
+ function launcherFileName(source = PTY_JOB_LAUNCHER_CS) {
328
+ return `rove-pty-job-${createHash2("sha256").update(source).digest("hex").slice(0, 12)}.exe`;
329
+ }
330
+ function newPtyJobName() {
331
+ return `Local\\rove-pty-${randomUUID()}`;
332
+ }
333
+ async function compileLauncher(binDir, exe) {
334
+ const csc = findCsc();
335
+ if (!csc)
336
+ return "no .NET Framework csc.exe";
337
+ mkdirSync(binDir, { recursive: true });
338
+ const tag = `${process.pid}-${randomUUID().slice(0, 8)}`;
339
+ const src = join4(binDir, `rove-pty-job-${tag}.cs`);
340
+ const tmp = join4(binDir, `rove-pty-job-${tag}.exe`);
341
+ try {
342
+ writeFileSync(src, PTY_JOB_LAUNCHER_CS);
343
+ const built = await run(csc, ["/nologo", "/target:exe", "/optimize+", `/out:${tmp}`, src], COMPILE_TIMEOUT_MS);
344
+ if (built.code !== 0 || !existsSync4(tmp))
345
+ return `csc failed: ${built.output}`;
346
+ try {
347
+ renameSync2(tmp, exe);
348
+ } catch (err) {
349
+ if (!existsSync4(exe))
350
+ return `could not place ${exe}: ${err.message}`;
351
+ }
352
+ return null;
353
+ } finally {
354
+ rmSync(src, { force: true });
355
+ rmSync(tmp, { force: true });
356
+ }
357
+ }
358
+ async function selfTest(exe) {
359
+ const comspec = process.env.ComSpec || "cmd.exe";
360
+ const probe = await run(exe, [comspec, "/d", "/c", `exit ${SELF_TEST_EXIT_CODE}`], SELF_TEST_TIMEOUT_MS, {
361
+ ...process.env,
362
+ [PTY_JOB_ENV]: newPtyJobName()
363
+ });
364
+ return probe.code === SELF_TEST_EXIT_CODE ? null : `self-test exited ${probe.code}: ${probe.output}`;
365
+ }
366
+ async function ensurePtyJobLauncher(binDir) {
367
+ try {
368
+ const exe = join4(binDir, launcherFileName());
369
+ let cachedFailure = null;
370
+ if (existsSync4(exe)) {
371
+ cachedFailure = await selfTest(exe);
372
+ if (cachedFailure === null)
373
+ return { path: exe };
374
+ rmSync(exe, { force: true });
375
+ }
376
+ const compileFailure = await compileLauncher(binDir, exe);
377
+ if (compileFailure)
378
+ return { path: null, reason: compileFailure };
379
+ const failure = await selfTest(exe);
380
+ if (failure === null)
381
+ return { path: exe };
382
+ return { path: null, reason: cachedFailure ? `${failure} (rebuilt after: ${cachedFailure})` : failure };
383
+ } catch (err) {
384
+ return { path: null, reason: `launcher setup failed: ${err.message}` };
385
+ }
386
+ }
387
+ function sameSocket(a, b) {
388
+ const norm = (p) => resolve(p.replace(/\//g, "\\")).toLowerCase();
389
+ return norm(a) === norm(b);
390
+ }
391
+ function withoutPtyJob(env) {
392
+ const out = { ...env };
393
+ delete out[PTY_JOB_ENV];
394
+ delete out[PTY_JOB_OWNER_ENV];
395
+ return out;
396
+ }
397
+ function ptyJobLaunch(env, ownDaemonSocket) {
398
+ const job = env[PTY_JOB_ENV];
399
+ const owner = env[PTY_JOB_OWNER_ENV];
400
+ if (!job || !owner)
401
+ return null;
402
+ return { job, mode: sameSocket(owner, ownDaemonSocket) ? "escape" : "join" };
403
+ }
404
+
405
+ // ../rove-daemon/src/daemon/pty-driver.ts
214
406
  var TERMINAL_NAME = "xterm-256color";
215
407
  function bunTerminalDriver(spawn) {
216
408
  const spawnTerminal = spawn ?? ((argv, options) => Bun.spawn(argv, options));
@@ -235,20 +427,26 @@ function bunTerminalDriver(spawn) {
235
427
  };
236
428
  };
237
429
  }
238
- async function nodePtyDriver(spawn, endTree = taskkillProcessTree) {
430
+ async function nodePtyDriver(spawn, endTree = taskkillProcessTree, job) {
239
431
  const spawnPty = spawn ?? (await import("node-pty")).spawn;
240
432
  return (request) => {
241
433
  const [file, ...args] = request.argv;
242
- const child = spawnPty(file ?? "", args, {
434
+ const env = Object.fromEntries(Object.entries(request.env).filter((entry) => entry[1] !== undefined));
435
+ const [spawnFile, spawnArgs, spawnEnv] = job ? [
436
+ job.launcher,
437
+ [file ?? "", ...args],
438
+ { ...env, [PTY_JOB_ENV]: newPtyJobName(), [PTY_JOB_OWNER_ENV]: job.owner }
439
+ ] : [file ?? "", args, env];
440
+ const child = spawnPty(spawnFile, spawnArgs, {
243
441
  name: TERMINAL_NAME,
244
442
  cols: request.cols,
245
443
  rows: request.rows,
246
444
  cwd: request.cwd,
247
- env: Object.fromEntries(Object.entries(request.env).filter((entry) => entry[1] !== undefined))
445
+ env: spawnEnv
248
446
  });
249
447
  let settle = () => {};
250
- const exited = new Promise((resolve) => {
251
- settle = resolve;
448
+ const exited = new Promise((resolve2) => {
449
+ settle = resolve2;
252
450
  });
253
451
  child.onData((data) => request.onData(data));
254
452
  child.onExit(({ exitCode }) => settle({ code: exitCode, signal: null }));
@@ -264,18 +462,18 @@ async function nodePtyDriver(spawn, endTree = taskkillProcessTree) {
264
462
  };
265
463
  }
266
464
 
267
- // ../kobe-daemon/src/daemon/pty-host-log.ts
465
+ // ../rove-daemon/src/daemon/pty-host-log.ts
268
466
  function formatPtyHostLine(event, message, now = new Date) {
269
467
  return `[${now.toISOString()}] pty-host [${event}]: ${message}`;
270
468
  }
271
469
 
272
- // ../kobe-daemon/src/daemon/pty-server.ts
470
+ // ../rove-daemon/src/daemon/pty-server.ts
273
471
  import { readFileSync as readFileSync4 } from "node:fs";
274
- import { mkdir as mkdir3, unlink as unlink2 } from "node:fs/promises";
472
+ import { mkdir as mkdir4, unlink } from "node:fs/promises";
275
473
  import { createServer } from "node:net";
276
- import { dirname as dirname5 } from "node:path";
474
+ import { dirname as dirname7 } from "node:path";
277
475
 
278
- // ../kobe-daemon/src/daemon/client-writer.ts
476
+ // ../rove-daemon/src/daemon/client-writer.ts
279
477
  var DEFAULT_WRITE_HIGH_WATER_MARK = 8 * 1024 * 1024;
280
478
 
281
479
  class ClientWriter {
@@ -368,28 +566,7 @@ class ClientWriter {
368
566
  }
369
567
  }
370
568
 
371
- // ../kobe-daemon/src/daemon/compat-link.ts
372
- import { lstat, symlink, unlink } from "node:fs/promises";
373
- import { dirname as dirname2 } from "node:path";
374
- async function linkLegacyRuntimePath(canonical, legacy) {
375
- if (canonical === legacy)
376
- return false;
377
- try {
378
- if (!await lstat(dirname2(legacy)).catch(() => null))
379
- return false;
380
- const existing = await lstat(legacy).catch(() => null);
381
- if (existing && !existing.isSymbolicLink())
382
- return false;
383
- if (existing)
384
- await unlink(legacy).catch(() => {});
385
- await symlink(canonical, legacy);
386
- return true;
387
- } catch {
388
- return false;
389
- }
390
- }
391
-
392
- // ../kobe-daemon/src/daemon/crash-log.ts
569
+ // ../rove-daemon/src/daemon/crash-log.ts
393
570
  function toError(err) {
394
571
  if (err instanceof Error)
395
572
  return err;
@@ -410,19 +587,19 @@ function logDaemonError(subsystem, err) {
410
587
  process.stderr.write(formatDaemonError(subsystem, err));
411
588
  }
412
589
 
413
- // ../kobe-daemon/src/daemon/json-file.ts
414
- import { randomUUID } from "node:crypto";
590
+ // ../rove-daemon/src/daemon/json-file.ts
591
+ import { randomUUID as randomUUID2 } from "node:crypto";
415
592
  import { mkdir, rename, writeFile } from "node:fs/promises";
416
593
  import { dirname as dirname3 } from "node:path";
417
594
  async function writeTextAtomic(path, text, mode) {
418
595
  await mkdir(dirname3(path), { recursive: true });
419
- const tmp = `${path}.tmp-${process.pid}-${randomUUID()}`;
596
+ const tmp = `${path}.tmp-${process.pid}-${randomUUID2()}`;
420
597
  await writeFile(tmp, text, { encoding: "utf8", mode });
421
598
  await rename(tmp, path);
422
599
  }
423
600
  var locks = new Map;
424
601
 
425
- // ../kobe-daemon/src/daemon/line-receiver.ts
602
+ // ../rove-daemon/src/daemon/line-receiver.ts
426
603
  var MAX_REQUEST_FRAME_BYTES = 8 * 1024 * 1024;
427
604
 
428
605
  class LineReceiver {
@@ -469,10 +646,10 @@ class LineReceiver {
469
646
  }
470
647
  }
471
648
 
472
- // ../kobe-daemon/src/daemon/owner-only.ts
649
+ // ../rove-daemon/src/daemon/owner-only.ts
473
650
  import { chmodSync } from "node:fs";
474
651
  import { chmod, mkdir as mkdir2 } from "node:fs/promises";
475
- import { join as join3 } from "node:path";
652
+ import { join as join5 } from "node:path";
476
653
  var OWNER_ONLY_DIR_MODE = 448;
477
654
  var OWNER_ONLY_FILE_MODE = 384;
478
655
  async function tighten(path, mode) {
@@ -501,12 +678,12 @@ async function ensureOwnerOnlyDir(dir) {
501
678
  await tightenDirPermissions(dir);
502
679
  }
503
680
  function ensureOwnerOnlyStateDir(homeDir) {
504
- return ensureOwnerOnlyDir(join3(homeDir, ROVE_STATE_DIR_BASENAME));
681
+ return ensureOwnerOnlyDir(join5(homeDir, ROVE_STATE_DIR_BASENAME));
505
682
  }
506
683
 
507
- // ../kobe-daemon/src/daemon/automation-contracts.ts
684
+ // ../rove-daemon/src/daemon/automation-contracts.ts
508
685
  var ROUTINE_RESPONSE_WINDOW_MS = 2 * 60 * 60000;
509
- // ../kobe-plugin-sdk/dist/contract.js
686
+ // ../rove-plugin-sdk/dist/contract.js
510
687
  var DAEMON_CHANNELS = [
511
688
  "task.snapshot",
512
689
  "issue.snapshot",
@@ -532,13 +709,13 @@ var DAEMON_CHANNELS = [
532
709
  "graphics.write"
533
710
  ];
534
711
 
535
- // ../kobe-daemon/src/daemon/channels.ts
712
+ // ../rove-daemon/src/daemon/channels.ts
536
713
  var CHANNEL_NAMES = DAEMON_CHANNELS;
537
714
  var CHANNEL_NAME_SET = new Set(CHANNEL_NAMES);
538
- // ../kobe-daemon/src/daemon/protocol-compat.ts
715
+ // ../rove-daemon/src/daemon/protocol-compat.ts
539
716
  var DAEMON_PROTOCOL_VERSION = 5;
540
717
 
541
- // ../kobe-daemon/src/daemon/protocol.ts
718
+ // ../rove-daemon/src/daemon/protocol.ts
542
719
  var BLOCKING_RPCS = new Set([
543
720
  "ui.prompt",
544
721
  "task.land",
@@ -556,10 +733,10 @@ function frameToLine(frame) {
556
733
  `;
557
734
  }
558
735
 
559
- // ../kobe-daemon/src/daemon/pty-data-migration.ts
560
- import { existsSync as existsSync4, lstatSync, mkdirSync, renameSync as renameSync2, symlinkSync } from "node:fs";
736
+ // ../rove-daemon/src/daemon/pty-data-migration.ts
737
+ import { existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync2, renameSync as renameSync3 } from "node:fs";
561
738
  import { homedir as homedir2 } from "node:os";
562
- import { join as join4 } from "node:path";
739
+ import { join as join6 } from "node:path";
563
740
  var PTY_HOST_DATA_ENTRIES = ["pty-exits.json", "pty-sessions"];
564
741
  function lstatIfExists(path) {
565
742
  try {
@@ -569,37 +746,34 @@ function lstatIfExists(path) {
569
746
  }
570
747
  }
571
748
  function migrateLegacyPtyHostData(homeDir = readRoveHomeDirEnv() ?? homedir2()) {
572
- const canonicalDir = join4(homeDir, ROVE_STATE_DIR_BASENAME);
573
- const legacyDir = join4(homeDir, COMPAT_STATE_DIR_BASENAME);
749
+ const canonicalDir = join6(homeDir, ROVE_STATE_DIR_BASENAME);
750
+ const legacyDir = preRenameStateDir(homeDir);
574
751
  if (canonicalDir === legacyDir)
575
752
  return [];
576
753
  const moved = [];
577
754
  for (const name of PTY_HOST_DATA_ENTRIES) {
578
- const canonical = join4(canonicalDir, name);
579
- const legacy = join4(legacyDir, name);
755
+ const canonical = join6(canonicalDir, name);
756
+ const legacy = join6(legacyDir, name);
580
757
  try {
581
- if (existsSync4(canonical))
758
+ if (existsSync5(canonical))
582
759
  continue;
583
760
  const stat = lstatIfExists(legacy);
584
761
  if (!stat || stat.isSymbolicLink())
585
762
  continue;
586
- mkdirSync(canonicalDir, { recursive: true });
587
- renameSync2(legacy, canonical);
763
+ mkdirSync2(canonicalDir, { recursive: true });
764
+ renameSync3(legacy, canonical);
588
765
  moved.push(name);
589
- try {
590
- symlinkSync(canonical, legacy);
591
- } catch {}
592
766
  } catch {}
593
767
  }
594
768
  return moved;
595
769
  }
596
770
 
597
- // ../kobe-daemon/src/daemon/pty-exit-store.ts
598
- import { randomUUID as randomUUID2 } from "node:crypto";
599
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync as renameSync3, writeFileSync } from "node:fs";
771
+ // ../rove-daemon/src/daemon/pty-exit-store.ts
772
+ import { randomUUID as randomUUID3 } from "node:crypto";
773
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync2, renameSync as renameSync4, writeFileSync as writeFileSync2 } from "node:fs";
600
774
  import { dirname as dirname4 } from "node:path";
601
775
 
602
- // ../kobe-daemon/src/daemon/terminal-rows.ts
776
+ // ../rove-daemon/src/daemon/terminal-rows.ts
603
777
  var CURSOR_DOWN_RE = /\x1b\[(\d*)[BE]/g;
604
778
  var CURSOR_POSITION_RE = /\x1b\[[\d;]*[Hf]/g;
605
779
  var MAX_ROWS_PER_ESCAPE = 200;
@@ -623,7 +797,7 @@ function terminalRows(raw, maxLineChars) {
623
797
  });
624
798
  }
625
799
 
626
- // ../kobe-daemon/src/daemon/pty-exit-store.ts
800
+ // ../rove-daemon/src/daemon/pty-exit-store.ts
627
801
  var MAX_RECORDS = 50;
628
802
  var TAIL_LINES = 40;
629
803
  var TAIL_LINE_CHARS = 500;
@@ -650,6 +824,11 @@ function healTails(records) {
650
824
  }
651
825
  return out;
652
826
  }
827
+ function compareByExitAtDesc(a, b) {
828
+ if (a.at === b.at)
829
+ return 0;
830
+ return a.at < b.at ? 1 : -1;
831
+ }
653
832
  function readPtyExitStore(path = defaultPtyExitsPath()) {
654
833
  const read = (status, records = {}) => ({
655
834
  records,
@@ -693,23 +872,23 @@ function writeRecord(storeKey, record, path) {
693
872
  throw new Error(`pty-exits store is unreadable (${path}) — refusing to overwrite it`);
694
873
  const records = store.records;
695
874
  records[storeKey] = record;
696
- const newest = Object.entries(records).sort(([, a], [, b]) => a.at < b.at ? 1 : -1).slice(0, MAX_RECORDS);
697
- mkdirSync2(dirname4(path), { recursive: true });
698
- const tmp = `${path}.tmp-${process.pid}-${randomUUID2()}`;
699
- writeFileSync(tmp, JSON.stringify(Object.fromEntries(newest), null, 2), {
875
+ const newest = Object.entries(records).sort(([, a], [, b]) => compareByExitAtDesc(a, b)).slice(0, MAX_RECORDS);
876
+ mkdirSync3(dirname4(path), { recursive: true });
877
+ const tmp = `${path}.tmp-${process.pid}-${randomUUID3()}`;
878
+ writeFileSync2(tmp, JSON.stringify(Object.fromEntries(newest), null, 2), {
700
879
  encoding: "utf8",
701
880
  mode: OWNER_ONLY_FILE_MODE
702
881
  });
703
- renameSync3(tmp, path);
882
+ renameSync4(tmp, path);
704
883
  }
705
884
 
706
- // ../kobe-daemon/src/daemon/pty-freeze-store.ts
707
- import { randomUUID as randomUUID3 } from "node:crypto";
708
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, readdirSync, renameSync as renameSync4, rmSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "node:fs";
709
- import { join as join5 } from "node:path";
885
+ // ../rove-daemon/src/daemon/pty-freeze-store.ts
886
+ import { randomUUID as randomUUID4 } from "node:crypto";
887
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync3, readdirSync, renameSync as renameSync5, rmSync as rmSync2, statSync as statSync2, writeFileSync as writeFileSync3 } from "node:fs";
888
+ import { join as join7 } from "node:path";
710
889
  import { StringDecoder } from "node:string_decoder";
711
890
 
712
- // ../kobe-daemon/src/daemon/terminal-colors.ts
891
+ // ../rove-daemon/src/daemon/terminal-colors.ts
713
892
  var DEFAULT_TERMINAL_COLORS = {
714
893
  foreground: "#eae7df",
715
894
  background: "#141413"
@@ -758,7 +937,7 @@ function foldDefaultColorQueries(previousCarry, chunkText) {
758
937
  return { slots, carry: trailingQueryPrefix(text.slice(end)) };
759
938
  }
760
939
 
761
- // ../kobe-daemon/src/daemon/terminal-modes.ts
940
+ // ../rove-daemon/src/daemon/terminal-modes.ts
762
941
  var DEFAULT_MODES = {
763
942
  applicationCursorKeys: false,
764
943
  applicationKeypad: false,
@@ -1030,7 +1209,7 @@ function ansiModeValue(modes, mode) {
1030
1209
  }
1031
1210
  }
1032
1211
 
1033
- // ../kobe-daemon/src/daemon/pty-freeze-store.ts
1212
+ // ../rove-daemon/src/daemon/pty-freeze-store.ts
1034
1213
  var FREEZE_VERSION = 1;
1035
1214
  function freezeSession(session, now = new Date) {
1036
1215
  return {
@@ -1071,7 +1250,7 @@ function thawSession(record, cap) {
1071
1250
  scanTerminalModes(modes, chunk.toString("latin1"));
1072
1251
  return {
1073
1252
  key: record.key,
1074
- generation: randomUUID3(),
1253
+ generation: randomUUID4(),
1075
1254
  cwd: record.cwd,
1076
1255
  proc: null,
1077
1256
  alive: false,
@@ -1098,7 +1277,7 @@ function thawSession(record, cap) {
1098
1277
  };
1099
1278
  }
1100
1279
  function recordFile(dir, key) {
1101
- return join5(dir, `${encodeURIComponent(key)}.json`);
1280
+ return join7(dir, `${encodeURIComponent(key)}.json`);
1102
1281
  }
1103
1282
  function parseRecord(raw) {
1104
1283
  try {
@@ -1136,7 +1315,7 @@ function loadFrozenSessions(dir = defaultPtyFreezeDir(), now = Date.now(), repor
1136
1315
  if (!name.endsWith(".json"))
1137
1316
  continue;
1138
1317
  try {
1139
- const st = statSync2(join5(dir, name));
1318
+ const st = statSync2(join7(dir, name));
1140
1319
  entries.push({ name, size: st.size, mtimeMs: st.mtimeMs });
1141
1320
  } catch {}
1142
1321
  }
@@ -1157,7 +1336,7 @@ function loadFrozenSessions(dir = defaultPtyFreezeDir(), now = Date.now(), repor
1157
1336
  }
1158
1337
  let record = null;
1159
1338
  try {
1160
- record = parseRecord(readFileSync3(join5(dir, entry.name), "utf8"));
1339
+ record = parseRecord(readFileSync3(join7(dir, entry.name), "utf8"));
1161
1340
  } catch {}
1162
1341
  bytesRead += entry.size;
1163
1342
  if (!record) {
@@ -1171,7 +1350,7 @@ function loadFrozenSessions(dir = defaultPtyFreezeDir(), now = Date.now(), repor
1171
1350
  }
1172
1351
  for (const name of stale) {
1173
1352
  try {
1174
- rmSync(join5(dir, name), { force: true });
1353
+ rmSync2(join7(dir, name), { force: true });
1175
1354
  } catch {}
1176
1355
  }
1177
1356
  kept.sort((a, b) => updatedAtMs(b) - updatedAtMs(a));
@@ -1188,7 +1367,7 @@ function tightenExistingPermissions(dir) {
1188
1367
  }
1189
1368
  for (const name of names) {
1190
1369
  if (name.endsWith(".json"))
1191
- tightenFilePermissionsSync(join5(dir, name));
1370
+ tightenFilePermissionsSync(join7(dir, name));
1192
1371
  }
1193
1372
  }
1194
1373
  function fileFreezeSink(dir = defaultPtyFreezeDir()) {
@@ -1196,31 +1375,31 @@ function fileFreezeSink(dir = defaultPtyFreezeDir()) {
1196
1375
  return {
1197
1376
  save(record) {
1198
1377
  try {
1199
- mkdirSync3(dir, { recursive: true, mode: OWNER_ONLY_DIR_MODE });
1378
+ mkdirSync4(dir, { recursive: true, mode: OWNER_ONLY_DIR_MODE });
1200
1379
  const target = recordFile(dir, record.key);
1201
1380
  const staging = `${target}.${process.pid}.tmp`;
1202
- writeFileSync2(staging, JSON.stringify(record), { encoding: "utf8", mode: OWNER_ONLY_FILE_MODE });
1203
- renameSync4(staging, target);
1381
+ writeFileSync3(staging, JSON.stringify(record), { encoding: "utf8", mode: OWNER_ONLY_FILE_MODE });
1382
+ renameSync5(staging, target);
1204
1383
  } catch {}
1205
1384
  },
1206
1385
  drop(key) {
1207
1386
  try {
1208
- rmSync(recordFile(dir, key), { force: true });
1387
+ rmSync2(recordFile(dir, key), { force: true });
1209
1388
  } catch {}
1210
1389
  }
1211
1390
  };
1212
1391
  }
1213
1392
  function clearFrozenSessions(dir = defaultPtyFreezeDir()) {
1214
1393
  try {
1215
- rmSync(dir, { recursive: true, force: true });
1394
+ rmSync2(dir, { recursive: true, force: true });
1216
1395
  } catch {}
1217
1396
  }
1218
1397
 
1219
- // ../kobe-daemon/src/daemon/pty-child-controller.ts
1220
- import { randomUUID as randomUUID5 } from "node:crypto";
1398
+ // ../rove-daemon/src/daemon/pty-child-controller.ts
1399
+ import { randomUUID as randomUUID6 } from "node:crypto";
1221
1400
 
1222
- // ../kobe-daemon/src/daemon/platform-shell.js
1223
- import { existsSync as existsSync5 } from "node:fs";
1401
+ // ../rove-daemon/src/daemon/platform-shell.js
1402
+ import { existsSync as existsSync6 } from "node:fs";
1224
1403
  function windowsBashCandidates(env) {
1225
1404
  const roots = [env.ProgramFiles, env["ProgramFiles(x86)"], env.LOCALAPPDATA && `${env.LOCALAPPDATA}\\Programs`];
1226
1405
  return roots.filter(Boolean).map((root) => `${root}\\Git\\bin\\bash.exe`);
@@ -1239,7 +1418,7 @@ function resolveLoginShell(options = {}) {
1239
1418
  const fallback = options.fallback ?? "/bin/bash";
1240
1419
  const platform = options.platform ?? process.platform;
1241
1420
  const env = options.env ?? process.env;
1242
- const exists = options.exists ?? existsSync5;
1421
+ const exists = options.exists ?? existsSync6;
1243
1422
  const cacheable = platform === process.platform && env === process.env && options.exists === undefined;
1244
1423
  if (cacheable) {
1245
1424
  const hit = cache.get(fallback);
@@ -1264,7 +1443,7 @@ function resolveUncached(fallback, platform, env, exists) {
1264
1443
  return WINDOWS_BASH_FALLBACK;
1265
1444
  }
1266
1445
 
1267
- // ../kobe-daemon/src/daemon/pty-env.js
1446
+ // ../rove-daemon/src/daemon/pty-env.js
1268
1447
  var IDENTITY_VARS = new Set([
1269
1448
  "TERM_PROGRAM",
1270
1449
  "TERM_PROGRAM_VERSION",
@@ -1295,13 +1474,13 @@ function embeddedTerminalEnv(base, overrides = {}) {
1295
1474
  return { ...env, ...overrides };
1296
1475
  }
1297
1476
 
1298
- // ../kobe-daemon/src/daemon/pty-host-types.ts
1299
- import { randomUUID as randomUUID4 } from "node:crypto";
1477
+ // ../rove-daemon/src/daemon/pty-host-types.ts
1478
+ import { randomUUID as randomUUID5 } from "node:crypto";
1300
1479
  import { StringDecoder as StringDecoder2 } from "node:string_decoder";
1301
1480
  function freshSessionState(key, spec, argv) {
1302
1481
  return {
1303
1482
  key,
1304
- generation: randomUUID4(),
1483
+ generation: randomUUID5(),
1305
1484
  cwd: spec.cwd,
1306
1485
  proc: null,
1307
1486
  alive: true,
@@ -1329,7 +1508,7 @@ function freshSessionState(key, spec, argv) {
1329
1508
  };
1330
1509
  }
1331
1510
 
1332
- // ../kobe-daemon/src/daemon/pty-observability.ts
1511
+ // ../rove-daemon/src/daemon/pty-observability.ts
1333
1512
  function sessionInfo(s) {
1334
1513
  return {
1335
1514
  key: s.key,
@@ -1432,13 +1611,13 @@ function peekRing(session, sinceOffset) {
1432
1611
  };
1433
1612
  }
1434
1613
 
1435
- // ../kobe-daemon/src/daemon/pty-termination.ts
1614
+ // ../rove-daemon/src/daemon/pty-termination.ts
1436
1615
  async function settledWithin(exited, ms) {
1437
1616
  let timer;
1438
1617
  const settled = await Promise.race([
1439
1618
  exited.then(() => true, () => true),
1440
- new Promise((resolve) => {
1441
- timer = setTimeout(() => resolve(false), ms);
1619
+ new Promise((resolve2) => {
1620
+ timer = setTimeout(() => resolve2(false), ms);
1442
1621
  })
1443
1622
  ]);
1444
1623
  if (timer)
@@ -1475,7 +1654,7 @@ async function terminatePtyChild(proc, onSettled, onSignal) {
1475
1654
  onSettled();
1476
1655
  }
1477
1656
 
1478
- // ../kobe-daemon/src/daemon/pty-child-controller.ts
1657
+ // ../rove-daemon/src/daemon/pty-child-controller.ts
1479
1658
  class PtyChildController {
1480
1659
  deps;
1481
1660
  constructor(deps) {
@@ -1492,7 +1671,7 @@ class PtyChildController {
1492
1671
  return session;
1493
1672
  }
1494
1673
  startChild(session) {
1495
- session.generation = randomUUID5();
1674
+ session.generation = randomUUID6();
1496
1675
  try {
1497
1676
  session.proc = (this.deps.driver ?? bunTerminalDriver())({
1498
1677
  argv: [...session.command],
@@ -1502,7 +1681,7 @@ class PtyChildController {
1502
1681
  COLUMNS: String(session.cols),
1503
1682
  LINES: String(session.rows),
1504
1683
  BASH_SILENCE_DEPRECATION_WARNING: "1",
1505
- KOBE_TERMINAL_PTY: "1"
1684
+ ROVE_TERMINAL_PTY: "1"
1506
1685
  }),
1507
1686
  cols: session.cols,
1508
1687
  rows: session.rows,
@@ -1569,7 +1748,7 @@ class PtyChildController {
1569
1748
  }
1570
1749
  }
1571
1750
 
1572
- // ../kobe-daemon/src/daemon/pty-freeze-policy.ts
1751
+ // ../rove-daemon/src/daemon/pty-freeze-policy.ts
1573
1752
  var FREEZE_INTERVAL_MS = 5000;
1574
1753
  var FREEZE_MIN_APPENDED_BYTES = 64 * 1024;
1575
1754
  var FREEZE_STALE_MS = 60000;
@@ -1583,7 +1762,7 @@ function shouldFreeze(session, now) {
1583
1762
  return appended >= FREEZE_MIN_APPENDED_BYTES || since >= FREEZE_STALE_MS;
1584
1763
  }
1585
1764
 
1586
- // ../kobe-daemon/src/daemon/pty-warm.ts
1765
+ // ../rove-daemon/src/daemon/pty-warm.ts
1587
1766
  class WarmSpare {
1588
1767
  deps;
1589
1768
  spare = null;
@@ -1636,7 +1815,7 @@ class WarmSpare {
1636
1815
  }
1637
1816
  }
1638
1817
 
1639
- // ../kobe-daemon/src/daemon/pty-host.ts
1818
+ // ../rove-daemon/src/daemon/pty-host.ts
1640
1819
  var LOGGED_WRITE_BYTES = 1024;
1641
1820
  var DEFAULT_SCROLLBACK_CAP = 512 * 1024;
1642
1821
  var EXIT_TAIL_BYTES = 16 * 1024;
@@ -1907,7 +2086,533 @@ class PtyHost {
1907
2086
  }
1908
2087
  }
1909
2088
 
1910
- // ../kobe-daemon/src/daemon/handler-validators.ts
2089
+ // ../rove-daemon/src/client/detached-spawn.ts
2090
+ import { spawn as spawn2 } from "node:child_process";
2091
+ import { appendFileSync, closeSync, mkdirSync as mkdirSync5, openSync } from "node:fs";
2092
+ import { dirname as dirname6 } from "node:path";
2093
+
2094
+ // ../rove-daemon/src/client/index.ts
2095
+ import { connect } from "node:net";
2096
+ import { StringDecoder as StringDecoder3 } from "node:string_decoder";
2097
+
2098
+ // ../rove-daemon/src/client/client-log.ts
2099
+ import { appendFile, mkdir as mkdir3, rename as rename2, stat } from "node:fs/promises";
2100
+ import { dirname as dirname5 } from "node:path";
2101
+ var context = "client";
2102
+ function coerceError(err) {
2103
+ if (err instanceof Error)
2104
+ return { message: err.message, stack: err.stack };
2105
+ let text;
2106
+ try {
2107
+ text = typeof err === "string" ? err : JSON.stringify(err);
2108
+ } catch {
2109
+ text = String(err);
2110
+ }
2111
+ return { message: text };
2112
+ }
2113
+ function formatClientEntry(subsystem, message, now = new Date) {
2114
+ return `[${now.toISOString()}] client ${context} [${subsystem}] pid=${process.pid}: ${message}
2115
+ `;
2116
+ }
2117
+ var warnedOnce = false;
2118
+ function warnOnce() {
2119
+ if (warnedOnce)
2120
+ return;
2121
+ warnedOnce = true;
2122
+ try {
2123
+ process.stderr.write(`[rove] client log write failed; continuing without it
2124
+ `);
2125
+ } catch {}
2126
+ }
2127
+ var writeChain = Promise.resolve();
2128
+ async function rotateClientLogIfNeeded(path) {
2129
+ try {
2130
+ const { size } = await stat(path);
2131
+ if (!shouldRotateLog(size, DEFAULT_LOG_ROTATE_CAP_BYTES))
2132
+ return;
2133
+ await rename2(path, `${path}.old`);
2134
+ } catch {}
2135
+ }
2136
+ function append(line, logPath) {
2137
+ const path = logPath ?? defaultClientLogPath();
2138
+ writeChain = writeChain.then(async () => {
2139
+ await rotateClientLogIfNeeded(path);
2140
+ try {
2141
+ await appendFile(path, line);
2142
+ } catch (err) {
2143
+ if (err?.code === "ENOENT") {
2144
+ await mkdir3(dirname5(path), { recursive: true });
2145
+ await appendFile(path, line);
2146
+ } else {
2147
+ throw err;
2148
+ }
2149
+ }
2150
+ }).catch(() => warnOnce());
2151
+ }
2152
+ function logClientError(subsystem, err) {
2153
+ const e = coerceError(err);
2154
+ append(formatClientEntry(subsystem, e.stack ?? e.message));
2155
+ }
2156
+
2157
+ // ../rove-daemon/src/client/index.ts
2158
+ class RpcTimeoutError extends Error {
2159
+ constructor(name, timeoutMs) {
2160
+ super(`daemon rpc "${name}" timed out after ${timeoutMs}ms (daemon wedged?)`);
2161
+ this.name = "RpcTimeoutError";
2162
+ }
2163
+ }
2164
+ function rpcTimeoutMs() {
2165
+ const raw = readRoveEnv("RPC_TIMEOUT_MS")?.trim();
2166
+ if (raw) {
2167
+ const parsed = Number.parseInt(raw, 10);
2168
+ if (Number.isFinite(parsed))
2169
+ return parsed;
2170
+ }
2171
+ return 20000;
2172
+ }
2173
+
2174
+ class ConnectTimeoutError extends Error {
2175
+ constructor(socketPath, timeoutMs) {
2176
+ super(`connecting to ${socketPath} timed out after ${timeoutMs}ms — nothing accepted the connection (daemon or PTY host not listening at that address)`);
2177
+ this.name = "ConnectTimeoutError";
2178
+ }
2179
+ }
2180
+ function connectTimeoutMs() {
2181
+ const raw = readRoveEnv("CONNECT_TIMEOUT_MS")?.trim();
2182
+ if (raw) {
2183
+ const parsed = Number.parseInt(raw, 10);
2184
+ if (Number.isFinite(parsed))
2185
+ return parsed;
2186
+ }
2187
+ return 5000;
2188
+ }
2189
+
2190
+ class RoveDaemonClient {
2191
+ socketPath;
2192
+ socket = null;
2193
+ buffer = "";
2194
+ nextId = 1;
2195
+ pending = new Map;
2196
+ handlers = new Map;
2197
+ lifecycleHandlers = new Map;
2198
+ connecting = null;
2199
+ disposed = false;
2200
+ constructor(socketPath) {
2201
+ this.socketPath = socketPath;
2202
+ }
2203
+ connect() {
2204
+ if (this.socket)
2205
+ return Promise.resolve();
2206
+ if (this.disposed)
2207
+ return Promise.reject(new Error("daemon client disposed"));
2208
+ if (this.connecting)
2209
+ return this.connecting;
2210
+ const p = this.openSocketWithFallback();
2211
+ this.connecting = p;
2212
+ const cleanup = () => {
2213
+ if (this.connecting === p)
2214
+ this.connecting = null;
2215
+ };
2216
+ p.then(cleanup, cleanup);
2217
+ return p;
2218
+ }
2219
+ get isDisposed() {
2220
+ return this.disposed;
2221
+ }
2222
+ close() {
2223
+ this.disposed = true;
2224
+ this.socket?.end();
2225
+ this.socket = null;
2226
+ this.failPending();
2227
+ }
2228
+ forceDisconnect() {
2229
+ const socket = this.socket;
2230
+ if (!socket)
2231
+ return;
2232
+ this.socket = null;
2233
+ socket.destroy();
2234
+ this.failPending();
2235
+ }
2236
+ failPending() {
2237
+ if (this.pending.size === 0)
2238
+ return;
2239
+ const err = new Error("daemon connection closed");
2240
+ for (const pending of this.pending.values()) {
2241
+ if (pending.timer)
2242
+ clearTimeout(pending.timer);
2243
+ pending.reject(err);
2244
+ }
2245
+ this.pending.clear();
2246
+ }
2247
+ on(name, handler) {
2248
+ let set = this.handlers.get(name);
2249
+ if (!set) {
2250
+ set = new Set;
2251
+ this.handlers.set(name, set);
2252
+ }
2253
+ set.add(handler);
2254
+ return () => {
2255
+ set?.delete(handler);
2256
+ if (set?.size === 0)
2257
+ this.handlers.delete(name);
2258
+ };
2259
+ }
2260
+ onChannel(channel, handler) {
2261
+ return this.on(channel, (frame) => handler(frame.payload));
2262
+ }
2263
+ subscribe(opts = {}) {
2264
+ const payload = {};
2265
+ if (opts.channels)
2266
+ payload.channels = opts.channels;
2267
+ if (opts.role)
2268
+ payload.role = opts.role;
2269
+ if (opts.cellPixelSize) {
2270
+ payload.cellPixelWidth = opts.cellPixelSize.width;
2271
+ payload.cellPixelHeight = opts.cellPixelSize.height;
2272
+ }
2273
+ return this.request("subscribe", payload);
2274
+ }
2275
+ onLifecycle(name, handler) {
2276
+ let set = this.lifecycleHandlers.get(name);
2277
+ if (!set) {
2278
+ set = new Set;
2279
+ this.lifecycleHandlers.set(name, set);
2280
+ }
2281
+ set.add(handler);
2282
+ return () => {
2283
+ set?.delete(handler);
2284
+ if (set?.size === 0)
2285
+ this.lifecycleHandlers.delete(name);
2286
+ };
2287
+ }
2288
+ async request(name, payload) {
2289
+ await this.connect();
2290
+ const socket = this.socket;
2291
+ if (!socket)
2292
+ throw new Error("daemon connection is not open");
2293
+ const id = String(this.nextId++);
2294
+ const promise = new Promise((resolve2, reject) => {
2295
+ const entry = { resolve: (value) => resolve2(value), reject };
2296
+ const timeoutMs = rpcTimeoutMs();
2297
+ if (timeoutMs > 0 && !BLOCKING_RPCS.has(name)) {
2298
+ entry.timer = setTimeout(() => this.onRequestTimeout(id, name, timeoutMs), timeoutMs);
2299
+ }
2300
+ this.pending.set(id, entry);
2301
+ });
2302
+ socket.write(frameToLine({ type: "request", id, name, payload }));
2303
+ return promise;
2304
+ }
2305
+ onRequestTimeout(id, name, timeoutMs) {
2306
+ const pending = this.pending.get(id);
2307
+ if (!pending)
2308
+ return;
2309
+ this.pending.delete(id);
2310
+ pending.reject(new RpcTimeoutError(name, timeoutMs));
2311
+ this.forceDisconnect();
2312
+ this.emitLifecycle("close");
2313
+ }
2314
+ async openSocketWithFallback() {
2315
+ try {
2316
+ await this.openSocket(this.socketPath);
2317
+ return;
2318
+ } catch (err) {
2319
+ if (!(err instanceof Error) || !("code" in err) || !["ENOENT", "ECONNREFUSED"].includes(String(err.code)))
2320
+ throw err;
2321
+ for (const path of preRenameRuntimePaths(this.socketPath)) {
2322
+ if (this.disposed)
2323
+ throw err;
2324
+ try {
2325
+ await this.openSocket(path);
2326
+ return;
2327
+ } catch {}
2328
+ }
2329
+ throw err;
2330
+ }
2331
+ }
2332
+ openSocket(path) {
2333
+ return new Promise((resolve2, reject) => {
2334
+ const socket = connect(path);
2335
+ this.socket = socket;
2336
+ const timeoutMs = connectTimeoutMs();
2337
+ let timer;
2338
+ if (timeoutMs > 0) {
2339
+ timer = setTimeout(() => {
2340
+ socket.off("connect", onConnect);
2341
+ socket.off("error", onError);
2342
+ socket.destroy();
2343
+ if (this.socket === socket)
2344
+ this.socket = null;
2345
+ reject(new ConnectTimeoutError(this.socketPath, timeoutMs));
2346
+ }, timeoutMs);
2347
+ }
2348
+ const onConnect = () => {
2349
+ if (timer)
2350
+ clearTimeout(timer);
2351
+ socket.off("error", onError);
2352
+ socket.on("error", () => socket.destroy());
2353
+ resolve2();
2354
+ };
2355
+ const onError = (err) => {
2356
+ if (timer)
2357
+ clearTimeout(timer);
2358
+ socket.off("connect", onConnect);
2359
+ if (this.socket === socket)
2360
+ this.socket = null;
2361
+ reject(err);
2362
+ };
2363
+ socket.once("connect", onConnect);
2364
+ socket.once("error", onError);
2365
+ const decoder = new StringDecoder3("utf8");
2366
+ this.buffer = "";
2367
+ socket.on("data", (chunk) => this.onData(decoder.write(chunk)));
2368
+ socket.on("close", () => this.onSocketClose(socket));
2369
+ });
2370
+ }
2371
+ onSocketClose(which) {
2372
+ if (this.socket !== which)
2373
+ return;
2374
+ this.socket = null;
2375
+ this.failPending();
2376
+ this.emitLifecycle("close");
2377
+ }
2378
+ emitLifecycle(name) {
2379
+ for (const handler of this.lifecycleHandlers.get(name) ?? []) {
2380
+ try {
2381
+ handler();
2382
+ } catch (err) {
2383
+ console.error(`[rove] lifecycle handler for "${name}" threw:`, err);
2384
+ }
2385
+ }
2386
+ }
2387
+ onData(chunk) {
2388
+ this.buffer += chunk;
2389
+ let nl = this.buffer.indexOf(`
2390
+ `);
2391
+ while (nl !== -1) {
2392
+ const line = this.buffer.slice(0, nl);
2393
+ this.buffer = this.buffer.slice(nl + 1);
2394
+ if (line.trim().length > 0)
2395
+ this.onLine(line);
2396
+ nl = this.buffer.indexOf(`
2397
+ `);
2398
+ }
2399
+ }
2400
+ onLine(line) {
2401
+ let frame;
2402
+ try {
2403
+ frame = JSON.parse(line);
2404
+ } catch (err) {
2405
+ logClientError("client-frame", err);
2406
+ return;
2407
+ }
2408
+ if (frame.type === "event") {
2409
+ this.emit(frame);
2410
+ return;
2411
+ }
2412
+ if (frame.type !== "response")
2413
+ return;
2414
+ const pending = this.pending.get(frame.id);
2415
+ if (!pending)
2416
+ return;
2417
+ this.pending.delete(frame.id);
2418
+ if (pending.timer)
2419
+ clearTimeout(pending.timer);
2420
+ if (frame.error) {
2421
+ const err = new Error(frame.error.message);
2422
+ if (frame.error.name)
2423
+ err.name = frame.error.name;
2424
+ pending.reject(err);
2425
+ } else
2426
+ pending.resolve(frame.payload);
2427
+ }
2428
+ emit(frame) {
2429
+ for (const handler of this.handlers.get(frame.name) ?? []) {
2430
+ try {
2431
+ handler(frame);
2432
+ } catch (err) {
2433
+ logClientError("client-event", err);
2434
+ }
2435
+ }
2436
+ for (const handler of this.handlers.get("*") ?? []) {
2437
+ try {
2438
+ handler(frame);
2439
+ } catch (err) {
2440
+ logClientError("client-event", err);
2441
+ }
2442
+ }
2443
+ }
2444
+ }
2445
+
2446
+ // ../rove-daemon/src/client/win-detached-launch.ts
2447
+ import { spawn } from "node:child_process";
2448
+ import { existsSync as existsSync7 } from "node:fs";
2449
+ import { join as join8 } from "node:path";
2450
+ var LAUNCH_CMD_ENV = "ROVE_LAUNCH_CMD";
2451
+ var LAUNCH_LOG_ENV = "ROVE_LAUNCH_LOG";
2452
+ var WIN_DETACHED_LAUNCHER_PS = `
2453
+ $ErrorActionPreference = 'Stop'
2454
+ $cmd = $env:${LAUNCH_CMD_ENV}
2455
+ $log = $env:${LAUNCH_LOG_ENV}
2456
+ if (-not $cmd -or -not $log) { [Console]::Error.WriteLine('rove launcher: ${LAUNCH_CMD_ENV}/${LAUNCH_LOG_ENV} not set'); exit 1 }
2457
+ Remove-Item Env:${LAUNCH_CMD_ENV}, Env:${LAUNCH_LOG_ENV}
2458
+ Add-Type -TypeDefinition @'
2459
+ using System;
2460
+ using System.Runtime.InteropServices;
2461
+ public static class RoveDetachedLaunch {
2462
+ [StructLayout(LayoutKind.Sequential)] public struct SA { public int nLength; public IntPtr lpSecurityDescriptor; public bool bInheritHandle; }
2463
+ [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] public struct SI { public int cb; public string lpReserved; public string lpDesktop; public string lpTitle; public int dwX, dwY, dwXSize, dwYSize, dwXCountChars, dwYCountChars, dwFillAttribute, dwFlags; public short wShowWindow, cbReserved2; public IntPtr lpReserved2, hStdInput, hStdOutput, hStdError; }
2464
+ [StructLayout(LayoutKind.Sequential)] public struct PI { public IntPtr hProcess, hThread; public int dwProcessId, dwThreadId; }
2465
+ [DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)] static extern IntPtr CreateFile(string name, uint access, uint share, ref SA sa, uint disposition, uint flags, IntPtr template);
2466
+ [DllImport("kernel32.dll", CharSet=CharSet.Unicode, SetLastError=true)] static extern bool CreateProcess(string app, string cmd, IntPtr pa, IntPtr ta, bool inherit, uint flags, IntPtr env, string cwd, ref SI si, out PI pi);
2467
+ [DllImport("kernel32.dll")] static extern bool CloseHandle(IntPtr h);
2468
+ public static int Launch(string cmd, string log) {
2469
+ SA sa = new SA(); sa.nLength = Marshal.SizeOf(sa); sa.bInheritHandle = true;
2470
+ // FILE_APPEND_DATA | GENERIC_READ, shared read/write, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL
2471
+ IntPtr h = CreateFile(log, 0x0004 | 0x80000000, 0x1 | 0x2, ref sa, 4, 0x80, IntPtr.Zero);
2472
+ if (h == (IntPtr)(-1)) throw new Exception("CreateFile(" + log + ") failed: " + Marshal.GetLastWin32Error());
2473
+ IntPtr nul = CreateFile("NUL", 0x80000000, 0x1 | 0x2, ref sa, 3, 0x80, IntPtr.Zero);
2474
+ SI si = new SI(); si.cb = Marshal.SizeOf(si);
2475
+ si.dwFlags = 0x100 | 0x1; // STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW
2476
+ si.wShowWindow = 0; // SW_HIDE
2477
+ si.hStdInput = nul; si.hStdOutput = h; si.hStdError = h;
2478
+ PI pi;
2479
+ uint flags = 0x00000010 | 0x01000000 | 0x00000200; // CREATE_NEW_CONSOLE | CREATE_BREAKAWAY_FROM_JOB | CREATE_NEW_PROCESS_GROUP
2480
+ if (!CreateProcess(null, cmd, IntPtr.Zero, IntPtr.Zero, true, flags, IntPtr.Zero, null, ref si, out pi)) throw new Exception("CreateProcess failed: " + Marshal.GetLastWin32Error());
2481
+ CloseHandle(h); CloseHandle(nul); CloseHandle(pi.hThread); CloseHandle(pi.hProcess);
2482
+ return pi.dwProcessId;
2483
+ }
2484
+ }
2485
+ '@
2486
+ [RoveDetachedLaunch]::Launch($cmd, $log) | Out-Null
2487
+ `;
2488
+ function windowsCommandLine(argv) {
2489
+ return argv.map(quoteWindowsArg).join(" ");
2490
+ }
2491
+ function quoteWindowsArg(arg) {
2492
+ if (arg !== "" && !/[\s"]/.test(arg))
2493
+ return arg;
2494
+ let out = '"';
2495
+ let backslashes = 0;
2496
+ for (const ch of arg) {
2497
+ if (ch === "\\") {
2498
+ backslashes++;
2499
+ continue;
2500
+ }
2501
+ if (ch === '"') {
2502
+ out += `${"\\".repeat(backslashes * 2 + 1)}"`;
2503
+ backslashes = 0;
2504
+ continue;
2505
+ }
2506
+ out += "\\".repeat(backslashes) + ch;
2507
+ backslashes = 0;
2508
+ }
2509
+ return `${out}${"\\".repeat(backslashes * 2)}"`;
2510
+ }
2511
+ function windowsPowershellPath(env = process.env, exists = existsSync7) {
2512
+ const root = env.SystemRoot || env.windir || "C:\\Windows";
2513
+ const absolute = join8(root, "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
2514
+ return exists(absolute) ? absolute : "powershell.exe";
2515
+ }
2516
+ function encodePowershellCommand(script) {
2517
+ return Buffer.from(script, "utf16le").toString("base64");
2518
+ }
2519
+ function windowsDetachedLauncherArgs() {
2520
+ return [
2521
+ "-NoProfile",
2522
+ "-NonInteractive",
2523
+ "-ExecutionPolicy",
2524
+ "Bypass",
2525
+ "-EncodedCommand",
2526
+ encodePowershellCommand(WIN_DETACHED_LAUNCHER_PS)
2527
+ ];
2528
+ }
2529
+ function spawnWindowsDetached(command, args, env, logPath, spawnImpl = spawn) {
2530
+ const child = spawnImpl(windowsPowershellPath(env), windowsDetachedLauncherArgs(), {
2531
+ windowsHide: true,
2532
+ stdio: "ignore",
2533
+ env: { ...env, [LAUNCH_CMD_ENV]: windowsCommandLine([command, ...args]), [LAUNCH_LOG_ENV]: logPath }
2534
+ });
2535
+ child.unref();
2536
+ return child;
2537
+ }
2538
+
2539
+ // ../rove-daemon/src/client/detached-spawn.ts
2540
+ var ESCAPE_REQUEST_TIMEOUT_MS = 5000;
2541
+ function appendLaunchLog(logPath, line) {
2542
+ try {
2543
+ appendFileSync(logPath, `[${new Date().toISOString()}] rove: ${line}
2544
+ `);
2545
+ } catch {}
2546
+ }
2547
+ async function launchViaPtyHost(command, args, env, logPath, socketPath) {
2548
+ const client = new RoveDaemonClient(socketPath);
2549
+ let timer;
2550
+ try {
2551
+ await Promise.race([
2552
+ client.request("spawn.detached", { command, args: [...args], env, logPath }),
2553
+ new Promise((_, reject) => {
2554
+ timer = setTimeout(() => reject(new Error("timed out")), ESCAPE_REQUEST_TIMEOUT_MS);
2555
+ })
2556
+ ]);
2557
+ } finally {
2558
+ if (timer)
2559
+ clearTimeout(timer);
2560
+ client.close();
2561
+ }
2562
+ }
2563
+ function detachOptions(platform = process.platform) {
2564
+ return platform === "win32" ? { windowsHide: true } : { detached: true };
2565
+ }
2566
+ function spawnDetachedDaemon(command, args, env, logPath, platform = process.platform) {
2567
+ try {
2568
+ mkdirSync5(dirname6(logPath), { recursive: true });
2569
+ } catch {}
2570
+ if (platform === "win32") {
2571
+ if (ptyJobLaunch(env, defaultDaemonSocketPath())?.mode === "escape") {
2572
+ const free = withoutPtyJob(env);
2573
+ launchViaPtyHost(command, args, free, logPath, defaultPtyHostSocketPath()).catch((err) => {
2574
+ appendLaunchLog(logPath, `PTY host could not launch outside the tab (${err.message}) — it ends with the tab`);
2575
+ launchWindowsDetached(command, args, free, logPath, platform);
2576
+ });
2577
+ return;
2578
+ }
2579
+ launchWindowsDetached(command, args, env, logPath, platform);
2580
+ return;
2581
+ }
2582
+ spawnDirect(command, args, env, logPath, platform);
2583
+ }
2584
+ function launchWindowsDetached(command, args, env, logPath, platform) {
2585
+ const launcher = spawnWindowsDetached(command, args, env, logPath);
2586
+ const fallback = (reason) => {
2587
+ appendLaunchLog(logPath, `detached launcher ${reason} — spawning directly; this process will not outlive its parent`);
2588
+ spawnDirect(command, args, env, logPath, platform);
2589
+ };
2590
+ launcher.once("error", (err) => fallback(`could not start (${err.message})`));
2591
+ launcher.once("exit", (code, signal) => {
2592
+ if (code !== 0)
2593
+ fallback(`exited ${code ?? signal}`);
2594
+ });
2595
+ }
2596
+ function spawnDirect(command, args, env, logPath, platform) {
2597
+ let stdio = "ignore";
2598
+ let logFd;
2599
+ try {
2600
+ mkdirSync5(dirname6(logPath), { recursive: true });
2601
+ logFd = openSync(logPath, "a");
2602
+ stdio = ["ignore", logFd, logFd];
2603
+ } catch {
2604
+ stdio = "ignore";
2605
+ }
2606
+ const child = spawn2(command, [...args], { ...detachOptions(platform), stdio, env });
2607
+ child.unref();
2608
+ if (logFd !== undefined) {
2609
+ try {
2610
+ closeSync(logFd);
2611
+ } catch {}
2612
+ }
2613
+ }
2614
+
2615
+ // ../rove-daemon/src/daemon/handler-validators.ts
1911
2616
  function objectPayload(payload) {
1912
2617
  if (!payload || typeof payload !== "object" || Array.isArray(payload))
1913
2618
  return {};
@@ -1928,7 +2633,7 @@ function optionalBoolean(payload, key) {
1928
2633
  return value;
1929
2634
  }
1930
2635
 
1931
- // ../kobe-daemon/src/daemon/pty-server-verbs.ts
2636
+ // ../rove-daemon/src/daemon/pty-server-verbs.ts
1932
2637
  function dispatchPtyRequest(req, client, deps) {
1933
2638
  const { ptys } = deps;
1934
2639
  switch (req.name) {
@@ -1996,6 +2701,14 @@ function dispatchPtyRequest(req, client, deps) {
1996
2701
  ptys.warm(requireString(payload, "cwd"), typeof payload.shell === "string" ? payload.shell : undefined, typeof payload.cols === "number" ? payload.cols : undefined, typeof payload.rows === "number" ? payload.rows : undefined);
1997
2702
  return {};
1998
2703
  }
2704
+ case "spawn.detached": {
2705
+ if (process.platform !== "win32")
2706
+ throw new Error("spawn.detached is Windows-only");
2707
+ const payload = objectPayload(req.payload);
2708
+ const env = objectPayload(payload.env);
2709
+ spawnDetachedDaemon(requireString(payload, "command"), Array.isArray(payload.args) ? payload.args.filter((a) => typeof a === "string") : [], withoutPtyJob(Object.fromEntries(Object.entries(env).filter((e) => typeof e[1] === "string"))), requireString(payload, "logPath"));
2710
+ return {};
2711
+ }
1999
2712
  case "daemon.stop":
2000
2713
  deps.requestStop();
2001
2714
  return {};
@@ -2004,32 +2717,32 @@ function dispatchPtyRequest(req, client, deps) {
2004
2717
  }
2005
2718
  }
2006
2719
 
2007
- // ../kobe-daemon/src/daemon/socket-guard.ts
2720
+ // ../rove-daemon/src/daemon/socket-guard.ts
2008
2721
  async function listenOnUnixSocket(server, socketPath) {
2009
- await new Promise((resolve, reject) => {
2722
+ await new Promise((resolve2, reject) => {
2010
2723
  const evented = server;
2011
2724
  evented.once("error", reject);
2012
2725
  server.listen(socketPath, () => {
2013
2726
  evented.removeListener("error", reject);
2014
- resolve();
2727
+ resolve2();
2015
2728
  });
2016
2729
  });
2017
2730
  if (!isWindowsPipePath(socketPath))
2018
2731
  await tightenFilePermissions(socketPath);
2019
2732
  }
2020
2733
 
2021
- // ../kobe-daemon/src/daemon/pty-server.ts
2734
+ // ../rove-daemon/src/daemon/pty-server.ts
2022
2735
  var DEFAULT_IDLE_EXIT_MS = 60000;
2023
2736
  var DEFAULT_ORPHAN_CHECK_MS = 30000;
2024
2737
  function resolveMaxLifetimeMs() {
2025
- const raw = process.env.KOBE_PTY_MAX_LIFETIME_MS;
2738
+ const raw = process.env.ROVE_PTY_MAX_LIFETIME_MS;
2026
2739
  if (raw === undefined)
2027
2740
  return null;
2028
2741
  const n = Number(raw);
2029
2742
  return Number.isFinite(n) && n > 0 ? n : null;
2030
2743
  }
2031
2744
  function resolveIdleExitMs() {
2032
- const raw = process.env.KOBE_PTY_IDLE_EXIT_MS;
2745
+ const raw = process.env.ROVE_PTY_IDLE_EXIT_MS;
2033
2746
  if (raw === undefined)
2034
2747
  return DEFAULT_IDLE_EXIT_MS;
2035
2748
  const n = Number(raw);
@@ -2121,8 +2834,8 @@ async function startPtyHostServer(options = {}) {
2121
2834
  const pipeSocket = isWindowsPipePath(socketPath);
2122
2835
  await ensureOwnerOnlyStateDir(resolveDaemonHomeDir());
2123
2836
  if (!pipeSocket)
2124
- await mkdir3(dirname5(socketPath), { recursive: true });
2125
- await mkdir3(dirname5(pidPath), { recursive: true });
2837
+ await mkdir4(dirname7(socketPath), { recursive: true });
2838
+ await mkdir4(dirname7(pidPath), { recursive: true });
2126
2839
  const server = createServer((socket) => {
2127
2840
  const client = {
2128
2841
  socket,
@@ -2161,10 +2874,10 @@ async function startPtyHostServer(options = {}) {
2161
2874
  clearFrozenSessions(freezeDir);
2162
2875
  for (const client of Array.from(clients))
2163
2876
  client.socket.destroy();
2164
- await new Promise((resolve) => server.close(() => resolve()));
2877
+ await new Promise((resolve2) => server.close(() => resolve2()));
2165
2878
  if (!pipeSocket)
2166
- await unlink2(socketPath).catch(() => {});
2167
- await unlink2(pidPath).catch(() => {});
2879
+ await unlink(socketPath).catch(() => {});
2880
+ await unlink(pidPath).catch(() => {});
2168
2881
  }
2169
2882
  };
2170
2883
  async function stop() {
@@ -2216,11 +2929,6 @@ async function startPtyHostServer(options = {}) {
2216
2929
  await listenOnUnixSocket(server, socketPath);
2217
2930
  await writeTextAtomic(pidPath, `${process.pid}
2218
2931
  `);
2219
- if (!pipeSocket) {
2220
- const home = resolveDaemonHomeDir();
2221
- await linkLegacyRuntimePath(socketPath, legacyPtyHostSocketPath(home));
2222
- await linkLegacyRuntimePath(pidPath, legacyPtyHostPidPath(home));
2223
- }
2224
2932
  armIdle();
2225
2933
  log("boot", `pty host listening on ${socketPath}`);
2226
2934
  return api;
@@ -2229,15 +2937,18 @@ function writeFrame(client, frame) {
2229
2937
  client.writer.write(frameToLine(frame));
2230
2938
  }
2231
2939
 
2232
- // ../kobe-daemon/src/daemon/pty-host-node-entry.ts
2940
+ // ../rove-daemon/src/daemon/pty-host-node-entry.ts
2233
2941
  async function main() {
2234
2942
  rotateLogIfNeeded(defaultPtyHostLogPath());
2235
2943
  process.on("uncaughtException", (err) => console.error(formatPtyHostLine("crash", err?.stack ?? String(err))));
2236
2944
  process.on("unhandledRejection", (err) => console.error(formatPtyHostLine("reject", String(err))));
2237
- const driver = await nodePtyDriver();
2945
+ const log = (event, message) => console.log(formatPtyHostLine(event, message));
2946
+ const launcher = await ensurePtyJobLauncher(join9(resolveDaemonHomeDir(), ROVE_STATE_DIR_BASENAME, "bin")).catch((err) => ({ path: null, reason: String(err) }));
2947
+ log("pty", launcher.path ? `session job launcher ${launcher.path}` : `session jobs off: ${launcher.reason}`);
2948
+ const driver = await nodePtyDriver(undefined, undefined, launcher.path ? { launcher: launcher.path, owner: defaultDaemonSocketPath() } : undefined);
2238
2949
  const server = await startPtyHostServer({
2239
2950
  driver,
2240
- log: (event, message) => console.log(formatPtyHostLine(event, message)),
2951
+ log,
2241
2952
  version: process.env.ROVE_PTY_HOST_VERSION,
2242
2953
  onStop: () => process.exit(0)
2243
2954
  });