harnery 0.0.1 → 0.2.1

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 (445) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +84 -2
  3. package/bin/agent-coord +42 -0
  4. package/bin/agent-hook +44 -0
  5. package/bin/harn +40 -0
  6. package/dist/cli.d.ts +9 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +18 -0
  9. package/dist/commander.d.ts +128 -0
  10. package/dist/commander.d.ts.map +1 -0
  11. package/dist/commander.js +126 -0
  12. package/dist/commands/agents.d.ts +18 -0
  13. package/dist/commands/agents.d.ts.map +1 -0
  14. package/dist/commands/agents.js +3946 -0
  15. package/dist/commands/backup.d.ts +22 -0
  16. package/dist/commands/backup.d.ts.map +1 -0
  17. package/dist/commands/backup.js +262 -0
  18. package/dist/commands/browse-ai.d.ts +4 -0
  19. package/dist/commands/browse-ai.d.ts.map +1 -0
  20. package/dist/commands/browse-ai.js +156 -0
  21. package/dist/commands/browse.d.ts +4 -0
  22. package/dist/commands/browse.d.ts.map +1 -0
  23. package/dist/commands/browse.js +590 -0
  24. package/dist/commands/callers.d.ts +4 -0
  25. package/dist/commands/callers.d.ts.map +1 -0
  26. package/dist/commands/callers.js +276 -0
  27. package/dist/commands/completion.d.ts +17 -0
  28. package/dist/commands/completion.d.ts.map +1 -0
  29. package/dist/commands/completion.js +158 -0
  30. package/dist/commands/config-get.d.ts +4 -0
  31. package/dist/commands/config-get.d.ts.map +1 -0
  32. package/dist/commands/config-get.js +131 -0
  33. package/dist/commands/context.d.ts +11 -0
  34. package/dist/commands/context.d.ts.map +1 -0
  35. package/dist/commands/context.js +185 -0
  36. package/dist/commands/cookies.d.ts +4 -0
  37. package/dist/commands/cookies.d.ts.map +1 -0
  38. package/dist/commands/cookies.js +140 -0
  39. package/dist/commands/docs.d.ts +4 -0
  40. package/dist/commands/docs.d.ts.map +1 -0
  41. package/dist/commands/docs.js +137 -0
  42. package/dist/commands/doctor.d.ts +25 -0
  43. package/dist/commands/doctor.d.ts.map +1 -0
  44. package/dist/commands/doctor.js +200 -0
  45. package/dist/commands/edit-batch.d.ts +18 -0
  46. package/dist/commands/edit-batch.d.ts.map +1 -0
  47. package/dist/commands/edit-batch.js +172 -0
  48. package/dist/commands/eml.d.ts +4 -0
  49. package/dist/commands/eml.d.ts.map +1 -0
  50. package/dist/commands/eml.js +428 -0
  51. package/dist/commands/env.d.ts +4 -0
  52. package/dist/commands/env.d.ts.map +1 -0
  53. package/dist/commands/env.js +201 -0
  54. package/dist/commands/fetch.d.ts +4 -0
  55. package/dist/commands/fetch.d.ts.map +1 -0
  56. package/dist/commands/fetch.js +99 -0
  57. package/dist/commands/file-history.d.ts +4 -0
  58. package/dist/commands/file-history.d.ts.map +1 -0
  59. package/dist/commands/file-history.js +152 -0
  60. package/dist/commands/grep.d.ts +4 -0
  61. package/dist/commands/grep.d.ts.map +1 -0
  62. package/dist/commands/grep.js +317 -0
  63. package/dist/commands/init.d.ts +82 -0
  64. package/dist/commands/init.d.ts.map +1 -0
  65. package/dist/commands/init.js +288 -0
  66. package/dist/commands/outline.d.ts +4 -0
  67. package/dist/commands/outline.d.ts.map +1 -0
  68. package/dist/commands/outline.js +509 -0
  69. package/dist/commands/presence.d.ts +12 -0
  70. package/dist/commands/presence.d.ts.map +1 -0
  71. package/dist/commands/presence.js +123 -0
  72. package/dist/commands/read.d.ts +7 -0
  73. package/dist/commands/read.d.ts.map +1 -0
  74. package/dist/commands/read.js +46 -0
  75. package/dist/commands/scratch.d.ts +4 -0
  76. package/dist/commands/scratch.d.ts.map +1 -0
  77. package/dist/commands/scratch.js +426 -0
  78. package/dist/commands/session.d.ts +4 -0
  79. package/dist/commands/session.d.ts.map +1 -0
  80. package/dist/commands/session.js +162 -0
  81. package/dist/commands/sync.d.ts +24 -0
  82. package/dist/commands/sync.d.ts.map +1 -0
  83. package/dist/commands/sync.js +275 -0
  84. package/dist/commands/toc.d.ts +5 -0
  85. package/dist/commands/toc.d.ts.map +1 -0
  86. package/dist/commands/toc.js +153 -0
  87. package/dist/commands/tokens.d.ts +4 -0
  88. package/dist/commands/tokens.d.ts.map +1 -0
  89. package/dist/commands/tokens.js +48 -0
  90. package/dist/commands/tunnel.d.ts +4 -0
  91. package/dist/commands/tunnel.d.ts.map +1 -0
  92. package/dist/commands/tunnel.js +513 -0
  93. package/dist/commands/uninstall.d.ts +22 -0
  94. package/dist/commands/uninstall.d.ts.map +1 -0
  95. package/dist/commands/uninstall.js +126 -0
  96. package/dist/commands/web.d.ts +4 -0
  97. package/dist/commands/web.d.ts.map +1 -0
  98. package/dist/commands/web.js +165 -0
  99. package/dist/core/agents/canonical-emit.d.ts +27 -0
  100. package/dist/core/agents/canonical-emit.d.ts.map +1 -0
  101. package/dist/core/agents/canonical-emit.js +72 -0
  102. package/dist/core/agents/cli-emit.d.ts +27 -0
  103. package/dist/core/agents/cli-emit.d.ts.map +1 -0
  104. package/dist/core/agents/cli-emit.js +57 -0
  105. package/dist/core/agents/cli.d.ts +10 -0
  106. package/dist/core/agents/cli.d.ts.map +1 -0
  107. package/dist/core/agents/cli.js +757 -0
  108. package/dist/core/agents/codex-replay.d.ts +29 -0
  109. package/dist/core/agents/codex-replay.d.ts.map +1 -0
  110. package/dist/core/agents/codex-replay.js +138 -0
  111. package/dist/core/agents/coord-client.d.ts +98 -0
  112. package/dist/core/agents/coord-client.d.ts.map +1 -0
  113. package/dist/core/agents/coord-client.js +212 -0
  114. package/dist/core/agents/events/consume.d.ts +59 -0
  115. package/dist/core/agents/events/consume.d.ts.map +1 -0
  116. package/dist/core/agents/events/consume.js +147 -0
  117. package/dist/core/agents/events/emit.d.ts +42 -0
  118. package/dist/core/agents/events/emit.d.ts.map +1 -0
  119. package/dist/core/agents/events/emit.js +70 -0
  120. package/dist/core/agents/events/ulid.d.ts +11 -0
  121. package/dist/core/agents/events/ulid.d.ts.map +1 -0
  122. package/dist/core/agents/events/ulid.js +47 -0
  123. package/dist/core/agents/index.d.ts +14 -0
  124. package/dist/core/agents/index.d.ts.map +1 -0
  125. package/dist/core/agents/index.js +13 -0
  126. package/dist/core/agents/paths.d.ts +6 -0
  127. package/dist/core/agents/paths.d.ts.map +1 -0
  128. package/dist/core/agents/paths.js +17 -0
  129. package/dist/core/agents/render/prompt-context.d.ts +43 -0
  130. package/dist/core/agents/render/prompt-context.d.ts.map +1 -0
  131. package/dist/core/agents/render/prompt-context.js +335 -0
  132. package/dist/core/agents/render/session-context.d.ts +39 -0
  133. package/dist/core/agents/render/session-context.d.ts.map +1 -0
  134. package/dist/core/agents/render/session-context.js +283 -0
  135. package/dist/core/agents/rules/claim-conflict.d.ts +35 -0
  136. package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -0
  137. package/dist/core/agents/rules/claim-conflict.js +244 -0
  138. package/dist/core/agents/rules/commit-conflict.d.ts +59 -0
  139. package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -0
  140. package/dist/core/agents/rules/commit-conflict.js +244 -0
  141. package/dist/core/agents/rules/stop-hook.d.ts +44 -0
  142. package/dist/core/agents/rules/stop-hook.d.ts.map +1 -0
  143. package/dist/core/agents/rules/stop-hook.js +161 -0
  144. package/dist/core/agents/session-events.d.ts +41 -0
  145. package/dist/core/agents/session-events.d.ts.map +1 -0
  146. package/dist/core/agents/session-events.js +205 -0
  147. package/dist/core/agents/state/activity-log.d.ts +18 -0
  148. package/dist/core/agents/state/activity-log.d.ts.map +1 -0
  149. package/dist/core/agents/state/activity-log.js +34 -0
  150. package/dist/core/agents/state/council.d.ts +39 -0
  151. package/dist/core/agents/state/council.d.ts.map +1 -0
  152. package/dist/core/agents/state/council.js +216 -0
  153. package/dist/core/agents/state/heartbeat-projector.d.ts +59 -0
  154. package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -0
  155. package/dist/core/agents/state/heartbeat-projector.js +436 -0
  156. package/dist/core/agents/state/heartbeat-writer.d.ts +64 -0
  157. package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -0
  158. package/dist/core/agents/state/heartbeat-writer.js +271 -0
  159. package/dist/core/agents/state/names.d.ts +35 -0
  160. package/dist/core/agents/state/names.d.ts.map +1 -0
  161. package/dist/core/agents/state/names.js +376 -0
  162. package/dist/core/agents/state/pidmap.d.ts +11 -0
  163. package/dist/core/agents/state/pidmap.d.ts.map +1 -0
  164. package/dist/core/agents/state/pidmap.js +32 -0
  165. package/dist/core/agents/state/scratch.d.ts +27 -0
  166. package/dist/core/agents/state/scratch.d.ts.map +1 -0
  167. package/dist/core/agents/state/scratch.js +90 -0
  168. package/dist/core/agents/state/shell-mutation.d.ts +17 -0
  169. package/dist/core/agents/state/shell-mutation.d.ts.map +1 -0
  170. package/dist/core/agents/state/shell-mutation.js +41 -0
  171. package/dist/core/agents/state/stale-sweep.d.ts +16 -0
  172. package/dist/core/agents/state/stale-sweep.d.ts.map +1 -0
  173. package/dist/core/agents/state/stale-sweep.js +166 -0
  174. package/dist/core/config.d.ts +29 -0
  175. package/dist/core/config.d.ts.map +1 -0
  176. package/dist/core/config.js +108 -0
  177. package/dist/core/hooks/cli.d.ts +21 -0
  178. package/dist/core/hooks/cli.d.ts.map +1 -0
  179. package/dist/core/hooks/cli.js +1123 -0
  180. package/dist/core/hooks/effects/image-capture.d.ts +43 -0
  181. package/dist/core/hooks/effects/image-capture.d.ts.map +1 -0
  182. package/dist/core/hooks/effects/image-capture.js +288 -0
  183. package/dist/core/hooks/effects/index.d.ts +64 -0
  184. package/dist/core/hooks/effects/index.d.ts.map +1 -0
  185. package/dist/core/hooks/effects/index.js +197 -0
  186. package/dist/core/hooks/events/emit.d.ts +31 -0
  187. package/dist/core/hooks/events/emit.d.ts.map +1 -0
  188. package/dist/core/hooks/events/emit.js +89 -0
  189. package/dist/core/hooks/events/schema.d.ts +235 -0
  190. package/dist/core/hooks/events/schema.d.ts.map +1 -0
  191. package/dist/core/hooks/events/schema.js +12 -0
  192. package/dist/core/hooks/events/ulid.d.ts +10 -0
  193. package/dist/core/hooks/events/ulid.d.ts.map +1 -0
  194. package/dist/core/hooks/events/ulid.js +47 -0
  195. package/dist/core/hooks/harness/detect.d.ts +9 -0
  196. package/dist/core/hooks/harness/detect.d.ts.map +1 -0
  197. package/dist/core/hooks/harness/detect.js +29 -0
  198. package/dist/core/hooks/harness/events.d.ts +45 -0
  199. package/dist/core/hooks/harness/events.d.ts.map +1 -0
  200. package/dist/core/hooks/harness/events.js +71 -0
  201. package/dist/core/hooks/harness/output.d.ts +46 -0
  202. package/dist/core/hooks/harness/output.d.ts.map +1 -0
  203. package/dist/core/hooks/harness/output.js +87 -0
  204. package/dist/core/hooks/harness/parse.d.ts +67 -0
  205. package/dist/core/hooks/harness/parse.d.ts.map +1 -0
  206. package/dist/core/hooks/harness/parse.js +132 -0
  207. package/dist/core/hooks/index.d.ts +8 -0
  208. package/dist/core/hooks/index.d.ts.map +1 -0
  209. package/dist/core/hooks/index.js +7 -0
  210. package/dist/core/hooks/resolve/anchor.d.ts +37 -0
  211. package/dist/core/hooks/resolve/anchor.d.ts.map +1 -0
  212. package/dist/core/hooks/resolve/anchor.js +48 -0
  213. package/dist/core/hooks/resolve/coord-root.d.ts +6 -0
  214. package/dist/core/hooks/resolve/coord-root.d.ts.map +1 -0
  215. package/dist/core/hooks/resolve/coord-root.js +27 -0
  216. package/dist/core/hooks/resolve/intent.d.ts +33 -0
  217. package/dist/core/hooks/resolve/intent.d.ts.map +1 -0
  218. package/dist/core/hooks/resolve/intent.js +79 -0
  219. package/dist/core/hooks/resolve/owner.d.ts +42 -0
  220. package/dist/core/hooks/resolve/owner.d.ts.map +1 -0
  221. package/dist/core/hooks/resolve/owner.js +140 -0
  222. package/dist/core/hooks/resolve/transcript.d.ts +26 -0
  223. package/dist/core/hooks/resolve/transcript.d.ts.map +1 -0
  224. package/dist/core/hooks/resolve/transcript.js +73 -0
  225. package/dist/index.d.ts +15 -0
  226. package/dist/index.d.ts.map +1 -0
  227. package/dist/index.js +13 -0
  228. package/dist/lib/agent-browser/client.d.ts +99 -0
  229. package/dist/lib/agent-browser/client.d.ts.map +1 -0
  230. package/dist/lib/agent-browser/client.js +177 -0
  231. package/dist/lib/agent-browser/index.d.ts +2 -0
  232. package/dist/lib/agent-browser/index.d.ts.map +1 -0
  233. package/dist/lib/agent-browser/index.js +1 -0
  234. package/dist/lib/browser/client.d.ts +193 -0
  235. package/dist/lib/browser/client.d.ts.map +1 -0
  236. package/dist/lib/browser/client.js +325 -0
  237. package/dist/lib/browser/dev-overlay.d.ts +23 -0
  238. package/dist/lib/browser/dev-overlay.d.ts.map +1 -0
  239. package/dist/lib/browser/dev-overlay.js +153 -0
  240. package/dist/lib/browser/index.d.ts +5 -0
  241. package/dist/lib/browser/index.d.ts.map +1 -0
  242. package/dist/lib/browser/index.js +2 -0
  243. package/dist/lib/browser/layout.d.ts +79 -0
  244. package/dist/lib/browser/layout.d.ts.map +1 -0
  245. package/dist/lib/browser/layout.js +220 -0
  246. package/dist/lib/browser/visibility.d.ts +86 -0
  247. package/dist/lib/browser/visibility.d.ts.map +1 -0
  248. package/dist/lib/browser/visibility.js +333 -0
  249. package/dist/lib/browser/visual-diff.d.ts +38 -0
  250. package/dist/lib/browser/visual-diff.d.ts.map +1 -0
  251. package/dist/lib/browser/visual-diff.js +107 -0
  252. package/dist/lib/completion/bash.d.ts +25 -0
  253. package/dist/lib/completion/bash.d.ts.map +1 -0
  254. package/dist/lib/completion/bash.js +284 -0
  255. package/dist/lib/completion/fish.d.ts +16 -0
  256. package/dist/lib/completion/fish.d.ts.map +1 -0
  257. package/dist/lib/completion/fish.js +118 -0
  258. package/dist/lib/completion/index.d.ts +5 -0
  259. package/dist/lib/completion/index.d.ts.map +1 -0
  260. package/dist/lib/completion/index.js +4 -0
  261. package/dist/lib/completion/walk.d.ts +68 -0
  262. package/dist/lib/completion/walk.d.ts.map +1 -0
  263. package/dist/lib/completion/walk.js +102 -0
  264. package/dist/lib/completion/zsh.d.ts +13 -0
  265. package/dist/lib/completion/zsh.d.ts.map +1 -0
  266. package/dist/lib/completion/zsh.js +249 -0
  267. package/dist/lib/context/index.d.ts +107 -0
  268. package/dist/lib/context/index.d.ts.map +1 -0
  269. package/dist/lib/context/index.js +275 -0
  270. package/dist/lib/cookies/client.d.ts +131 -0
  271. package/dist/lib/cookies/client.d.ts.map +1 -0
  272. package/dist/lib/cookies/client.js +239 -0
  273. package/dist/lib/cookies/index.d.ts +2 -0
  274. package/dist/lib/cookies/index.d.ts.map +1 -0
  275. package/dist/lib/cookies/index.js +1 -0
  276. package/dist/lib/council/index.d.ts +266 -0
  277. package/dist/lib/council/index.d.ts.map +1 -0
  278. package/dist/lib/council/index.js +674 -0
  279. package/dist/lib/docs-index.d.ts +28 -0
  280. package/dist/lib/docs-index.d.ts.map +1 -0
  281. package/dist/lib/docs-index.js +169 -0
  282. package/dist/lib/docs-lint.d.ts +26 -0
  283. package/dist/lib/docs-lint.d.ts.map +1 -0
  284. package/dist/lib/docs-lint.js +378 -0
  285. package/dist/lib/docs-sweep.d.ts +34 -0
  286. package/dist/lib/docs-sweep.d.ts.map +1 -0
  287. package/dist/lib/docs-sweep.js +304 -0
  288. package/dist/lib/docs.d.ts +27 -0
  289. package/dist/lib/docs.d.ts.map +1 -0
  290. package/dist/lib/docs.js +142 -0
  291. package/dist/lib/env.d.ts +11 -0
  292. package/dist/lib/env.d.ts.map +1 -0
  293. package/dist/lib/env.js +12 -0
  294. package/dist/lib/exec.d.ts +32 -0
  295. package/dist/lib/exec.d.ts.map +1 -0
  296. package/dist/lib/exec.js +54 -0
  297. package/dist/lib/format.d.ts +29 -0
  298. package/dist/lib/format.d.ts.map +1 -0
  299. package/dist/lib/format.js +139 -0
  300. package/dist/lib/http/client.d.ts +56 -0
  301. package/dist/lib/http/client.d.ts.map +1 -0
  302. package/dist/lib/http/client.js +160 -0
  303. package/dist/lib/http/index.d.ts +2 -0
  304. package/dist/lib/http/index.d.ts.map +1 -0
  305. package/dist/lib/http/index.js +1 -0
  306. package/dist/lib/identities/index.d.ts +77 -0
  307. package/dist/lib/identities/index.d.ts.map +1 -0
  308. package/dist/lib/identities/index.js +190 -0
  309. package/dist/lib/machine.d.ts +19 -0
  310. package/dist/lib/machine.d.ts.map +1 -0
  311. package/dist/lib/machine.js +61 -0
  312. package/dist/lib/presence.d.ts +48 -0
  313. package/dist/lib/presence.d.ts.map +1 -0
  314. package/dist/lib/presence.js +123 -0
  315. package/dist/lib/readability/client.d.ts +39 -0
  316. package/dist/lib/readability/client.d.ts.map +1 -0
  317. package/dist/lib/readability/client.js +121 -0
  318. package/dist/lib/readability/index.d.ts +2 -0
  319. package/dist/lib/readability/index.d.ts.map +1 -0
  320. package/dist/lib/readability/index.js +1 -0
  321. package/dist/lib/scratch/index.d.ts +74 -0
  322. package/dist/lib/scratch/index.d.ts.map +1 -0
  323. package/dist/lib/scratch/index.js +393 -0
  324. package/dist/lib/tunnel/gate.d.ts +12 -0
  325. package/dist/lib/tunnel/gate.d.ts.map +1 -0
  326. package/dist/lib/tunnel/gate.js +101 -0
  327. package/dist/lib/tunnel/state.d.ts +34 -0
  328. package/dist/lib/tunnel/state.d.ts.map +1 -0
  329. package/dist/lib/tunnel/state.js +132 -0
  330. package/package.json +160 -8
  331. package/schemas/.gitkeep +0 -0
  332. package/schemas/config.schema.json +109 -0
  333. package/src/cli.ts +22 -0
  334. package/src/commander.ts +242 -0
  335. package/src/commands/.gitkeep +0 -0
  336. package/src/commands/agents.ts +4567 -0
  337. package/src/commands/backup.ts +305 -0
  338. package/src/commands/browse-ai.ts +198 -0
  339. package/src/commands/browse.ts +849 -0
  340. package/src/commands/callers.ts +363 -0
  341. package/src/commands/completion.ts +193 -0
  342. package/src/commands/config-get.ts +161 -0
  343. package/src/commands/context.ts +209 -0
  344. package/src/commands/cookies.ts +198 -0
  345. package/src/commands/docs.ts +174 -0
  346. package/src/commands/doctor.ts +231 -0
  347. package/src/commands/edit-batch.ts +233 -0
  348. package/src/commands/eml.ts +519 -0
  349. package/src/commands/env.ts +254 -0
  350. package/src/commands/fetch.ts +136 -0
  351. package/src/commands/file-history.ts +202 -0
  352. package/src/commands/grep.ts +371 -0
  353. package/src/commands/init.ts +335 -0
  354. package/src/commands/outline.ts +583 -0
  355. package/src/commands/presence.ts +152 -0
  356. package/src/commands/read.ts +64 -0
  357. package/src/commands/scratch.ts +445 -0
  358. package/src/commands/session.ts +187 -0
  359. package/src/commands/sync.ts +306 -0
  360. package/src/commands/toc.ts +218 -0
  361. package/src/commands/tokens.ts +79 -0
  362. package/src/commands/tunnel.ts +633 -0
  363. package/src/commands/uninstall.ts +144 -0
  364. package/src/commands/web.ts +193 -0
  365. package/src/core/agents/canonical-emit.ts +77 -0
  366. package/src/core/agents/cli-emit.ts +64 -0
  367. package/src/core/agents/cli.ts +838 -0
  368. package/src/core/agents/codex-replay.ts +163 -0
  369. package/src/core/agents/coord-client.ts +249 -0
  370. package/src/core/agents/events/consume.ts +196 -0
  371. package/src/core/agents/events/emit.ts +108 -0
  372. package/src/core/agents/events/ulid.ts +51 -0
  373. package/src/core/agents/index.ts +14 -0
  374. package/src/core/agents/paths.ts +16 -0
  375. package/src/core/agents/render/prompt-context.ts +401 -0
  376. package/src/core/agents/render/session-context.ts +341 -0
  377. package/src/core/agents/rules/claim-conflict.ts +282 -0
  378. package/src/core/agents/rules/commit-conflict.ts +303 -0
  379. package/src/core/agents/rules/stop-hook.ts +229 -0
  380. package/src/core/agents/session-events.ts +228 -0
  381. package/src/core/agents/state/activity-log.ts +33 -0
  382. package/src/core/agents/state/council.ts +265 -0
  383. package/src/core/agents/state/heartbeat-projector.ts +488 -0
  384. package/src/core/agents/state/heartbeat-writer.ts +333 -0
  385. package/src/core/agents/state/names.ts +399 -0
  386. package/src/core/agents/state/pidmap.ts +38 -0
  387. package/src/core/agents/state/scratch.ts +121 -0
  388. package/src/core/agents/state/shell-mutation.ts +44 -0
  389. package/src/core/agents/state/stale-sweep.ts +190 -0
  390. package/src/core/config.ts +111 -0
  391. package/src/core/hooks/cli.ts +1247 -0
  392. package/src/core/hooks/effects/image-capture.ts +330 -0
  393. package/src/core/hooks/effects/index.ts +210 -0
  394. package/src/core/hooks/events/emit.ts +120 -0
  395. package/src/core/hooks/events/schema.ts +430 -0
  396. package/src/core/hooks/events/ulid.ts +51 -0
  397. package/src/core/hooks/harness/detect.ts +30 -0
  398. package/src/core/hooks/harness/events.ts +102 -0
  399. package/src/core/hooks/harness/output.ts +100 -0
  400. package/src/core/hooks/harness/parse.ts +180 -0
  401. package/src/core/hooks/index.ts +16 -0
  402. package/src/core/hooks/resolve/anchor.ts +51 -0
  403. package/src/core/hooks/resolve/coord-root.ts +25 -0
  404. package/src/core/hooks/resolve/intent.ts +89 -0
  405. package/src/core/hooks/resolve/owner.ts +140 -0
  406. package/src/core/hooks/resolve/transcript.ts +72 -0
  407. package/src/hooks/.gitkeep +0 -0
  408. package/src/index.ts +15 -0
  409. package/src/lib/agent-browser/client.ts +239 -0
  410. package/src/lib/agent-browser/index.ts +1 -0
  411. package/src/lib/browser/client.ts +449 -0
  412. package/src/lib/browser/dev-overlay.ts +207 -0
  413. package/src/lib/browser/index.ts +24 -0
  414. package/src/lib/browser/layout.ts +288 -0
  415. package/src/lib/browser/visibility.ts +419 -0
  416. package/src/lib/browser/visual-diff.ts +150 -0
  417. package/src/lib/completion/bash.ts +291 -0
  418. package/src/lib/completion/fish.ts +134 -0
  419. package/src/lib/completion/index.ts +10 -0
  420. package/src/lib/completion/walk.ts +184 -0
  421. package/src/lib/completion/zsh.ts +262 -0
  422. package/src/lib/context/index.ts +386 -0
  423. package/src/lib/cookies/client.ts +301 -0
  424. package/src/lib/cookies/index.ts +13 -0
  425. package/src/lib/council/index.ts +803 -0
  426. package/src/lib/docs-index.ts +216 -0
  427. package/src/lib/docs-lint.ts +413 -0
  428. package/src/lib/docs-sweep.ts +348 -0
  429. package/src/lib/docs.ts +199 -0
  430. package/src/lib/env.ts +12 -0
  431. package/src/lib/exec.ts +74 -0
  432. package/src/lib/format.ts +147 -0
  433. package/src/lib/http/client.ts +211 -0
  434. package/src/lib/http/index.ts +1 -0
  435. package/src/lib/identities/index.ts +210 -0
  436. package/src/lib/machine.ts +61 -0
  437. package/src/lib/presence.ts +154 -0
  438. package/src/lib/readability/client.ts +169 -0
  439. package/src/lib/readability/index.ts +5 -0
  440. package/src/lib/readability/turndown-plugin-gfm.d.ts +10 -0
  441. package/src/lib/scratch/index.ts +470 -0
  442. package/src/lib/tunnel/gate.ts +113 -0
  443. package/src/lib/tunnel/state.ts +167 -0
  444. package/src/web/.gitkeep +0 -0
  445. package/index.js +0 -1
