happy2 0.0.5

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 (373) hide show
  1. package/README.md +82 -0
  2. package/package.json +74 -0
  3. package/packages/happy2-server/Dockerfile +18 -0
  4. package/packages/happy2-server/README.md +418 -0
  5. package/packages/happy2-server/bin/happy2.js +3 -0
  6. package/packages/happy2-server/dist/cli.d.ts +3 -0
  7. package/packages/happy2-server/dist/cli.d.ts.map +1 -0
  8. package/packages/happy2-server/dist/cli.js +44 -0
  9. package/packages/happy2-server/dist/cli.js.map +1 -0
  10. package/packages/happy2-server/dist/index.d.ts +24 -0
  11. package/packages/happy2-server/dist/index.d.ts.map +1 -0
  12. package/packages/happy2-server/dist/index.js +22 -0
  13. package/packages/happy2-server/dist/index.js.map +1 -0
  14. package/packages/happy2-server/dist/modules/agents/builtin-images.d.ts +8 -0
  15. package/packages/happy2-server/dist/modules/agents/builtin-images.d.ts.map +1 -0
  16. package/packages/happy2-server/dist/modules/agents/builtin-images.js +283 -0
  17. package/packages/happy2-server/dist/modules/agents/builtin-images.js.map +1 -0
  18. package/packages/happy2-server/dist/modules/agents/command.d.ts +3 -0
  19. package/packages/happy2-server/dist/modules/agents/command.d.ts.map +1 -0
  20. package/packages/happy2-server/dist/modules/agents/command.js +7 -0
  21. package/packages/happy2-server/dist/modules/agents/command.js.map +1 -0
  22. package/packages/happy2-server/dist/modules/agents/daemon.d.ts +106 -0
  23. package/packages/happy2-server/dist/modules/agents/daemon.d.ts.map +1 -0
  24. package/packages/happy2-server/dist/modules/agents/daemon.js +522 -0
  25. package/packages/happy2-server/dist/modules/agents/daemon.js.map +1 -0
  26. package/packages/happy2-server/dist/modules/agents/docker.d.ts +51 -0
  27. package/packages/happy2-server/dist/modules/agents/docker.d.ts.map +1 -0
  28. package/packages/happy2-server/dist/modules/agents/docker.js +256 -0
  29. package/packages/happy2-server/dist/modules/agents/docker.js.map +1 -0
  30. package/packages/happy2-server/dist/modules/agents/docker.test.d.ts +2 -0
  31. package/packages/happy2-server/dist/modules/agents/docker.test.d.ts.map +1 -0
  32. package/packages/happy2-server/dist/modules/agents/docker.test.js +155 -0
  33. package/packages/happy2-server/dist/modules/agents/docker.test.js.map +1 -0
  34. package/packages/happy2-server/dist/modules/agents/index.d.ts +5 -0
  35. package/packages/happy2-server/dist/modules/agents/index.d.ts.map +1 -0
  36. package/packages/happy2-server/dist/modules/agents/index.js +4 -0
  37. package/packages/happy2-server/dist/modules/agents/index.js.map +1 -0
  38. package/packages/happy2-server/dist/modules/agents/service.d.ts +140 -0
  39. package/packages/happy2-server/dist/modules/agents/service.d.ts.map +1 -0
  40. package/packages/happy2-server/dist/modules/agents/service.js +1283 -0
  41. package/packages/happy2-server/dist/modules/agents/service.js.map +1 -0
  42. package/packages/happy2-server/dist/modules/auth/cloudflare-access.d.ts +15 -0
  43. package/packages/happy2-server/dist/modules/auth/cloudflare-access.d.ts.map +1 -0
  44. package/packages/happy2-server/dist/modules/auth/cloudflare-access.js +49 -0
  45. package/packages/happy2-server/dist/modules/auth/cloudflare-access.js.map +1 -0
  46. package/packages/happy2-server/dist/modules/auth/crypto.d.ts +4 -0
  47. package/packages/happy2-server/dist/modules/auth/crypto.d.ts.map +1 -0
  48. package/packages/happy2-server/dist/modules/auth/crypto.js +31 -0
  49. package/packages/happy2-server/dist/modules/auth/crypto.js.map +1 -0
  50. package/packages/happy2-server/dist/modules/auth/crypto.test.d.ts +2 -0
  51. package/packages/happy2-server/dist/modules/auth/crypto.test.d.ts.map +1 -0
  52. package/packages/happy2-server/dist/modules/auth/crypto.test.js +11 -0
  53. package/packages/happy2-server/dist/modules/auth/crypto.test.js.map +1 -0
  54. package/packages/happy2-server/dist/modules/auth/email.d.ts +3 -0
  55. package/packages/happy2-server/dist/modules/auth/email.d.ts.map +1 -0
  56. package/packages/happy2-server/dist/modules/auth/email.js +22 -0
  57. package/packages/happy2-server/dist/modules/auth/email.js.map +1 -0
  58. package/packages/happy2-server/dist/modules/auth/metadata.d.ts +5 -0
  59. package/packages/happy2-server/dist/modules/auth/metadata.d.ts.map +1 -0
  60. package/packages/happy2-server/dist/modules/auth/metadata.js +28 -0
  61. package/packages/happy2-server/dist/modules/auth/metadata.js.map +1 -0
  62. package/packages/happy2-server/dist/modules/auth/metadata.test.d.ts +2 -0
  63. package/packages/happy2-server/dist/modules/auth/metadata.test.d.ts.map +1 -0
  64. package/packages/happy2-server/dist/modules/auth/metadata.test.js +10 -0
  65. package/packages/happy2-server/dist/modules/auth/metadata.test.js.map +1 -0
  66. package/packages/happy2-server/dist/modules/auth/methods.d.ts +10 -0
  67. package/packages/happy2-server/dist/modules/auth/methods.d.ts.map +1 -0
  68. package/packages/happy2-server/dist/modules/auth/methods.js +21 -0
  69. package/packages/happy2-server/dist/modules/auth/methods.js.map +1 -0
  70. package/packages/happy2-server/dist/modules/auth/methods.test.d.ts +2 -0
  71. package/packages/happy2-server/dist/modules/auth/methods.test.d.ts.map +1 -0
  72. package/packages/happy2-server/dist/modules/auth/methods.test.js +16 -0
  73. package/packages/happy2-server/dist/modules/auth/methods.test.js.map +1 -0
  74. package/packages/happy2-server/dist/modules/auth/oidc.d.ts +9 -0
  75. package/packages/happy2-server/dist/modules/auth/oidc.d.ts.map +1 -0
  76. package/packages/happy2-server/dist/modules/auth/oidc.js +68 -0
  77. package/packages/happy2-server/dist/modules/auth/oidc.js.map +1 -0
  78. package/packages/happy2-server/dist/modules/auth/service.d.ts +44 -0
  79. package/packages/happy2-server/dist/modules/auth/service.d.ts.map +1 -0
  80. package/packages/happy2-server/dist/modules/auth/service.js +208 -0
  81. package/packages/happy2-server/dist/modules/auth/service.js.map +1 -0
  82. package/packages/happy2-server/dist/modules/auth/tokens.d.ts +21 -0
  83. package/packages/happy2-server/dist/modules/auth/tokens.d.ts.map +1 -0
  84. package/packages/happy2-server/dist/modules/auth/tokens.js +79 -0
  85. package/packages/happy2-server/dist/modules/auth/tokens.js.map +1 -0
  86. package/packages/happy2-server/dist/modules/automation/repository.d.ts +127 -0
  87. package/packages/happy2-server/dist/modules/automation/repository.d.ts.map +1 -0
  88. package/packages/happy2-server/dist/modules/automation/repository.js +821 -0
  89. package/packages/happy2-server/dist/modules/automation/repository.js.map +1 -0
  90. package/packages/happy2-server/dist/modules/collaboration/repository.d.ts +839 -0
  91. package/packages/happy2-server/dist/modules/collaboration/repository.d.ts.map +1 -0
  92. package/packages/happy2-server/dist/modules/collaboration/repository.js +5795 -0
  93. package/packages/happy2-server/dist/modules/collaboration/repository.js.map +1 -0
  94. package/packages/happy2-server/dist/modules/collaboration/repository.test.d.ts +2 -0
  95. package/packages/happy2-server/dist/modules/collaboration/repository.test.d.ts.map +1 -0
  96. package/packages/happy2-server/dist/modules/collaboration/repository.test.js +620 -0
  97. package/packages/happy2-server/dist/modules/collaboration/repository.test.js.map +1 -0
  98. package/packages/happy2-server/dist/modules/collaboration/types.d.ts +243 -0
  99. package/packages/happy2-server/dist/modules/collaboration/types.d.ts.map +1 -0
  100. package/packages/happy2-server/dist/modules/collaboration/types.js +8 -0
  101. package/packages/happy2-server/dist/modules/collaboration/types.js.map +1 -0
  102. package/packages/happy2-server/dist/modules/config/defaults.d.ts +8 -0
  103. package/packages/happy2-server/dist/modules/config/defaults.d.ts.map +1 -0
  104. package/packages/happy2-server/dist/modules/config/defaults.js +70 -0
  105. package/packages/happy2-server/dist/modules/config/defaults.js.map +1 -0
  106. package/packages/happy2-server/dist/modules/config/defaults.test.d.ts +2 -0
  107. package/packages/happy2-server/dist/modules/config/defaults.test.d.ts.map +1 -0
  108. package/packages/happy2-server/dist/modules/config/defaults.test.js +27 -0
  109. package/packages/happy2-server/dist/modules/config/defaults.test.js.map +1 -0
  110. package/packages/happy2-server/dist/modules/config/environment.d.ts +4 -0
  111. package/packages/happy2-server/dist/modules/config/environment.d.ts.map +1 -0
  112. package/packages/happy2-server/dist/modules/config/environment.js +92 -0
  113. package/packages/happy2-server/dist/modules/config/environment.js.map +1 -0
  114. package/packages/happy2-server/dist/modules/config/loader.d.ts +5 -0
  115. package/packages/happy2-server/dist/modules/config/loader.d.ts.map +1 -0
  116. package/packages/happy2-server/dist/modules/config/loader.js +251 -0
  117. package/packages/happy2-server/dist/modules/config/loader.js.map +1 -0
  118. package/packages/happy2-server/dist/modules/config/loader.test.d.ts +2 -0
  119. package/packages/happy2-server/dist/modules/config/loader.test.d.ts.map +1 -0
  120. package/packages/happy2-server/dist/modules/config/loader.test.js +108 -0
  121. package/packages/happy2-server/dist/modules/config/loader.test.js.map +1 -0
  122. package/packages/happy2-server/dist/modules/config/paths.d.ts +7 -0
  123. package/packages/happy2-server/dist/modules/config/paths.d.ts.map +1 -0
  124. package/packages/happy2-server/dist/modules/config/paths.js +15 -0
  125. package/packages/happy2-server/dist/modules/config/paths.js.map +1 -0
  126. package/packages/happy2-server/dist/modules/config/paths.test.d.ts +2 -0
  127. package/packages/happy2-server/dist/modules/config/paths.test.d.ts.map +1 -0
  128. package/packages/happy2-server/dist/modules/config/paths.test.js +22 -0
  129. package/packages/happy2-server/dist/modules/config/paths.test.js.map +1 -0
  130. package/packages/happy2-server/dist/modules/config/runtime.d.ts +6 -0
  131. package/packages/happy2-server/dist/modules/config/runtime.d.ts.map +1 -0
  132. package/packages/happy2-server/dist/modules/config/runtime.js +14 -0
  133. package/packages/happy2-server/dist/modules/config/runtime.js.map +1 -0
  134. package/packages/happy2-server/dist/modules/config/type.d.ts +88 -0
  135. package/packages/happy2-server/dist/modules/config/type.d.ts.map +1 -0
  136. package/packages/happy2-server/dist/modules/config/type.js +2 -0
  137. package/packages/happy2-server/dist/modules/config/type.js.map +1 -0
  138. package/packages/happy2-server/dist/modules/database.d.ts +105 -0
  139. package/packages/happy2-server/dist/modules/database.d.ts.map +1 -0
  140. package/packages/happy2-server/dist/modules/database.js +583 -0
  141. package/packages/happy2-server/dist/modules/database.js.map +1 -0
  142. package/packages/happy2-server/dist/modules/drizzle.d.ts +11 -0
  143. package/packages/happy2-server/dist/modules/drizzle.d.ts.map +1 -0
  144. package/packages/happy2-server/dist/modules/drizzle.js +31 -0
  145. package/packages/happy2-server/dist/modules/drizzle.js.map +1 -0
  146. package/packages/happy2-server/dist/modules/files/lock.d.ts +3 -0
  147. package/packages/happy2-server/dist/modules/files/lock.d.ts.map +1 -0
  148. package/packages/happy2-server/dist/modules/files/lock.js +83 -0
  149. package/packages/happy2-server/dist/modules/files/lock.js.map +1 -0
  150. package/packages/happy2-server/dist/modules/files/media.d.ts +18 -0
  151. package/packages/happy2-server/dist/modules/files/media.d.ts.map +1 -0
  152. package/packages/happy2-server/dist/modules/files/media.js +352 -0
  153. package/packages/happy2-server/dist/modules/files/media.js.map +1 -0
  154. package/packages/happy2-server/dist/modules/files/provider.d.ts +130 -0
  155. package/packages/happy2-server/dist/modules/files/provider.d.ts.map +1 -0
  156. package/packages/happy2-server/dist/modules/files/provider.js +448 -0
  157. package/packages/happy2-server/dist/modules/files/provider.js.map +1 -0
  158. package/packages/happy2-server/dist/modules/files/quota.d.ts +58 -0
  159. package/packages/happy2-server/dist/modules/files/quota.d.ts.map +1 -0
  160. package/packages/happy2-server/dist/modules/files/quota.js +180 -0
  161. package/packages/happy2-server/dist/modules/files/quota.js.map +1 -0
  162. package/packages/happy2-server/dist/modules/files/routes.test.d.ts +2 -0
  163. package/packages/happy2-server/dist/modules/files/routes.test.d.ts.map +1 -0
  164. package/packages/happy2-server/dist/modules/files/routes.test.js +163 -0
  165. package/packages/happy2-server/dist/modules/files/routes.test.js.map +1 -0
  166. package/packages/happy2-server/dist/modules/files/scanner.d.ts +27 -0
  167. package/packages/happy2-server/dist/modules/files/scanner.d.ts.map +1 -0
  168. package/packages/happy2-server/dist/modules/files/scanner.js +66 -0
  169. package/packages/happy2-server/dist/modules/files/scanner.js.map +1 -0
  170. package/packages/happy2-server/dist/modules/files/storage.d.ts +95 -0
  171. package/packages/happy2-server/dist/modules/files/storage.d.ts.map +1 -0
  172. package/packages/happy2-server/dist/modules/files/storage.js +855 -0
  173. package/packages/happy2-server/dist/modules/files/storage.js.map +1 -0
  174. package/packages/happy2-server/dist/modules/files/storage.test.d.ts +2 -0
  175. package/packages/happy2-server/dist/modules/files/storage.test.d.ts.map +1 -0
  176. package/packages/happy2-server/dist/modules/files/storage.test.js +170 -0
  177. package/packages/happy2-server/dist/modules/files/storage.test.js.map +1 -0
  178. package/packages/happy2-server/dist/modules/integrations/index.d.ts +6 -0
  179. package/packages/happy2-server/dist/modules/integrations/index.d.ts.map +1 -0
  180. package/packages/happy2-server/dist/modules/integrations/index.js +6 -0
  181. package/packages/happy2-server/dist/modules/integrations/index.js.map +1 -0
  182. package/packages/happy2-server/dist/modules/integrations/repository.d.ts +136 -0
  183. package/packages/happy2-server/dist/modules/integrations/repository.d.ts.map +1 -0
  184. package/packages/happy2-server/dist/modules/integrations/repository.js +1229 -0
  185. package/packages/happy2-server/dist/modules/integrations/repository.js.map +1 -0
  186. package/packages/happy2-server/dist/modules/integrations/repository.test.d.ts +2 -0
  187. package/packages/happy2-server/dist/modules/integrations/repository.test.d.ts.map +1 -0
  188. package/packages/happy2-server/dist/modules/integrations/repository.test.js +345 -0
  189. package/packages/happy2-server/dist/modules/integrations/repository.test.js.map +1 -0
  190. package/packages/happy2-server/dist/modules/integrations/routes.test.d.ts +2 -0
  191. package/packages/happy2-server/dist/modules/integrations/routes.test.d.ts.map +1 -0
  192. package/packages/happy2-server/dist/modules/integrations/routes.test.js +161 -0
  193. package/packages/happy2-server/dist/modules/integrations/routes.test.js.map +1 -0
  194. package/packages/happy2-server/dist/modules/integrations/secrets.d.ts +19 -0
  195. package/packages/happy2-server/dist/modules/integrations/secrets.d.ts.map +1 -0
  196. package/packages/happy2-server/dist/modules/integrations/secrets.js +69 -0
  197. package/packages/happy2-server/dist/modules/integrations/secrets.js.map +1 -0
  198. package/packages/happy2-server/dist/modules/integrations/ssrf.d.ts +32 -0
  199. package/packages/happy2-server/dist/modules/integrations/ssrf.d.ts.map +1 -0
  200. package/packages/happy2-server/dist/modules/integrations/ssrf.js +185 -0
  201. package/packages/happy2-server/dist/modules/integrations/ssrf.js.map +1 -0
  202. package/packages/happy2-server/dist/modules/integrations/ssrf.test.d.ts +2 -0
  203. package/packages/happy2-server/dist/modules/integrations/ssrf.test.d.ts.map +1 -0
  204. package/packages/happy2-server/dist/modules/integrations/ssrf.test.js +56 -0
  205. package/packages/happy2-server/dist/modules/integrations/ssrf.test.js.map +1 -0
  206. package/packages/happy2-server/dist/modules/integrations/transport.d.ts +21 -0
  207. package/packages/happy2-server/dist/modules/integrations/transport.d.ts.map +1 -0
  208. package/packages/happy2-server/dist/modules/integrations/transport.js +108 -0
  209. package/packages/happy2-server/dist/modules/integrations/transport.js.map +1 -0
  210. package/packages/happy2-server/dist/modules/integrations/transport.test.d.ts +2 -0
  211. package/packages/happy2-server/dist/modules/integrations/transport.test.d.ts.map +1 -0
  212. package/packages/happy2-server/dist/modules/integrations/transport.test.js +66 -0
  213. package/packages/happy2-server/dist/modules/integrations/transport.test.js.map +1 -0
  214. package/packages/happy2-server/dist/modules/integrations/types.d.ts +155 -0
  215. package/packages/happy2-server/dist/modules/integrations/types.d.ts.map +1 -0
  216. package/packages/happy2-server/dist/modules/integrations/types.js +26 -0
  217. package/packages/happy2-server/dist/modules/integrations/types.js.map +1 -0
  218. package/packages/happy2-server/dist/modules/operations/export-worker.d.ts +16 -0
  219. package/packages/happy2-server/dist/modules/operations/export-worker.d.ts.map +1 -0
  220. package/packages/happy2-server/dist/modules/operations/export-worker.js +56 -0
  221. package/packages/happy2-server/dist/modules/operations/export-worker.js.map +1 -0
  222. package/packages/happy2-server/dist/modules/operations/repository.d.ts +215 -0
  223. package/packages/happy2-server/dist/modules/operations/repository.d.ts.map +1 -0
  224. package/packages/happy2-server/dist/modules/operations/repository.js +2011 -0
  225. package/packages/happy2-server/dist/modules/operations/repository.js.map +1 -0
  226. package/packages/happy2-server/dist/modules/operations/repository.test.d.ts +2 -0
  227. package/packages/happy2-server/dist/modules/operations/repository.test.d.ts.map +1 -0
  228. package/packages/happy2-server/dist/modules/operations/repository.test.js +610 -0
  229. package/packages/happy2-server/dist/modules/operations/repository.test.js.map +1 -0
  230. package/packages/happy2-server/dist/modules/operations/types.d.ts +144 -0
  231. package/packages/happy2-server/dist/modules/operations/types.d.ts.map +1 -0
  232. package/packages/happy2-server/dist/modules/operations/types.js +9 -0
  233. package/packages/happy2-server/dist/modules/operations/types.js.map +1 -0
  234. package/packages/happy2-server/dist/modules/realtime/events.d.ts +99 -0
  235. package/packages/happy2-server/dist/modules/realtime/events.d.ts.map +1 -0
  236. package/packages/happy2-server/dist/modules/realtime/events.js +173 -0
  237. package/packages/happy2-server/dist/modules/realtime/events.js.map +1 -0
  238. package/packages/happy2-server/dist/modules/realtime/index.d.ts +4 -0
  239. package/packages/happy2-server/dist/modules/realtime/index.d.ts.map +1 -0
  240. package/packages/happy2-server/dist/modules/realtime/index.js +4 -0
  241. package/packages/happy2-server/dist/modules/realtime/index.js.map +1 -0
  242. package/packages/happy2-server/dist/modules/realtime/local.d.ts +37 -0
  243. package/packages/happy2-server/dist/modules/realtime/local.d.ts.map +1 -0
  244. package/packages/happy2-server/dist/modules/realtime/local.js +181 -0
  245. package/packages/happy2-server/dist/modules/realtime/local.js.map +1 -0
  246. package/packages/happy2-server/dist/modules/realtime/local.test.d.ts +2 -0
  247. package/packages/happy2-server/dist/modules/realtime/local.test.d.ts.map +1 -0
  248. package/packages/happy2-server/dist/modules/realtime/local.test.js +186 -0
  249. package/packages/happy2-server/dist/modules/realtime/local.test.js.map +1 -0
  250. package/packages/happy2-server/dist/modules/realtime/pubsub.d.ts +26 -0
  251. package/packages/happy2-server/dist/modules/realtime/pubsub.d.ts.map +1 -0
  252. package/packages/happy2-server/dist/modules/realtime/pubsub.js +7 -0
  253. package/packages/happy2-server/dist/modules/realtime/pubsub.js.map +1 -0
  254. package/packages/happy2-server/dist/modules/request/database-idempotency.d.ts +15 -0
  255. package/packages/happy2-server/dist/modules/request/database-idempotency.d.ts.map +1 -0
  256. package/packages/happy2-server/dist/modules/request/database-idempotency.js +141 -0
  257. package/packages/happy2-server/dist/modules/request/database-idempotency.js.map +1 -0
  258. package/packages/happy2-server/dist/modules/request/idempotency-hook.d.ts +12 -0
  259. package/packages/happy2-server/dist/modules/request/idempotency-hook.d.ts.map +1 -0
  260. package/packages/happy2-server/dist/modules/request/idempotency-hook.js +98 -0
  261. package/packages/happy2-server/dist/modules/request/idempotency-hook.js.map +1 -0
  262. package/packages/happy2-server/dist/modules/request/idempotency.d.ts +146 -0
  263. package/packages/happy2-server/dist/modules/request/idempotency.d.ts.map +1 -0
  264. package/packages/happy2-server/dist/modules/request/idempotency.js +312 -0
  265. package/packages/happy2-server/dist/modules/request/idempotency.js.map +1 -0
  266. package/packages/happy2-server/dist/modules/request/idempotency.test.d.ts +2 -0
  267. package/packages/happy2-server/dist/modules/request/idempotency.test.d.ts.map +1 -0
  268. package/packages/happy2-server/dist/modules/request/idempotency.test.js +191 -0
  269. package/packages/happy2-server/dist/modules/request/idempotency.test.js.map +1 -0
  270. package/packages/happy2-server/dist/modules/request/index.d.ts +5 -0
  271. package/packages/happy2-server/dist/modules/request/index.d.ts.map +1 -0
  272. package/packages/happy2-server/dist/modules/request/index.js +5 -0
  273. package/packages/happy2-server/dist/modules/request/index.js.map +1 -0
  274. package/packages/happy2-server/dist/modules/request/rate-limit.d.ts +100 -0
  275. package/packages/happy2-server/dist/modules/request/rate-limit.d.ts.map +1 -0
  276. package/packages/happy2-server/dist/modules/request/rate-limit.js +239 -0
  277. package/packages/happy2-server/dist/modules/request/rate-limit.js.map +1 -0
  278. package/packages/happy2-server/dist/modules/request/rate-limit.test.d.ts +2 -0
  279. package/packages/happy2-server/dist/modules/request/rate-limit.test.d.ts.map +1 -0
  280. package/packages/happy2-server/dist/modules/request/rate-limit.test.js +123 -0
  281. package/packages/happy2-server/dist/modules/request/rate-limit.test.js.map +1 -0
  282. package/packages/happy2-server/dist/modules/schema.d.ts +31454 -0
  283. package/packages/happy2-server/dist/modules/schema.d.ts.map +1 -0
  284. package/packages/happy2-server/dist/modules/schema.js +1081 -0
  285. package/packages/happy2-server/dist/modules/schema.js.map +1 -0
  286. package/packages/happy2-server/dist/modules/workspace/index.d.ts +3 -0
  287. package/packages/happy2-server/dist/modules/workspace/index.d.ts.map +1 -0
  288. package/packages/happy2-server/dist/modules/workspace/index.js +3 -0
  289. package/packages/happy2-server/dist/modules/workspace/index.js.map +1 -0
  290. package/packages/happy2-server/dist/modules/workspace/service.d.ts +54 -0
  291. package/packages/happy2-server/dist/modules/workspace/service.d.ts.map +1 -0
  292. package/packages/happy2-server/dist/modules/workspace/service.js +997 -0
  293. package/packages/happy2-server/dist/modules/workspace/service.js.map +1 -0
  294. package/packages/happy2-server/dist/modules/workspace/types.d.ts +58 -0
  295. package/packages/happy2-server/dist/modules/workspace/types.d.ts.map +1 -0
  296. package/packages/happy2-server/dist/modules/workspace/types.js +11 -0
  297. package/packages/happy2-server/dist/modules/workspace/types.js.map +1 -0
  298. package/packages/happy2-server/dist/routes/agents.d.ts +5 -0
  299. package/packages/happy2-server/dist/routes/agents.d.ts.map +1 -0
  300. package/packages/happy2-server/dist/routes/agents.js +225 -0
  301. package/packages/happy2-server/dist/routes/agents.js.map +1 -0
  302. package/packages/happy2-server/dist/routes/auth.d.ts +5 -0
  303. package/packages/happy2-server/dist/routes/auth.d.ts.map +1 -0
  304. package/packages/happy2-server/dist/routes/auth.js +128 -0
  305. package/packages/happy2-server/dist/routes/auth.js.map +1 -0
  306. package/packages/happy2-server/dist/routes/automation.d.ts +6 -0
  307. package/packages/happy2-server/dist/routes/automation.d.ts.map +1 -0
  308. package/packages/happy2-server/dist/routes/automation.js +315 -0
  309. package/packages/happy2-server/dist/routes/automation.js.map +1 -0
  310. package/packages/happy2-server/dist/routes/basic.d.ts +3 -0
  311. package/packages/happy2-server/dist/routes/basic.d.ts.map +1 -0
  312. package/packages/happy2-server/dist/routes/basic.js +5 -0
  313. package/packages/happy2-server/dist/routes/basic.js.map +1 -0
  314. package/packages/happy2-server/dist/routes/collaboration.d.ts +7 -0
  315. package/packages/happy2-server/dist/routes/collaboration.d.ts.map +1 -0
  316. package/packages/happy2-server/dist/routes/collaboration.js +1285 -0
  317. package/packages/happy2-server/dist/routes/collaboration.js.map +1 -0
  318. package/packages/happy2-server/dist/routes/files.d.ts +9 -0
  319. package/packages/happy2-server/dist/routes/files.d.ts.map +1 -0
  320. package/packages/happy2-server/dist/routes/files.js +397 -0
  321. package/packages/happy2-server/dist/routes/files.js.map +1 -0
  322. package/packages/happy2-server/dist/routes/integrations.d.ts +6 -0
  323. package/packages/happy2-server/dist/routes/integrations.d.ts.map +1 -0
  324. package/packages/happy2-server/dist/routes/integrations.js +486 -0
  325. package/packages/happy2-server/dist/routes/integrations.js.map +1 -0
  326. package/packages/happy2-server/dist/routes/operations.d.ts +5 -0
  327. package/packages/happy2-server/dist/routes/operations.d.ts.map +1 -0
  328. package/packages/happy2-server/dist/routes/operations.js +588 -0
  329. package/packages/happy2-server/dist/routes/operations.js.map +1 -0
  330. package/packages/happy2-server/dist/routes/operations.test.d.ts +2 -0
  331. package/packages/happy2-server/dist/routes/operations.test.d.ts.map +1 -0
  332. package/packages/happy2-server/dist/routes/operations.test.js +106 -0
  333. package/packages/happy2-server/dist/routes/operations.test.js.map +1 -0
  334. package/packages/happy2-server/dist/routes/sync.d.ts +6 -0
  335. package/packages/happy2-server/dist/routes/sync.d.ts.map +1 -0
  336. package/packages/happy2-server/dist/routes/sync.js +549 -0
  337. package/packages/happy2-server/dist/routes/sync.js.map +1 -0
  338. package/packages/happy2-server/dist/routes/workspace.d.ts +5 -0
  339. package/packages/happy2-server/dist/routes/workspace.d.ts.map +1 -0
  340. package/packages/happy2-server/dist/routes/workspace.js +240 -0
  341. package/packages/happy2-server/dist/routes/workspace.js.map +1 -0
  342. package/packages/happy2-server/dist/runner.d.ts +3 -0
  343. package/packages/happy2-server/dist/runner.d.ts.map +1 -0
  344. package/packages/happy2-server/dist/runner.js +30 -0
  345. package/packages/happy2-server/dist/runner.js.map +1 -0
  346. package/packages/happy2-server/dist/server.d.ts +32 -0
  347. package/packages/happy2-server/dist/server.d.ts.map +1 -0
  348. package/packages/happy2-server/dist/server.js +284 -0
  349. package/packages/happy2-server/dist/server.js.map +1 -0
  350. package/packages/happy2-server/dist/standalone.d.ts +13 -0
  351. package/packages/happy2-server/dist/standalone.d.ts.map +1 -0
  352. package/packages/happy2-server/dist/standalone.js +99 -0
  353. package/packages/happy2-server/dist/standalone.js.map +1 -0
  354. package/packages/happy2-server/dist/web/assets/figtree-latin-wght-normal-D_ZTVpCC.woff2 +0 -0
  355. package/packages/happy2-server/dist/web/assets/happy-otter-Q2O_g-DT.png +0 -0
  356. package/packages/happy2-server/dist/web/assets/index-COvbX8JP.css +2 -0
  357. package/packages/happy2-server/dist/web/assets/index-DdpugxMO.js +57 -0
  358. package/packages/happy2-server/dist/web/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  359. package/packages/happy2-server/dist/web/assets/onboarding-CRUpq9xl.png +0 -0
  360. package/packages/happy2-server/dist/web/index.html +13 -0
  361. package/packages/happy2-server/drizzle/0000_initial.sql +13 -0
  362. package/packages/happy2-server/drizzle/0001_accounts_profiles_files.sql +42 -0
  363. package/packages/happy2-server/drizzle/0002_file_visibility_and_uploader.sql +5 -0
  364. package/packages/happy2-server/drizzle/0003_collaboration.sql +565 -0
  365. package/packages/happy2-server/drizzle/0004_collaboration_expansion.sql +1099 -0
  366. package/packages/happy2-server/drizzle/0005_ai_agents.sql +85 -0
  367. package/packages/happy2-server/drizzle/0006_rig_global_event_queue.sql +10 -0
  368. package/packages/happy2-server/drizzle/0007_rename_server_identity.sql +3 -0
  369. package/packages/happy2-server/drizzle/0008_agent_message_streaming.sql +47 -0
  370. package/packages/happy2-server/drizzle/0009_chat_workspace_binding_lookup.sql +2 -0
  371. package/packages/happy2-server/drizzle/0010_rig_secret_assignments.sql +21 -0
  372. package/packages/happy2-server/drizzle/meta/_journal.json +83 -0
  373. package/packages/happy2-server/happy2.example.toml +104 -0