@@ -0,0 +1,393 @@
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ import { monorepoRoot, readHeartbeat, resolveOwner } from "../../core/agents/index.js";
4
+ import { resolveMachineLabel } from "../machine.js";
5
+ /**
6
+ * Agent scratchpad: per-agent markdown journal at `.harnery/scratch/<instance_id>.md`.
7
+ *
8
+ * Rules:
9
+ * - Single writer (the agent itself, via this lib). Peers read but never write.
10
+ * - Append-only, newest entry first. Atomic temp-file + rename on each write.
11
+ * - Strict entry-header format: `## <Chicago datetime> · <category>`. The
12
+ * linter enforces this so format violations are impossible at the write path.
13
+ * - 50 KB hard cap; auto-prunes oldest entries when exceeded.
14
+ * - SessionEnd hook archives to `.harnery/scratch/archived/<instance_id>-<ts>.md`;
15
+ * SessionStart janitor deletes archives older than 7 days + surfaces the
16
+ * most-recent archive as a recovery cue.
17
+ */
18
+ export const SCRATCH_CATEGORIES = [
19
+ "note",
20
+ "plan",
21
+ "decision",
22
+ "blocker",
23
+ "question",
24
+ "done",
25
+ "handoff",
26
+ ];
27
+ export const MAX_SCRATCH_BYTES = 50 * 1024;
28
+ export const WARN_SCRATCH_BYTES = 40 * 1024;
29
+ export const ARCHIVE_RETENTION_DAYS = 7;
30
+ /** Entry-header regex: `## 2026-05-15 1:48 AM CDT · note` */
31
+ export const ENTRY_HEADER_RE = /^## (\d{4}-\d{2}-\d{2}) (\d{1,2}):(\d{2}) (AM|PM) (CDT|CST) · (note|plan|decision|blocker|question|done|handoff)$/;
32
+ /** First line of every scratchpad file: this prefix followed by the agent name. */
33
+ export const SCRATCHPAD_HEADER_PREFIX = "# Scratchpad: agent-";
34
+ // ─── Paths ────────────────────────────────────────────────────────────────
35
+ export function scratchDir() {
36
+ const root = monorepoRoot();
37
+ if (!root)
38
+ throw new Error("Not in a coord-aware repo (coord_root() returned null).");
39
+ const dir = resolve(root, ".harnery", "scratch");
40
+ mkdirSync(dir, { recursive: true });
41
+ return dir;
42
+ }
43
+ export function archiveDir() {
44
+ const dir = resolve(scratchDir(), "archived");
45
+ mkdirSync(dir, { recursive: true });
46
+ return dir;
47
+ }
48
+ export function scratchPath(instanceId) {
49
+ return resolve(scratchDir(), `${instanceId}.md`);
50
+ }
51
+ // ─── Chicago time formatting ──────────────────────────────────────────────
52
+ const CHICAGO_FORMATTER = new Intl.DateTimeFormat("en-US", {
53
+ year: "numeric",
54
+ month: "2-digit",
55
+ day: "2-digit",
56
+ hour: "numeric",
57
+ minute: "2-digit",
58
+ timeZoneName: "short",
59
+ hour12: true,
60
+ timeZone: "America/Chicago",
61
+ });
62
+ /** Format `Date` as `2026-05-15 1:48 AM CDT`, what we write into the file. */
63
+ export function formatChicago(d = new Date()) {
64
+ const parts = CHICAGO_FORMATTER.formatToParts(d);
65
+ const get = (t) => parts.find((p) => p.type === t)?.value ?? "";
66
+ const year = get("year");
67
+ const month = get("month");
68
+ const day = get("day");
69
+ const hour = get("hour");
70
+ const minute = get("minute");
71
+ const dayPeriod = get("dayPeriod");
72
+ const tz = get("timeZoneName");
73
+ return `${year}-${month}-${day} ${hour}:${minute} ${dayPeriod} ${tz}`;
74
+ }
75
+ // ─── Parse / serialize ────────────────────────────────────────────────────
76
+ /** Parse a scratchpad file (or empty content) into a structured doc. */
77
+ export function parseScratch(path, content) {
78
+ const lines = content.split("\n");
79
+ const header = {
80
+ name: "unknown",
81
+ session_id: "",
82
+ machine: "",
83
+ started: "",
84
+ last_updated: "",
85
+ };
86
+ const entries = [];
87
+ let i = 0;
88
+ // ── header ──
89
+ if (lines[i]?.startsWith(SCRATCHPAD_HEADER_PREFIX)) {
90
+ header.name = lines[i].replace(SCRATCHPAD_HEADER_PREFIX, "").trim();
91
+ i++;
92
+ }
93
+ for (; i < lines.length; i++) {
94
+ const line = lines[i];
95
+ if (line === "---") {
96
+ i++;
97
+ break;
98
+ }
99
+ const m = line.match(/^(session_id|machine|started|last_updated):\s*(.+?)\s*$/);
100
+ if (m) {
101
+ header[m[1]] = m[2].trim();
102
+ }
103
+ }
104
+ // ── entries ──
105
+ let currentHeader = null;
106
+ let bodyLines = [];
107
+ const flush = () => {
108
+ if (!currentHeader)
109
+ return;
110
+ const m = currentHeader.match(ENTRY_HEADER_RE);
111
+ if (!m) {
112
+ currentHeader = null;
113
+ bodyLines = [];
114
+ return;
115
+ }
116
+ const [, date, hour, minute, period, tz, category] = m;
117
+ entries.push({
118
+ ts_iso: chicagoToIso(date, hour, minute, period, tz),
119
+ ts_display: `${date} ${hour}:${minute} ${period} ${tz}`,
120
+ category: category,
121
+ body: bodyLines.join("\n").trim(),
122
+ });
123
+ currentHeader = null;
124
+ bodyLines = [];
125
+ };
126
+ for (; i < lines.length; i++) {
127
+ const line = lines[i];
128
+ if (line.startsWith("## ")) {
129
+ flush();
130
+ currentHeader = line;
131
+ }
132
+ else if (currentHeader) {
133
+ bodyLines.push(line);
134
+ }
135
+ }
136
+ flush();
137
+ return { path, header, entries, bytes: Buffer.byteLength(content, "utf8") };
138
+ }
139
+ /** Serialize a parsed doc back to markdown. */
140
+ export function serializeScratch(doc) {
141
+ const lines = [];
142
+ lines.push(`${SCRATCHPAD_HEADER_PREFIX}${doc.header.name}`);
143
+ if (doc.header.session_id)
144
+ lines.push(`session_id: ${doc.header.session_id}`);
145
+ if (doc.header.machine)
146
+ lines.push(`machine: ${doc.header.machine}`);
147
+ if (doc.header.started)
148
+ lines.push(`started: ${doc.header.started}`);
149
+ if (doc.header.last_updated)
150
+ lines.push(`last_updated: ${doc.header.last_updated}`);
151
+ lines.push("");
152
+ lines.push("---");
153
+ lines.push("");
154
+ for (const entry of doc.entries) {
155
+ lines.push(`## ${entry.ts_display} · ${entry.category}`);
156
+ if (entry.body.length > 0) {
157
+ lines.push(entry.body);
158
+ }
159
+ lines.push("");
160
+ }
161
+ return `${lines.join("\n").trimEnd()}\n`;
162
+ }
163
+ function chicagoToIso(date, hourStr, minute, period, tz) {
164
+ let hour = Number.parseInt(hourStr, 10);
165
+ if (period === "PM" && hour !== 12)
166
+ hour += 12;
167
+ if (period === "AM" && hour === 12)
168
+ hour = 0;
169
+ const offset = tz === "CDT" ? "-05:00" : "-06:00";
170
+ const hh = String(hour).padStart(2, "0");
171
+ return `${date}T${hh}:${minute}:00${offset}`;
172
+ }
173
+ export function lintScratch(content, byteCap = MAX_SCRATCH_BYTES) {
174
+ const issues = [];
175
+ const lines = content.split("\n");
176
+ // Frontmatter checks
177
+ if (!lines[0]?.startsWith(SCRATCHPAD_HEADER_PREFIX)) {
178
+ issues.push({ line: 1, message: `missing '${SCRATCHPAD_HEADER_PREFIX}<name>' header` });
179
+ }
180
+ for (const required of ["session_id:", "machine:", "started:"]) {
181
+ if (!lines.slice(0, 10).some((l) => l.startsWith(required))) {
182
+ issues.push({ line: 1, message: `missing frontmatter field '${required}'` });
183
+ }
184
+ }
185
+ // Entry header + chronological order checks
186
+ const entryTimes = [];
187
+ for (let i = 0; i < lines.length; i++) {
188
+ const line = lines[i];
189
+ if (!line.startsWith("## "))
190
+ continue;
191
+ if (!ENTRY_HEADER_RE.test(line)) {
192
+ issues.push({ line: i + 1, message: `malformed entry header: ${line}` });
193
+ continue;
194
+ }
195
+ const m = line.match(ENTRY_HEADER_RE);
196
+ const iso = chicagoToIso(m[1], m[2], m[3], m[4], m[5]);
197
+ const ts = Date.parse(iso);
198
+ if (Number.isFinite(ts))
199
+ entryTimes.push(ts);
200
+ }
201
+ for (let i = 1; i < entryTimes.length; i++) {
202
+ if (entryTimes[i] > entryTimes[i - 1]) {
203
+ issues.push({
204
+ line: 0,
205
+ message: "entries are not in chronological-descending order (newest first)",
206
+ });
207
+ break;
208
+ }
209
+ }
210
+ // CRLF + trailing whitespace
211
+ if (content.includes("\r\n")) {
212
+ issues.push({ line: 0, message: "CRLF line endings detected; LF required" });
213
+ }
214
+ for (let i = 0; i < lines.length; i++) {
215
+ if (/\s$/.test(lines[i]) && lines[i].length > 0) {
216
+ issues.push({ line: i + 1, message: "trailing whitespace" });
217
+ break;
218
+ }
219
+ }
220
+ // Size cap
221
+ const bytes = Buffer.byteLength(content, "utf8");
222
+ if (bytes > byteCap) {
223
+ issues.push({
224
+ line: 0,
225
+ message: `file size ${bytes} bytes exceeds ${byteCap}-byte cap`,
226
+ });
227
+ }
228
+ return issues;
229
+ }
230
+ // ─── Append + prune ───────────────────────────────────────────────────────
231
+ export function appendEntry(instanceId, category, body) {
232
+ const hb = readHeartbeat(instanceId);
233
+ const path = scratchPath(instanceId);
234
+ let doc;
235
+ if (existsSync(path)) {
236
+ doc = parseScratch(path, readFileSync(path, "utf8"));
237
+ }
238
+ else {
239
+ // Seed `started` from the heartbeat's session start so cross-agent writes
240
+ // (`harn agents ping`) don't stamp the peer's scratchpad with our wall-clock.
241
+ const startedSeed = hb?.started_at ? formatChicago(new Date(hb.started_at)) : formatChicago();
242
+ doc = {
243
+ path,
244
+ header: {
245
+ name: hb?.name ?? "unknown",
246
+ session_id: hb?.session_id ?? instanceId,
247
+ machine: resolveMachineLabel(),
248
+ started: startedSeed,
249
+ last_updated: "",
250
+ },
251
+ entries: [],
252
+ bytes: 0,
253
+ };
254
+ }
255
+ const now = new Date();
256
+ const entry = {
257
+ ts_iso: now.toISOString(),
258
+ ts_display: formatChicago(now),
259
+ category,
260
+ body: body.trim(),
261
+ };
262
+ doc.entries.unshift(entry);
263
+ doc.header.last_updated = entry.ts_display;
264
+ if (!doc.header.name || doc.header.name === "unknown") {
265
+ doc.header.name = hb?.name ?? doc.header.name;
266
+ }
267
+ // Prune from the tail until under cap.
268
+ let serialized = serializeScratch(doc);
269
+ let pruned = 0;
270
+ while (Buffer.byteLength(serialized, "utf8") > MAX_SCRATCH_BYTES && doc.entries.length > 1) {
271
+ doc.entries.pop();
272
+ pruned++;
273
+ serialized = serializeScratch(doc);
274
+ }
275
+ if (pruned > 0) {
276
+ const pruneEntry = {
277
+ ts_iso: new Date().toISOString(),
278
+ ts_display: formatChicago(),
279
+ category: "note",
280
+ body: `(auto-pruned ${pruned} oldest entries to stay under ${MAX_SCRATCH_BYTES}-byte cap)`,
281
+ };
282
+ doc.entries.unshift(pruneEntry);
283
+ serialized = serializeScratch(doc);
284
+ }
285
+ doc.bytes = Buffer.byteLength(serialized, "utf8");
286
+ // Atomic write
287
+ const tmpPath = `${path}.tmp.${process.pid}`;
288
+ mkdirSync(dirname(path), { recursive: true });
289
+ writeFileSync(tmpPath, serialized, "utf8");
290
+ renameSync(tmpPath, path);
291
+ return doc;
292
+ }
293
+ // ─── Archive lifecycle ────────────────────────────────────────────────────
294
+ /** Move `<owner>.md` → `archived/<owner>-<ts>.md`. No-op if missing. Returns archive path (or null). */
295
+ export function archiveScratch(instanceId) {
296
+ const src = scratchPath(instanceId);
297
+ if (!existsSync(src))
298
+ return null;
299
+ const ts = new Date().toISOString().replace(/[:.]/g, "-");
300
+ const dest = resolve(archiveDir(), `${instanceId}-${ts}.md`);
301
+ renameSync(src, dest);
302
+ return dest;
303
+ }
304
+ /** Delete archives older than `days`. Returns number deleted. */
305
+ export function pruneArchives(days = ARCHIVE_RETENTION_DAYS) {
306
+ const cutoff = Date.now() - days * 24 * 3600 * 1000;
307
+ const dir = archiveDir();
308
+ let deleted = 0;
309
+ for (const f of readdirSync(dir)) {
310
+ if (!f.endsWith(".md"))
311
+ continue;
312
+ const fp = resolve(dir, f);
313
+ try {
314
+ const s = statSync(fp);
315
+ if (s.mtimeMs < cutoff) {
316
+ unlinkSync(fp);
317
+ deleted++;
318
+ }
319
+ }
320
+ catch {
321
+ // skip
322
+ }
323
+ }
324
+ return deleted;
325
+ }
326
+ /** Sweep `.harnery/scratch/<owner>.md` files whose corresponding heartbeat is gone. */
327
+ export function sweepOrphanScratchpads() {
328
+ const dir = scratchDir();
329
+ const archived = [];
330
+ for (const f of readdirSync(dir)) {
331
+ if (!f.endsWith(".md"))
332
+ continue;
333
+ const instanceId = f.replace(/\.md$/, "");
334
+ const hb = readHeartbeat(instanceId);
335
+ if (!hb) {
336
+ const dest = archiveScratch(instanceId);
337
+ if (dest)
338
+ archived.push(dest);
339
+ }
340
+ }
341
+ return archived;
342
+ }
343
+ /** List archive files sorted by mtime descending. */
344
+ export function listArchives() {
345
+ const dir = archiveDir();
346
+ if (!existsSync(dir))
347
+ return [];
348
+ return readdirSync(dir)
349
+ .filter((f) => f.endsWith(".md"))
350
+ .map((f) => {
351
+ const fp = resolve(dir, f);
352
+ const s = statSync(fp);
353
+ return { path: fp, basename: f, mtimeMs: s.mtimeMs, bytes: s.size };
354
+ })
355
+ .sort((a, b) => b.mtimeMs - a.mtimeMs);
356
+ }
357
+ // ─── Helpers ──────────────────────────────────────────────────────────────
358
+ export function currentOwnerOrThrow() {
359
+ const owner = resolveOwner();
360
+ if (!owner) {
361
+ throw new Error("Not in an agent session; ppid walk found no pid-map entry.");
362
+ }
363
+ return owner;
364
+ }
365
+ export function loadScratch(instanceId) {
366
+ const path = scratchPath(instanceId);
367
+ if (!existsSync(path))
368
+ return null;
369
+ return parseScratch(path, readFileSync(path, "utf8"));
370
+ }
371
+ /** Resolve an agent name → instance_id by walking active heartbeats (case-insensitive). */
372
+ export function resolveOwnerByName(name) {
373
+ const root = monorepoRoot();
374
+ if (!root)
375
+ return null;
376
+ const activeDir = resolve(root, ".harnery", "active");
377
+ if (!existsSync(activeDir))
378
+ return null;
379
+ for (const f of readdirSync(activeDir)) {
380
+ if (!f.endsWith(".json"))
381
+ continue;
382
+ try {
383
+ const hb = JSON.parse(readFileSync(resolve(activeDir, f), "utf8"));
384
+ if ((hb.name ?? "").toLowerCase() === name.toLowerCase()) {
385
+ return hb.instance_id ?? null;
386
+ }
387
+ }
388
+ catch {
389
+ // skip
390
+ }
391
+ }
392
+ return null;
393
+ }
@@ -0,0 +1,12 @@
1
+ declare function argvFlag(flag: string): string | undefined;
2
+ declare const ALLOW: Set<string>;
3
+ declare const TARGET: string;
4
+ declare const VHOST: string;
5
+ declare const PORT: number;
6
+ declare const UPSTREAM_HTTP: string;
7
+ declare const UPSTREAM_WS: string;
8
+ interface WsData {
9
+ path: string;
10
+ }
11
+ declare const server: Bun.Server<WsData>;
12
+ //# sourceMappingURL=gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.d.ts","sourceRoot":"","sources":["../../../src/lib/tunnel/gate.ts"],"names":[],"mappings":"AAgBA,iBAAS,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGlD;AAED,QAAA,MAAM,KAAK,aAKV,CAAC;AACF,QAAA,MAAM,MAAM,QAAwD,CAAC;AACrE,QAAA,MAAM,KAAK,QAAkD,CAAC;AAC9D,QAAA,MAAM,IAAI,QAA0E,CAAC;AAErF,QAAA,MAAM,aAAa,QAAqB,CAAC;AACzC,QAAA,MAAM,WAAW,QAAmB,CAAC;AAErC,UAAU,MAAM;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,QAAA,MAAM,MAAM,oBAoEV,CAAC"}
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ // Reverse-proxy worker spawned by `tunnel up`. Listens on 127.0.0.1:<port>,
3
+ // checks CF-Connecting-IP against an allowlist passed via env, and forwards
4
+ // HTTP + WebSocket requests to an upstream with a Host header rewrite and
5
+ // Content-Encoding stripped (Bun's fetch auto-decompresses the body but
6
+ // retains the encoding header, which breaks browser decoding downstream).
7
+ //
8
+ // Runs detached, outside the CLI command framework; no command context is
9
+ // available; stdout/stderr is captured into .cache/tunnel/gate.log by the
10
+ // spawner.
11
+ // `--port`/`--name` are also passed on argv (not just env) so the gate's port
12
+ // and instance name show up in its process command line. That's what lets
13
+ // `tunnel down` scope its stray-process sweep to a single instance via
14
+ // `pgrep -f`: every instance runs the same `bun run gate.ts`, so the port in
15
+ // the command line is the only thing that distinguishes them. Env stays the
16
+ // source of truth; argv is read only as a fallback/marker.
17
+ function argvFlag(flag) {
18
+ const i = process.argv.indexOf(flag);
19
+ return i !== -1 ? process.argv[i + 1] : undefined;
20
+ }
21
+ const ALLOW = new Set((process.env.HARNERY_TUNNEL_ALLOW ?? "")
22
+ .split(",")
23
+ .map((s) => s.trim())
24
+ .filter(Boolean));
25
+ const TARGET = process.env.HARNERY_TUNNEL_TARGET ?? "127.0.0.1:8001";
26
+ const VHOST = process.env.HARNERY_TUNNEL_VHOST ?? "localhost";
27
+ const PORT = Number(process.env.HARNERY_TUNNEL_PORT ?? argvFlag("--port") ?? "9001");
28
+ const UPSTREAM_HTTP = `http://${TARGET}`;
29
+ const UPSTREAM_WS = `ws://${TARGET}`;
30
+ const server = Bun.serve({
31
+ port: PORT,
32
+ hostname: "127.0.0.1",
33
+ // Bun.serve defaults idleTimeout to 10s. LLM SSE streams commonly take
34
+ // longer than that between events (first-token latency + tool-call
35
+ // pauses), so 10s would kill in-flight chats over the tunnel. 255s is
36
+ // Bun's max, and beyond any realistic single-turn delay.
37
+ idleTimeout: 255,
38
+ async fetch(req, server) {
39
+ const ip = req.headers.get("cf-connecting-ip") ?? "";
40
+ if (!ALLOW.has(ip)) {
41
+ return new Response("403 Forbidden\n", { status: 403 });
42
+ }
43
+ const url = new URL(req.url);
44
+ if (req.headers.get("upgrade")?.toLowerCase() === "websocket") {
45
+ if (server.upgrade(req, { data: { path: url.pathname + url.search } })) {
46
+ return undefined;
47
+ }
48
+ return new Response("WS upgrade failed\n", { status: 500 });
49
+ }
50
+ const headers = new Headers(req.headers);
51
+ headers.set("host", VHOST);
52
+ headers.set("accept-encoding", "identity");
53
+ headers.delete("connection");
54
+ const init = {
55
+ method: req.method,
56
+ headers,
57
+ redirect: "manual",
58
+ };
59
+ if (!["GET", "HEAD"].includes(req.method)) {
60
+ init.body = await req.arrayBuffer();
61
+ }
62
+ const resp = await fetch(UPSTREAM_HTTP + url.pathname + url.search, init);
63
+ const respHeaders = new Headers(resp.headers);
64
+ respHeaders.delete("content-encoding");
65
+ respHeaders.delete("content-length");
66
+ return new Response(resp.body, {
67
+ status: resp.status,
68
+ statusText: resp.statusText,
69
+ headers: respHeaders,
70
+ });
71
+ },
72
+ websocket: {
73
+ open(ws) {
74
+ const { path } = ws.data;
75
+ // Bun's WebSocket constructor accepts a `headers` option at runtime
76
+ // even though the standard lib.dom.d.ts WebSocket type doesn't.
77
+ const upstream = new WebSocket(UPSTREAM_WS + path, {
78
+ headers: { host: VHOST },
79
+ });
80
+ upstream.binaryType = "arraybuffer";
81
+ ws.upstream = upstream;
82
+ upstream.onmessage = (ev) => ws.send(ev.data);
83
+ upstream.onerror = () => ws.close();
84
+ upstream.onclose = () => ws.close();
85
+ },
86
+ message(ws, msg) {
87
+ const u = ws.upstream;
88
+ if (u && u.readyState === 1)
89
+ u.send(msg);
90
+ },
91
+ close(ws) {
92
+ const u = ws.upstream;
93
+ u?.close();
94
+ },
95
+ },
96
+ });
97
+ // This worker runs detached via `bun run gate.ts`, outside the CLI command
98
+ // framework, so no AsyncLocalStorage context is available. stdout/stderr is
99
+ // captured into .cache/tunnel/gate.log by the spawning command.
100
+ console.log(`bp-tunnel-gate :${server.port} -> ${UPSTREAM_HTTP} (Host: ${VHOST})`); // lint-ok-emission: detached worker, see file note above
101
+ console.log(`allow: ${[...ALLOW].join(", ") || "(empty, denies all)"}`); // lint-ok-emission: detached worker, see file note above
@@ -0,0 +1,34 @@
1
+ /** The default instance name when `--name` is omitted. */
2
+ export declare const DEFAULT_INSTANCE = "default";
3
+ export declare function gateLogFile(name: string): string;
4
+ export declare function cfdLogFile(name: string): string;
5
+ export interface TunnelConfig {
6
+ allowed_ips: string[];
7
+ }
8
+ export interface TunnelState {
9
+ name: string;
10
+ url: string;
11
+ gate_pid: number;
12
+ cloudflared_pid: number;
13
+ started_at: string;
14
+ target: string;
15
+ vhost: string;
16
+ gate_port: number;
17
+ }
18
+ export declare function readConfig(): TunnelConfig;
19
+ export declare function writeConfig(cfg: TunnelConfig): void;
20
+ export declare function readState(name?: string): TunnelState | null;
21
+ export declare function writeState(state: TunnelState): void;
22
+ export declare function clearState(name?: string): void;
23
+ /**
24
+ * Every persisted tunnel instance, newest-started first. Reads every
25
+ * `state*.json` under `.cache/tunnel/`; tolerates missing/corrupt files.
26
+ */
27
+ export declare function listStates(): TunnelState[];
28
+ export declare function isProcessAlive(pid: number): boolean;
29
+ /**
30
+ * Ensure cloudflared is on PATH or installed at ~/.local/bin/cloudflared.
31
+ * Auto-downloads on Linux; throws on other platforms with brew hint.
32
+ */
33
+ export declare function ensureCloudflared(): string;
34
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/lib/tunnel/state.ts"],"names":[],"mappings":"AAuBA,0DAA0D;AAC1D,eAAO,MAAM,gBAAgB,YAAY,CAAC;AAW1C,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAgBD,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAOD,wBAAgB,UAAU,IAAI,YAAY,CAWzC;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAEnD;AAED,wBAAgB,SAAS,CAAC,IAAI,GAAE,MAAyB,GAAG,WAAW,GAAG,IAAI,CAQ7E;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAEnD;AAED,wBAAgB,UAAU,CAAC,IAAI,GAAE,MAAyB,GAAG,IAAI,CAGhE;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,WAAW,EAAE,CAW1C;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOnD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAuB1C"}
@@ -0,0 +1,132 @@
1
+ // Tunnel config + state persistence + cloudflared-install helper. Lives
2
+ // under <cwd>/.cache/tunnel/; gitignored, so the allowlist is per-machine.
3
+ import { execSync } from "node:child_process";
4
+ import { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync, } from "node:fs";
5
+ import { resolve } from "node:path";
6
+ // Inline cachePath: tunnel state under <cwd>/.cache/tunnel/.
7
+ function cachePath(tool, filename) {
8
+ const dir = resolve(process.cwd(), ".cache", tool);
9
+ if (!existsSync(dir))
10
+ mkdirSync(dir, { recursive: true });
11
+ return resolve(dir, filename);
12
+ }
13
+ const CONFIG_FILE = "config.json";
14
+ /** The default instance name when `--name` is omitted. */
15
+ export const DEFAULT_INSTANCE = "default";
16
+ // Per-instance file naming. The default instance keeps the original
17
+ // unsuffixed filenames (`state.json`, `gate.log`, `cloudflared.log`) so a
18
+ // pre-multi-instance tunnel keeps working untouched across the upgrade; named
19
+ // instances get a `-<name>` suffix. Names are validated upstream (tunnel.ts)
20
+ // to `[a-z0-9][a-z0-9-]*`, so they're always safe as filename fragments.
21
+ function stateFile(name) {
22
+ return name === DEFAULT_INSTANCE ? "state.json" : `state-${name}.json`;
23
+ }
24
+ export function gateLogFile(name) {
25
+ return name === DEFAULT_INSTANCE ? "gate.log" : `gate-${name}.log`;
26
+ }
27
+ export function cfdLogFile(name) {
28
+ return name === DEFAULT_INSTANCE ? "cloudflared.log" : `cloudflared-${name}.log`;
29
+ }
30
+ /** Map a state filename back to its instance name (inverse of stateFile). */
31
+ function nameFromStateFile(file) {
32
+ if (file === "state.json")
33
+ return DEFAULT_INSTANCE;
34
+ const m = file.match(/^state-(.+)\.json$/);
35
+ return m ? m[1] : null;
36
+ }
37
+ // Empty by default; `tunnel up` refuses to start until the operator adds
38
+ // their own IP (`tunnel allow add <ip>`). No address is baked in, so the
39
+ // public package ships nobody's IP.
40
+ const DEFAULT_CONFIG = {
41
+ allowed_ips: [],
42
+ };
43
+ /** Normalize a parsed state blob; supply `name` for pre-multi-instance files. */
44
+ function normalizeState(raw, fallbackName) {
45
+ return { ...raw, name: raw.name ?? fallbackName };
46
+ }
47
+ export function readConfig() {
48
+ const p = cachePath("tunnel", CONFIG_FILE);
49
+ if (!existsSync(p)) {
50
+ writeConfig(DEFAULT_CONFIG);
51
+ return { ...DEFAULT_CONFIG, allowed_ips: [...DEFAULT_CONFIG.allowed_ips] };
52
+ }
53
+ try {
54
+ return JSON.parse(readFileSync(p, "utf-8"));
55
+ }
56
+ catch {
57
+ return { ...DEFAULT_CONFIG, allowed_ips: [...DEFAULT_CONFIG.allowed_ips] };
58
+ }
59
+ }
60
+ export function writeConfig(cfg) {
61
+ writeFileSync(cachePath("tunnel", CONFIG_FILE), JSON.stringify(cfg, null, 2));
62
+ }
63
+ export function readState(name = DEFAULT_INSTANCE) {
64
+ const p = cachePath("tunnel", stateFile(name));
65
+ if (!existsSync(p))
66
+ return null;
67
+ try {
68
+ return normalizeState(JSON.parse(readFileSync(p, "utf-8")), name);
69
+ }
70
+ catch {
71
+ return null;
72
+ }
73
+ }
74
+ export function writeState(state) {
75
+ writeFileSync(cachePath("tunnel", stateFile(state.name)), JSON.stringify(state, null, 2));
76
+ }
77
+ export function clearState(name = DEFAULT_INSTANCE) {
78
+ const p = cachePath("tunnel", stateFile(name));
79
+ if (existsSync(p))
80
+ unlinkSync(p);
81
+ }
82
+ /**
83
+ * Every persisted tunnel instance, newest-started first. Reads every
84
+ * `state*.json` under `.cache/tunnel/`; tolerates missing/corrupt files.
85
+ */
86
+ export function listStates() {
87
+ const dir = resolve(process.cwd(), ".cache", "tunnel");
88
+ if (!existsSync(dir))
89
+ return [];
90
+ const out = [];
91
+ for (const file of readdirSync(dir)) {
92
+ const name = nameFromStateFile(file);
93
+ if (name === null)
94
+ continue;
95
+ const state = readState(name);
96
+ if (state)
97
+ out.push(state);
98
+ }
99
+ return out.sort((a, b) => (b.started_at ?? "").localeCompare(a.started_at ?? ""));
100
+ }
101
+ export function isProcessAlive(pid) {
102
+ try {
103
+ process.kill(pid, 0);
104
+ return true;
105
+ }
106
+ catch {
107
+ return false;
108
+ }
109
+ }
110
+ /**
111
+ * Ensure cloudflared is on PATH or installed at ~/.local/bin/cloudflared.
112
+ * Auto-downloads on Linux; throws on other platforms with brew hint.
113
+ */
114
+ export function ensureCloudflared() {
115
+ try {
116
+ execSync("command -v cloudflared", { stdio: "ignore" });
117
+ return "cloudflared";
118
+ }
119
+ catch {
120
+ /* not on PATH; fall through */
121
+ }
122
+ const local = `${process.env.HOME}/.local/bin/cloudflared`;
123
+ if (existsSync(local))
124
+ return local;
125
+ if (process.platform !== "linux") {
126
+ throw new Error("cloudflared not installed. On macOS: `brew install cloudflared`. " +
127
+ "On Linux it auto-installs to ~/.local/bin/cloudflared.");
128
+ }
129
+ process.stderr.write("Installing cloudflared to ~/.local/bin/...\n"); // lint-ok-emission: sync setup phase before structured output; pairs with the inherited stdio of the curl below
130
+ execSync(`curl -fsSL https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o ${local} && chmod +x ${local}`, { stdio: "inherit" });
131
+ return local;
132
+ }