@@ -0,0 +1,997 @@
1
+ import { execFile } from "node:child_process";
2
+ import { createHash, randomUUID } from "node:crypto";
3
+ import { constants, watch } from "node:fs";
4
+ import { link, lstat, open, readdir, realpath, rename, unlink, } from "node:fs/promises";
5
+ import { dirname, join, sep } from "node:path";
6
+ import { realtimeTopics } from "../realtime/index.js";
7
+ import { WorkspaceError, } from "./types.js";
8
+ const CHANGE_DEBOUNCE_MS = 20;
9
+ const DEFAULT_DIRECTORY_PAGE_LIMIT = 250;
10
+ const MAX_DIRECTORY_PAGE_LIMIT = 1_000;
11
+ const DIRECTORY_PAGE_PATH_BYTES = 128 * 1024;
12
+ const GIT_MAX_BUFFER_BYTES = 4 * 1024 * 1024;
13
+ const GIT_TIMEOUT_MS = 5_000;
14
+ const MAX_CACHED_DIRECTORY_ENTRIES = 20_000;
15
+ const MAX_EAGER_DIRECTORY_CHILDREN = 400;
16
+ const MAX_PRELOAD_DIRECTORIES = 128;
17
+ const MAX_PRELOAD_DEPTH = 3;
18
+ const MAX_PRELOAD_PATHS = 2_500;
19
+ const MAX_PRELOAD_PATH_BYTES = 192 * 1024;
20
+ const MAX_WARM_WORKSPACE_INDEXES = 8;
21
+ export const MAX_WORKSPACE_TEXT_FILE_BYTES = 4 * 1024 * 1024;
22
+ /**
23
+ * These directories remain visible but collapsed during adaptive preload. They
24
+ * are ordinary pageable directories after that; none are hidden from the API.
25
+ */
26
+ export const DEFAULT_DEFERRED_WORKSPACE_DIRECTORIES = Object.freeze([
27
+ ".git",
28
+ ".next",
29
+ ".pnpm",
30
+ ".turbo",
31
+ ".yarn",
32
+ ".cache",
33
+ "build",
34
+ "coverage",
35
+ "dist",
36
+ "node_modules",
37
+ "target",
38
+ "vendor",
39
+ ]);
40
+ const defaultDeferredDirectories = new Set(DEFAULT_DEFERRED_WORKSPACE_DIRECTORIES);
41
+ export class WorkspaceService {
42
+ repository;
43
+ pubsub;
44
+ onError;
45
+ indexes = new Map();
46
+ indexCreations = new Map();
47
+ warmIndexes = new Map();
48
+ indexesByChat = new Map();
49
+ closed = false;
50
+ constructor(repository, pubsub, onError = () => undefined) {
51
+ this.repository = repository;
52
+ this.pubsub = pubsub;
53
+ this.onError = onError;
54
+ }
55
+ async getSnapshot(userId, chatId) {
56
+ const index = await this.authorizedIndex(userId, chatId);
57
+ return index.preload();
58
+ }
59
+ async getDirectory(input) {
60
+ const index = await this.authorizedIndex(input.userId, input.chatId);
61
+ return index.getDirectory({
62
+ directory: canonicalDirectory(input.directory),
63
+ cursor: input.cursor,
64
+ limit: input.limit ?? DEFAULT_DIRECTORY_PAGE_LIMIT,
65
+ });
66
+ }
67
+ async getFile(input) {
68
+ const index = await this.authorizedIndex(input.userId, input.chatId);
69
+ return index.getFile(canonicalFilePath(input.path));
70
+ }
71
+ async writeFile(input) {
72
+ const index = await this.authorizedIndex(input.userId, input.chatId);
73
+ return index.writeFile({
74
+ path: canonicalFilePath(input.path),
75
+ expectedVersion: input.expectedVersion,
76
+ content: input.content,
77
+ patch: input.patch,
78
+ });
79
+ }
80
+ async deleteFile(input) {
81
+ const index = await this.authorizedIndex(input.userId, input.chatId);
82
+ return index.deleteFile({
83
+ path: canonicalFilePath(input.path),
84
+ expectedVersion: input.expectedVersion,
85
+ });
86
+ }
87
+ async close() {
88
+ if (this.closed)
89
+ return;
90
+ this.closed = true;
91
+ const creating = [...this.indexCreations.values()];
92
+ const existing = [...this.indexes.values()];
93
+ const created = await Promise.all(creating.map((creation) => creation.catch(() => undefined)));
94
+ await Promise.all([...new Set([...existing, ...created.filter((index) => index !== undefined)])].map((index) => index.close()));
95
+ this.indexes.clear();
96
+ this.indexCreations.clear();
97
+ this.warmIndexes.clear();
98
+ this.indexesByChat.clear();
99
+ }
100
+ async authorizedIndex(userId, chatId) {
101
+ if (this.closed)
102
+ throw new Error("Workspace service is closed");
103
+ const binding = await this.repository.getChatWorkspaceBinding(userId, chatId);
104
+ const root = await workspaceRoot(binding.cwd);
105
+ const existing = this.indexes.get(root);
106
+ if (existing) {
107
+ this.attachChat(binding.chatId, existing);
108
+ this.touchWarmIndex(root, existing);
109
+ existing.ensureWatcher();
110
+ existing.warmUp();
111
+ this.coolOldIndexes();
112
+ return existing;
113
+ }
114
+ const pending = this.indexCreations.get(root);
115
+ if (pending) {
116
+ const index = await pending;
117
+ this.attachChat(binding.chatId, index);
118
+ this.touchWarmIndex(root, index);
119
+ index.ensureWatcher();
120
+ index.warmUp();
121
+ this.coolOldIndexes();
122
+ return index;
123
+ }
124
+ const creation = (async () => {
125
+ const index = new WorkspaceIndex(root, this.pubsub, this.onError);
126
+ index.attachChat(binding.chatId);
127
+ await index.start();
128
+ if (this.closed) {
129
+ await index.close();
130
+ throw new Error("Workspace service is closed");
131
+ }
132
+ this.indexes.set(root, index);
133
+ return index;
134
+ })();
135
+ this.indexCreations.set(root, creation);
136
+ try {
137
+ const index = await creation;
138
+ this.attachChat(binding.chatId, index);
139
+ this.touchWarmIndex(root, index);
140
+ this.coolOldIndexes();
141
+ return index;
142
+ }
143
+ finally {
144
+ this.indexCreations.delete(root);
145
+ }
146
+ }
147
+ attachChat(chatId, index) {
148
+ const previous = this.indexesByChat.get(chatId);
149
+ if (previous && previous !== index)
150
+ previous.detachChat(chatId);
151
+ index.attachChat(chatId);
152
+ this.indexesByChat.set(chatId, index);
153
+ }
154
+ touchWarmIndex(root, index) {
155
+ this.warmIndexes.delete(root);
156
+ this.warmIndexes.set(root, index);
157
+ }
158
+ coolOldIndexes() {
159
+ while (this.warmIndexes.size > MAX_WARM_WORKSPACE_INDEXES) {
160
+ const oldest = this.warmIndexes.entries().next().value;
161
+ if (!oldest)
162
+ return;
163
+ this.warmIndexes.delete(oldest[0]);
164
+ oldest[1].cool();
165
+ }
166
+ }
167
+ }
168
+ class WorkspaceIndex {
169
+ root;
170
+ pubsub;
171
+ onError;
172
+ directoryCache = new Map();
173
+ deletedByDirectory = new Map();
174
+ gitStatusByPath = new Map();
175
+ cachedEntryCount = 0;
176
+ changeTimer;
177
+ closed = false;
178
+ gitGeneration = 0;
179
+ gitRefreshAgain = false;
180
+ gitRefreshTask;
181
+ gitStatusPending = true;
182
+ realRoot = "";
183
+ revisionId = randomUUID();
184
+ treeGeneration = 0;
185
+ watcher;
186
+ changedPaths = new Set();
187
+ unknownChange = false;
188
+ warm = true;
189
+ chatIds = new Set();
190
+ mutationTail = Promise.resolve();
191
+ constructor(root, pubsub, onError) {
192
+ this.root = root;
193
+ this.pubsub = pubsub;
194
+ this.onError = onError;
195
+ }
196
+ attachChat(chatId) {
197
+ this.chatIds.add(chatId);
198
+ }
199
+ detachChat(chatId) {
200
+ this.chatIds.delete(chatId);
201
+ }
202
+ async start() {
203
+ this.realRoot = await realpath(this.root);
204
+ this.ensureWatcher();
205
+ this.requestGitRefresh();
206
+ }
207
+ ensureWatcher() {
208
+ if (this.closed || this.watcher)
209
+ return;
210
+ const watcher = watch(this.root, { recursive: true }, (_eventType, filename) => {
211
+ this.queueChange(filename === null ? undefined : String(filename));
212
+ });
213
+ watcher.unref();
214
+ watcher.on("error", (error) => {
215
+ if (this.closed || this.watcher !== watcher)
216
+ return;
217
+ this.watcher = undefined;
218
+ watcher.close();
219
+ this.onError(error);
220
+ this.unknownChange = true;
221
+ this.flushChanges();
222
+ });
223
+ this.watcher = watcher;
224
+ }
225
+ warmUp() {
226
+ if (this.closed || this.warm)
227
+ return;
228
+ this.warm = true;
229
+ this.gitStatusPending = true;
230
+ this.requestGitRefresh();
231
+ }
232
+ cool() {
233
+ if (this.closed || !this.warm)
234
+ return;
235
+ this.warm = false;
236
+ this.clearDirectoryCache();
237
+ this.deletedByDirectory.clear();
238
+ this.gitStatusByPath.clear();
239
+ this.gitGeneration += 1;
240
+ this.gitStatusPending = true;
241
+ }
242
+ async preload() {
243
+ this.ensureWatcher();
244
+ const paths = [];
245
+ const unloaded = new Set();
246
+ const queue = [{ depth: 0, directory: "" }];
247
+ let pathBytes = 0;
248
+ let directoriesRead = 0;
249
+ let stopped = false;
250
+ while (queue.length > 0 && !stopped) {
251
+ const current = queue.shift();
252
+ if (current.directory &&
253
+ defaultDeferredDirectories.has(directoryBasename(current.directory))) {
254
+ unloaded.add(current.directory);
255
+ continue;
256
+ }
257
+ if (directoriesRead >= MAX_PRELOAD_DIRECTORIES) {
258
+ unloaded.add(current.directory);
259
+ for (const queued of queue)
260
+ unloaded.add(queued.directory);
261
+ break;
262
+ }
263
+ let entries;
264
+ try {
265
+ entries = await this.entriesForDirectory(current.directory);
266
+ }
267
+ catch (error) {
268
+ if (error instanceof WorkspaceError && error.code === "not_found")
269
+ continue;
270
+ throw error;
271
+ }
272
+ directoriesRead += 1;
273
+ if (current.directory && entries.length > MAX_EAGER_DIRECTORY_CHILDREN) {
274
+ unloaded.add(current.directory);
275
+ continue;
276
+ }
277
+ for (const entry of entries) {
278
+ const nextBytes = encodedPathBytes(entry.path);
279
+ if (paths.length >= MAX_PRELOAD_PATHS ||
280
+ pathBytes + nextBytes > MAX_PRELOAD_PATH_BYTES) {
281
+ unloaded.add(current.directory);
282
+ for (const queued of queue)
283
+ unloaded.add(queued.directory);
284
+ stopped = true;
285
+ break;
286
+ }
287
+ paths.push(entry.path);
288
+ pathBytes += nextBytes;
289
+ if (!entry.isDirectory)
290
+ continue;
291
+ if (current.depth + 1 < MAX_PRELOAD_DEPTH)
292
+ queue.push({ directory: entry.path, depth: current.depth + 1 });
293
+ else
294
+ unloaded.add(entry.path);
295
+ }
296
+ }
297
+ return this.snapshot(paths, unloaded);
298
+ }
299
+ async getDirectory(input) {
300
+ this.ensureWatcher();
301
+ const cursor = input.cursor ? decodeCursor(input.cursor) : undefined;
302
+ if (cursor &&
303
+ (cursor.directory !== input.directory ||
304
+ cursor.treeGeneration !== this.treeGeneration ||
305
+ cursor.gitGeneration !== this.gitGeneration))
306
+ throw new WorkspaceError("stale_cursor", "Workspace changed while the directory was being paged");
307
+ const entries = await this.entriesForDirectory(input.directory);
308
+ let index = cursor
309
+ ? entries.findIndex((entry) => comparePaths(entry.name, cursor.after) > 0)
310
+ : 0;
311
+ if (index < 0)
312
+ index = entries.length;
313
+ const paths = [];
314
+ const unloaded = new Set();
315
+ let pathBytes = 0;
316
+ while (index < entries.length && paths.length < input.limit) {
317
+ const entry = entries[index];
318
+ const nextBytes = encodedPathBytes(entry.path);
319
+ if (paths.length > 0 && pathBytes + nextBytes > DIRECTORY_PAGE_PATH_BYTES)
320
+ break;
321
+ paths.push(entry.path);
322
+ pathBytes += nextBytes;
323
+ if (entry.isDirectory)
324
+ unloaded.add(entry.path);
325
+ index += 1;
326
+ }
327
+ const lastEntry = paths.length ? entries[index - 1] : undefined;
328
+ return {
329
+ ...this.snapshot(paths, unloaded),
330
+ directory: input.directory,
331
+ ...(index < entries.length && lastEntry
332
+ ? {
333
+ nextCursor: encodeCursor({
334
+ after: lastEntry.name,
335
+ directory: input.directory,
336
+ treeGeneration: this.treeGeneration,
337
+ gitGeneration: this.gitGeneration,
338
+ }),
339
+ }
340
+ : {}),
341
+ };
342
+ }
343
+ async getFile(path) {
344
+ this.ensureWatcher();
345
+ const file = await this.readTextFile(path);
346
+ return publicTextFile(file);
347
+ }
348
+ async writeFile(input) {
349
+ return this.mutate(async () => {
350
+ const current = await this.readTextFileIfPresent(input.path);
351
+ assertExpectedVersion(current, input.expectedVersion);
352
+ const nextContent = nextFileContent(current?.content ?? "", input);
353
+ assertTextFileSize(nextContent);
354
+ if (current?.content === nextContent)
355
+ return {
356
+ path: current.path,
357
+ size: current.size,
358
+ version: current.version,
359
+ created: false,
360
+ };
361
+ await this.replaceTextFile(input.path, nextContent, current);
362
+ const written = await this.readTextFileIfPresent(input.path);
363
+ if (!written || written.content !== nextContent)
364
+ throw workspaceConflict(written?.version ?? null);
365
+ return {
366
+ path: written.path,
367
+ size: written.size,
368
+ version: written.version,
369
+ created: current === undefined,
370
+ };
371
+ });
372
+ }
373
+ async deleteFile(input) {
374
+ return this.mutate(async () => {
375
+ const current = await this.readTextFileIfPresent(input.path);
376
+ assertExpectedVersion(current, input.expectedVersion);
377
+ const latest = await this.readTextFileIfPresent(input.path);
378
+ assertExpectedVersion(latest, input.expectedVersion);
379
+ try {
380
+ await unlink(latest.fullPath);
381
+ }
382
+ catch (error) {
383
+ if (isMissingPathError(error))
384
+ throw workspaceConflict(null);
385
+ throw error;
386
+ }
387
+ return { path: input.path, deletedVersion: input.expectedVersion };
388
+ });
389
+ }
390
+ async close() {
391
+ if (this.closed)
392
+ return;
393
+ this.closed = true;
394
+ if (this.changeTimer)
395
+ clearTimeout(this.changeTimer);
396
+ this.watcher?.close();
397
+ this.watcher = undefined;
398
+ await this.gitRefreshTask;
399
+ this.directoryCache.clear();
400
+ this.deletedByDirectory.clear();
401
+ this.gitStatusByPath.clear();
402
+ this.cachedEntryCount = 0;
403
+ }
404
+ async readTextFile(path) {
405
+ const file = await this.readTextFileIfPresent(path);
406
+ if (!file)
407
+ throw new WorkspaceError("not_found", "Workspace file was not found");
408
+ return file;
409
+ }
410
+ async readTextFileIfPresent(path) {
411
+ const fullPath = await this.validatedFilePath(path);
412
+ let handle;
413
+ try {
414
+ handle = await open(fullPath, constants.O_RDONLY | constants.O_NOFOLLOW);
415
+ }
416
+ catch (error) {
417
+ if (isUnavailableFileError(error))
418
+ return undefined;
419
+ throw error;
420
+ }
421
+ try {
422
+ const metadata = await handle.stat({ bigint: true });
423
+ if (!metadata.isFile())
424
+ throw new WorkspaceError("not_found", "Workspace file was not found");
425
+ if (metadata.size > BigInt(MAX_WORKSPACE_TEXT_FILE_BYTES))
426
+ throw new WorkspaceError("too_large", `Workspace text files are limited to ${MAX_WORKSPACE_TEXT_FILE_BYTES} bytes`);
427
+ const buffer = await handle.readFile();
428
+ if (buffer.byteLength > MAX_WORKSPACE_TEXT_FILE_BYTES)
429
+ throw new WorkspaceError("too_large", `Workspace text files are limited to ${MAX_WORKSPACE_TEXT_FILE_BYTES} bytes`);
430
+ let content;
431
+ try {
432
+ content = new TextDecoder("utf-8", { fatal: true }).decode(buffer);
433
+ }
434
+ catch {
435
+ throw new WorkspaceError("not_text", "Workspace file is not valid UTF-8 text");
436
+ }
437
+ const observedNs = metadata.ctimeNs > metadata.mtimeNs ? metadata.ctimeNs : metadata.mtimeNs;
438
+ const hash = createHash("sha256").update(buffer).digest("hex");
439
+ return {
440
+ path,
441
+ content,
442
+ size: buffer.byteLength,
443
+ version: `${observedNs.toString().padStart(20, "0")}.${hash}`,
444
+ fullPath,
445
+ mode: Number(metadata.mode & 511n),
446
+ };
447
+ }
448
+ finally {
449
+ await handle.close();
450
+ }
451
+ }
452
+ async replaceTextFile(path, content, current) {
453
+ const fullPath = await this.validatedFilePath(path);
454
+ const parent = dirname(fullPath);
455
+ const temporaryPath = join(parent, `.happy2-write-${randomUUID()}.tmp`);
456
+ let temporary;
457
+ try {
458
+ temporary = await open(temporaryPath, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | constants.O_NOFOLLOW, current?.mode ?? 0o666);
459
+ await temporary.writeFile(content, "utf8");
460
+ await temporary.sync();
461
+ await temporary.close();
462
+ temporary = undefined;
463
+ const latest = await this.readTextFileIfPresent(path);
464
+ assertExpectedVersion(latest, current?.version ?? null);
465
+ if (!current) {
466
+ try {
467
+ await link(temporaryPath, fullPath);
468
+ }
469
+ catch (error) {
470
+ if (isAlreadyExistsError(error)) {
471
+ const conflicting = await this.readTextFileIfPresent(path);
472
+ throw workspaceConflict(conflicting?.version ?? null);
473
+ }
474
+ throw error;
475
+ }
476
+ await unlink(temporaryPath);
477
+ return;
478
+ }
479
+ await rename(temporaryPath, fullPath);
480
+ }
481
+ finally {
482
+ await temporary?.close().catch(() => undefined);
483
+ await unlink(temporaryPath).catch((error) => {
484
+ if (!isMissingPathError(error))
485
+ this.onError(error);
486
+ });
487
+ }
488
+ }
489
+ async validatedFilePath(path) {
490
+ const parent = await this.validatedDirectoryPath(parentDirectory(path));
491
+ return join(parent, pathBasename(path));
492
+ }
493
+ async mutate(action) {
494
+ const previous = this.mutationTail;
495
+ let release;
496
+ const current = new Promise((resolve) => {
497
+ release = resolve;
498
+ });
499
+ this.mutationTail = previous.then(() => current);
500
+ await previous;
501
+ try {
502
+ return await action();
503
+ }
504
+ finally {
505
+ release();
506
+ }
507
+ }
508
+ snapshot(paths, unloaded) {
509
+ const gitStatus = [];
510
+ for (const path of paths) {
511
+ const entry = this.gitStatusByPath.get(path) ??
512
+ (path.endsWith("/") ? this.gitStatusByPath.get(path.slice(0, -1)) : undefined);
513
+ if (entry)
514
+ gitStatus.push(entry.path === path ? entry : { ...entry, path });
515
+ }
516
+ gitStatus.sort((left, right) => comparePaths(left.path, right.path));
517
+ return {
518
+ paths,
519
+ gitStatus,
520
+ revision: `${this.revisionId}.${this.treeGeneration}.${this.gitGeneration}`,
521
+ unloadedDirectories: [...unloaded].sort(comparePaths),
522
+ gitStatusPending: this.gitStatusPending,
523
+ };
524
+ }
525
+ async entriesForDirectory(directory) {
526
+ const entries = [...(await this.readDirectory(directory))];
527
+ const known = new Set(entries.map((entry) => entry.path));
528
+ for (const status of this.deletedByDirectory.get(directory) ?? []) {
529
+ if (known.has(status.path))
530
+ continue;
531
+ entries.push({
532
+ name: pathBasename(status.path),
533
+ path: status.path,
534
+ isDirectory: status.path.endsWith("/"),
535
+ });
536
+ }
537
+ entries.sort((left, right) => comparePaths(left.name, right.name));
538
+ return entries;
539
+ }
540
+ async readDirectory(directory) {
541
+ const cached = this.directoryCache.get(directory);
542
+ if (cached) {
543
+ this.directoryCache.delete(directory);
544
+ this.directoryCache.set(directory, cached);
545
+ return cached;
546
+ }
547
+ const fullPath = await this.validatedDirectoryPath(directory);
548
+ let dirents;
549
+ try {
550
+ dirents = await readdir(fullPath, { withFileTypes: true });
551
+ }
552
+ catch (error) {
553
+ if (isMissingPathError(error))
554
+ throw new WorkspaceError("not_found", "Workspace directory was not found");
555
+ throw error;
556
+ }
557
+ const entries = dirents
558
+ .map((entry) => {
559
+ const path = `${directory}${entry.name}${entry.isDirectory() ? "/" : ""}`;
560
+ return { isDirectory: entry.isDirectory(), name: entry.name, path };
561
+ })
562
+ .sort((left, right) => comparePaths(left.name, right.name));
563
+ this.cacheDirectory(directory, entries);
564
+ return entries;
565
+ }
566
+ async validatedDirectoryPath(directory) {
567
+ const segments = directory ? directory.slice(0, -1).split("/") : [];
568
+ const expected = join(this.realRoot, ...segments);
569
+ let metadata;
570
+ try {
571
+ metadata = await lstat(expected);
572
+ }
573
+ catch (error) {
574
+ if (isMissingPathError(error))
575
+ throw new WorkspaceError("not_found", "Workspace directory was not found");
576
+ throw error;
577
+ }
578
+ if (!metadata.isDirectory() || metadata.isSymbolicLink())
579
+ throw new WorkspaceError("not_found", "Workspace directory was not found");
580
+ const actual = await realpath(expected);
581
+ if (actual !== expected || !isWithinRoot(this.realRoot, actual))
582
+ throw new WorkspaceError("not_found", "Workspace directory was not found");
583
+ return actual;
584
+ }
585
+ cacheDirectory(directory, entries) {
586
+ if (entries.length > MAX_CACHED_DIRECTORY_ENTRIES)
587
+ return;
588
+ const previous = this.directoryCache.get(directory);
589
+ if (previous) {
590
+ this.directoryCache.delete(directory);
591
+ this.cachedEntryCount -= previous.length;
592
+ }
593
+ while (this.directoryCache.size > 0 &&
594
+ this.cachedEntryCount + entries.length > MAX_CACHED_DIRECTORY_ENTRIES) {
595
+ const oldest = this.directoryCache.entries().next().value;
596
+ if (!oldest)
597
+ break;
598
+ this.directoryCache.delete(oldest[0]);
599
+ this.cachedEntryCount -= oldest[1].length;
600
+ }
601
+ this.directoryCache.set(directory, entries);
602
+ this.cachedEntryCount += entries.length;
603
+ }
604
+ queueChange(filename) {
605
+ if (this.closed)
606
+ return;
607
+ if (filename === undefined)
608
+ this.unknownChange = true;
609
+ else
610
+ this.changedPaths.add(filename);
611
+ if (this.changeTimer)
612
+ clearTimeout(this.changeTimer);
613
+ this.changeTimer = setTimeout(() => this.flushChanges(), CHANGE_DEBOUNCE_MS);
614
+ this.changeTimer.unref();
615
+ }
616
+ flushChanges() {
617
+ if (this.closed)
618
+ return;
619
+ if (this.changeTimer)
620
+ clearTimeout(this.changeTimer);
621
+ this.changeTimer = undefined;
622
+ if (this.unknownChange)
623
+ this.clearDirectoryCache();
624
+ else
625
+ for (const path of this.changedPaths)
626
+ this.invalidatePath(path);
627
+ this.changedPaths.clear();
628
+ this.unknownChange = false;
629
+ this.treeGeneration += 1;
630
+ this.gitStatusPending = true;
631
+ void this.publishChange();
632
+ if (this.warm)
633
+ this.requestGitRefresh();
634
+ }
635
+ invalidatePath(nativePath) {
636
+ const normalized = sep === "/" ? nativePath : nativePath.replaceAll(sep, "/");
637
+ if (!normalized || normalized.startsWith("/") || normalized.split("/").includes("..")) {
638
+ this.clearDirectoryCache();
639
+ return;
640
+ }
641
+ this.removeCachedDirectory(parentDirectory(normalized));
642
+ const possibleDirectory = normalized.endsWith("/") ? normalized : `${normalized}/`;
643
+ for (const directory of this.directoryCache.keys()) {
644
+ if (directory === possibleDirectory || directory.startsWith(possibleDirectory))
645
+ this.removeCachedDirectory(directory);
646
+ }
647
+ }
648
+ removeCachedDirectory(directory) {
649
+ const entries = this.directoryCache.get(directory);
650
+ if (!entries)
651
+ return;
652
+ this.directoryCache.delete(directory);
653
+ this.cachedEntryCount -= entries.length;
654
+ }
655
+ clearDirectoryCache() {
656
+ this.directoryCache.clear();
657
+ this.cachedEntryCount = 0;
658
+ }
659
+ requestGitRefresh() {
660
+ if (this.closed || !this.warm)
661
+ return;
662
+ this.gitStatusPending = true;
663
+ if (this.gitRefreshTask) {
664
+ this.gitRefreshAgain = true;
665
+ return;
666
+ }
667
+ this.gitRefreshTask = (async () => {
668
+ do {
669
+ this.gitRefreshAgain = false;
670
+ const generation = this.treeGeneration;
671
+ const status = await readGitStatus(this.root, this.onError);
672
+ if (this.closed || !this.warm)
673
+ return;
674
+ if (generation !== this.treeGeneration) {
675
+ this.gitRefreshAgain = true;
676
+ continue;
677
+ }
678
+ this.replaceGitStatus(status);
679
+ this.gitGeneration += 1;
680
+ } while (this.gitRefreshAgain && !this.closed);
681
+ if (this.closed)
682
+ return;
683
+ this.gitStatusPending = false;
684
+ await this.publishChange();
685
+ })()
686
+ .catch(this.onError)
687
+ .finally(() => {
688
+ this.gitRefreshTask = undefined;
689
+ if (this.gitRefreshAgain && !this.closed)
690
+ this.requestGitRefresh();
691
+ });
692
+ }
693
+ replaceGitStatus(statuses) {
694
+ this.gitStatusByPath.clear();
695
+ this.deletedByDirectory.clear();
696
+ const deleted = new Map();
697
+ for (const status of statuses) {
698
+ this.gitStatusByPath.set(status.path, status);
699
+ if (status.status !== "deleted")
700
+ continue;
701
+ const directory = parentDirectory(status.path);
702
+ const entries = deleted.get(directory) ?? [];
703
+ entries.push(status);
704
+ deleted.set(directory, entries);
705
+ }
706
+ for (const [directory, entries] of deleted)
707
+ this.deletedByDirectory.set(directory, entries);
708
+ }
709
+ publishChange() {
710
+ if (this.closed)
711
+ return Promise.resolve();
712
+ const occurredAt = Date.now();
713
+ return Promise.all([...this.chatIds].map((chatId) => this.pubsub
714
+ .publish(realtimeTopics.chat(chatId), {
715
+ type: "workspace.changed",
716
+ chatId,
717
+ occurredAt,
718
+ })
719
+ .catch(this.onError))).then(() => undefined);
720
+ }
721
+ }
722
+ async function readGitStatus(root, onError) {
723
+ let output;
724
+ try {
725
+ // A mounted workspace is its own filesystem boundary. Do not accidentally
726
+ // inherit a Git repository that contains the server-owned sandbox directory.
727
+ const repositoryPrefix = await gitOutput(root, ["rev-parse", "--show-prefix"]);
728
+ if (repositoryPrefix.trim())
729
+ return [];
730
+ output = await gitOutput(root, [
731
+ "status",
732
+ "--porcelain=v2",
733
+ "-z",
734
+ "--untracked-files=all",
735
+ "--ignored=matching",
736
+ "--",
737
+ ".",
738
+ ]);
739
+ }
740
+ catch (error) {
741
+ if (!isExpectedNoRepositoryError(error))
742
+ onError(error);
743
+ return [];
744
+ }
745
+ return parseGitStatus(output);
746
+ }
747
+ function parseGitStatus(output) {
748
+ const records = output.split("\0");
749
+ const byPath = new Map();
750
+ for (let index = 0; index < records.length; index += 1) {
751
+ const record = records[index];
752
+ if (!record)
753
+ continue;
754
+ if (record.startsWith("? ")) {
755
+ setStatus(byPath, canonicalGitPath(record.slice(2)), "untracked");
756
+ continue;
757
+ }
758
+ if (record.startsWith("! ")) {
759
+ setStatus(byPath, canonicalGitPath(record.slice(2)), "ignored");
760
+ continue;
761
+ }
762
+ if (record.startsWith("1 ")) {
763
+ const match = /^1 (\S{2}) (?:\S+ ){6}([\s\S]+)$/u.exec(record);
764
+ if (match)
765
+ setStatus(byPath, canonicalGitPath(match[2]), statusFromXy(match[1]));
766
+ continue;
767
+ }
768
+ if (record.startsWith("2 ")) {
769
+ const match = /^2 \S{2} (?:\S+ ){7}([\s\S]+)$/u.exec(record);
770
+ if (match)
771
+ setStatus(byPath, canonicalGitPath(match[1]), "renamed");
772
+ // Porcelain v2 places the original path in the following NUL record.
773
+ index += 1;
774
+ continue;
775
+ }
776
+ if (record.startsWith("u ")) {
777
+ const match = /^u \S{2} (?:\S+ ){8}([\s\S]+)$/u.exec(record);
778
+ if (match)
779
+ setStatus(byPath, canonicalGitPath(match[1]), "modified");
780
+ }
781
+ }
782
+ return [...byPath]
783
+ .map(([path, status]) => ({ path, status }))
784
+ .sort((left, right) => comparePaths(left.path, right.path));
785
+ }
786
+ function canonicalDirectory(value) {
787
+ if (value === "")
788
+ return "";
789
+ if (!value.endsWith("/") || value.startsWith("/") || Buffer.byteLength(value) > 16_384)
790
+ throw new WorkspaceError("not_found", "Workspace directory was not found");
791
+ const segments = value.slice(0, -1).split("/");
792
+ if (segments.some((segment) => !segment || segment === "." || segment === ".."))
793
+ throw new WorkspaceError("not_found", "Workspace directory was not found");
794
+ return value;
795
+ }
796
+ function canonicalFilePath(value) {
797
+ if (!value || value.endsWith("/") || value.startsWith("/") || Buffer.byteLength(value) > 16_384)
798
+ throw new WorkspaceError("not_found", "Workspace file was not found");
799
+ const segments = value.split("/");
800
+ if (segments.some((segment) => !segment || segment === "." || segment === ".."))
801
+ throw new WorkspaceError("not_found", "Workspace file was not found");
802
+ return value;
803
+ }
804
+ function publicTextFile(file) {
805
+ return {
806
+ path: file.path,
807
+ content: file.content,
808
+ size: file.size,
809
+ version: file.version,
810
+ };
811
+ }
812
+ function assertExpectedVersion(current, expectedVersion) {
813
+ if ((expectedVersion === null && current === undefined) ||
814
+ (expectedVersion !== null && current?.version === expectedVersion))
815
+ return;
816
+ throw workspaceConflict(current?.version ?? null);
817
+ }
818
+ function workspaceConflict(currentVersion) {
819
+ return new WorkspaceError("conflict", "Workspace file changed after it was read", currentVersion);
820
+ }
821
+ function nextFileContent(current, input) {
822
+ if ((input.content === undefined) === (input.patch === undefined))
823
+ throw new WorkspaceError("invalid_patch", "Provide exactly one of content or patch when writing a workspace file");
824
+ if (input.content !== undefined) {
825
+ assertValidUnicode(input.content);
826
+ return input.content;
827
+ }
828
+ let position = 0;
829
+ let result = "";
830
+ for (const edit of input.patch.edits) {
831
+ if (!Number.isSafeInteger(edit.start) ||
832
+ !Number.isSafeInteger(edit.end) ||
833
+ edit.start < position ||
834
+ edit.end < edit.start ||
835
+ edit.end > current.length)
836
+ throw new WorkspaceError("invalid_patch", "Workspace text edits must be sorted, non-overlapping, and within the file");
837
+ assertValidUnicode(edit.text);
838
+ result += current.slice(position, edit.start);
839
+ result += edit.text;
840
+ position = edit.end;
841
+ }
842
+ result += current.slice(position);
843
+ assertValidUnicode(result);
844
+ return result;
845
+ }
846
+ function assertTextFileSize(content) {
847
+ if (Buffer.byteLength(content) > MAX_WORKSPACE_TEXT_FILE_BYTES)
848
+ throw new WorkspaceError("too_large", `Workspace text files are limited to ${MAX_WORKSPACE_TEXT_FILE_BYTES} bytes`);
849
+ }
850
+ function assertValidUnicode(content) {
851
+ if (Buffer.from(content, "utf8").toString("utf8") !== content)
852
+ throw new WorkspaceError("invalid_patch", "Workspace text must contain valid Unicode");
853
+ }
854
+ function canonicalGitPath(path) {
855
+ const normalized = path.replace(/^\.\//u, "");
856
+ if (!normalized ||
857
+ normalized.startsWith("/") ||
858
+ normalized === ".." ||
859
+ normalized.startsWith("../") ||
860
+ normalized.includes("/../"))
861
+ return undefined;
862
+ return normalized;
863
+ }
864
+ function statusFromXy(xy) {
865
+ if (xy.includes("D"))
866
+ return "deleted";
867
+ if (xy.includes("A"))
868
+ return "added";
869
+ return "modified";
870
+ }
871
+ function setStatus(statuses, path, status) {
872
+ if (!path)
873
+ return;
874
+ const current = statuses.get(path);
875
+ if (!current || statusPriority(status) > statusPriority(current))
876
+ statuses.set(path, status);
877
+ }
878
+ function statusPriority(status) {
879
+ switch (status) {
880
+ case "deleted":
881
+ return 6;
882
+ case "renamed":
883
+ return 5;
884
+ case "added":
885
+ return 4;
886
+ case "modified":
887
+ return 3;
888
+ case "untracked":
889
+ return 2;
890
+ case "ignored":
891
+ return 1;
892
+ }
893
+ }
894
+ function encodeCursor(cursor) {
895
+ return Buffer.from(JSON.stringify(cursor)).toString("base64url");
896
+ }
897
+ function decodeCursor(value) {
898
+ try {
899
+ const decoded = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
900
+ if (typeof decoded.after !== "string" ||
901
+ typeof decoded.directory !== "string" ||
902
+ !Number.isSafeInteger(decoded.treeGeneration) ||
903
+ decoded.treeGeneration < 0 ||
904
+ !Number.isSafeInteger(decoded.gitGeneration) ||
905
+ decoded.gitGeneration < 0)
906
+ throw new Error("invalid cursor");
907
+ return {
908
+ after: decoded.after,
909
+ directory: decoded.directory,
910
+ treeGeneration: decoded.treeGeneration,
911
+ gitGeneration: decoded.gitGeneration,
912
+ };
913
+ }
914
+ catch {
915
+ throw new WorkspaceError("stale_cursor", "Workspace directory cursor is invalid");
916
+ }
917
+ }
918
+ function directoryBasename(directory) {
919
+ return pathBasename(directory.slice(0, -1));
920
+ }
921
+ function parentDirectory(path) {
922
+ const withoutSlash = path.endsWith("/") ? path.slice(0, -1) : path;
923
+ const separator = withoutSlash.lastIndexOf("/");
924
+ return separator < 0 ? "" : `${withoutSlash.slice(0, separator)}/`;
925
+ }
926
+ function pathBasename(path) {
927
+ const withoutSlash = path.endsWith("/") ? path.slice(0, -1) : path;
928
+ const separator = withoutSlash.lastIndexOf("/");
929
+ return withoutSlash.slice(separator + 1);
930
+ }
931
+ function comparePaths(left, right) {
932
+ return left < right ? -1 : left > right ? 1 : 0;
933
+ }
934
+ function encodedPathBytes(path) {
935
+ return Buffer.byteLength(JSON.stringify(path)) + 1;
936
+ }
937
+ function isWithinRoot(root, path) {
938
+ return path === root || path.startsWith(`${root}${sep}`);
939
+ }
940
+ function gitOutput(root, arguments_) {
941
+ return new Promise((resolve, reject) => {
942
+ execFile("git", ["-C", root, ...arguments_], {
943
+ encoding: "utf8",
944
+ env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" },
945
+ maxBuffer: GIT_MAX_BUFFER_BYTES,
946
+ timeout: GIT_TIMEOUT_MS,
947
+ windowsHide: true,
948
+ }, (error, stdout) => {
949
+ if (error)
950
+ reject(error);
951
+ else
952
+ resolve(stdout);
953
+ });
954
+ });
955
+ }
956
+ function isExpectedNoRepositoryError(error) {
957
+ if (!error || typeof error !== "object")
958
+ return false;
959
+ const candidate = error;
960
+ return (candidate.code === 128 ||
961
+ candidate.code === "ENOENT" ||
962
+ candidate.stderr?.includes("not a git repository") === true);
963
+ }
964
+ function isMissingPathError(error) {
965
+ return Boolean(error && typeof error === "object" && error.code === "ENOENT");
966
+ }
967
+ function isUnavailableFileError(error) {
968
+ if (!error || typeof error !== "object")
969
+ return false;
970
+ return ["EISDIR", "ELOOP", "ENOENT", "ENOTDIR"].includes(String(error.code));
971
+ }
972
+ function isAlreadyExistsError(error) {
973
+ return Boolean(error && typeof error === "object" && error.code === "EEXIST");
974
+ }
975
+ async function workspaceRoot(cwd) {
976
+ try {
977
+ const root = await realpath(cwd);
978
+ if (!(await lstat(root)).isDirectory())
979
+ throw new WorkspaceError("not_found", "Chat workspace was not found");
980
+ return root;
981
+ }
982
+ catch (error) {
983
+ if (error instanceof WorkspaceError)
984
+ throw error;
985
+ if (isMissingPathError(error))
986
+ throw new WorkspaceError("not_found", "Chat workspace was not found");
987
+ throw error;
988
+ }
989
+ }
990
+ export function workspaceDirectoryPageLimit(value) {
991
+ if (value === undefined)
992
+ return DEFAULT_DIRECTORY_PAGE_LIMIT;
993
+ if (!Number.isSafeInteger(value) || value < 1 || value > MAX_DIRECTORY_PAGE_LIMIT)
994
+ throw new RangeError(`limit must be between 1 and ${MAX_DIRECTORY_PAGE_LIMIT}`);
995
+ return value;
996
+ }
997
+ //# sourceMappingURL=service.js.map