From: Alan Schmitt <alan.schmitt@polytechnique.org>
To: "lwn" <lwn@lwn.net>, "cwn" <cwn@lists.idyll.org>, caml-list@inria.fr
Subject: [Caml-list] Attn: Development Editor, Latest OCaml Weekly News
Date: Tue, 15 Mar 2022 10:59:22 +0100 [thread overview]
Message-ID: <87a6dr1qpx.fsf@m4x.org> (raw)
[-- Attachment #1: Type: text/plain, Size: 13917 bytes --]
Hello
Here is the latest OCaml Weekly News, for the week of March 08 to 15,
2022.
Table of Contents
─────────────────
Robur Reproducible Builds
OCaml TeXmacs plugin
Release of ocaml-sf/learn-ocaml:0.14.0
Tutorial: Roguelike with effect handlers
Awesome Multicore OCaml and Multicore Monorepo
ppx_viewpattern initial release
Old CWN
Robur Reproducible Builds
═════════════════════════
Archive:
<https://discuss.ocaml.org/t/ann-robur-reproducible-builds/8827/6>
Continuing this thread, Hannes Mehnert announced
────────────────────────────────────────────────
The background article by @rand is now online
<https://r7p5.earth/blog/2022-3-7/Builder-web%20visualizations%20at%20Robur>
OCaml TeXmacs plugin
════════════════════
Archive:
<https://sympa.inria.fr/sympa/arc/caml-list/2022-03/msg00009.html>
Nicolas Ratier announced
────────────────────────
I made a basic OCaml plugin for TeXmacs (<http://www.texmacs.org>) I
would like to keep it simple, but comments and improvements are
welcome.
<http://forum.texmacs.cn/t/ocaml-a-basic-ocaml-plugin-for-texmacs/813>
Release of ocaml-sf/learn-ocaml:0.14.0
══════════════════════════════════════
Archive:
<https://discuss.ocaml.org/t/ann-release-of-ocaml-sf-learn-ocaml-0-14-0/9491/1>
Yurug announced
───────────────
We are very pleased to announce the latest stable release of
[Learn-OCaml], version `0.14.0'.
Many thanks to all users and developers who reported bugs, contributed
features, or patches! Special thanks to @erikmd who made many of the
changes included in this release.
A (mostly) comprehensive list of the features, fixes, and enhancements
offered by this release is available in [the Release Notes ].
A brief and incomplete summary of the changes:
• A long-standing bug has been fixed. This bug was triggered when the
user opened several sessions: the auto-sync mechanism could lead to
overwriting the student's code with an older version.
• The release assets now include a zip file containing the contents of
the `www` directory. This eases the usage of the distributed
binaries.
If need be, feel free to open issues in the [Learn-OCaml bug tracker]
or the [learn-ocaml.el bug tracker], or post in this thread to share
thoughts or experience-feedback.
Happy OCaml learning and teaching!
[Learn-OCaml] <https://github.com/ocaml-sf/learn-ocaml>
[the Release Notes ]
<https://github.com/ocaml-sf/learn-ocaml/releases/tag/v0.14.0>
[Learn-OCaml bug tracker]
<https://github.com/ocaml-sf/learn-ocaml/issues>
[learn-ocaml.el bug tracker]
<https://github.com/pfitaxel/learn-ocaml.el/issues>
Tutorial: Roguelike with effect handlers
════════════════════════════════════════
Archive:
<https://discuss.ocaml.org/t/tutorial-roguelike-with-effect-handlers/9422/18>
Continuing this thread, stw said
────────────────────────────────
Sorry about the late reply, I was busy actually verifying that my
concept works out. Thankfully it does :smile:
The UI framework is inspired by [Concur] which means that every widget
listens for some set of events and suspends computation until one of
these events occurs. Once it does, it continues execution until it
encounter the next await at which point it will suspend once
more. Once a widget has fulfilled its purpose it terminates with some
return value (e.g. text input is confirmed with enter -> return with a
string). Complex UIs are then built by composing simpler widgets. A
more detailed explanation can be found in the link above.
I've implemented this concept using an await function that takes a
list of triggers and a handler for each possible event:
┌────
│ effect Await : Event.t list -> Event.t
│ let rec await triggers handler =
│ handler (EffectHandlers.perform (Await triggers))
│
│ let rec check_box checked =
│ (* display check box *)
│ ...;
│ await [Mouse_press; Key_press] (function
│ | Mouse_press ->
│ print_endline "I've been (un-)checked!";
│ check_box (not checked)
│ | Key_press -> (* Terminate if any key is pressed *) checked)
└────
Every widget can then be implemented as a function which displays the
widget and performs an `Await triggers' which is resumed by passing an
event from `triggers', for example the check box above.
The most complex widget I've implemented so far is a single line text
input. It can be clicked or selected with tab. Moving the mouse while
holding the button down changes the selection. As an automaton:
<https://aws1.discourse-cdn.com/standard11/uploads/ocaml/original/2X/5/574e164b6189608283de32d9f375534ca80caffa.png>
Obviously, this is not a directed acyclic graph and therefore not a
perfect fit for the implicit state stored in the
continuation. Specifically, `Pressed' has an edge to one of its
multiple parents. We can extract the `Pressed' state into its own
function and therefore avoid this issue by 'duplicating' this
state. Now `Pressed' no longer has multiple parents:
<https://aws1.discourse-cdn.com/standard11/uploads/ocaml/original/2X/7/70a34d2f4bb81800a5e3b12b8e49147a0d80ece4.png>
Some cycles remain and we can't remove them because they are essential
to the functionality. Instead we throw an `exception Repeat' that
returns us to a parent node (explicitly shown for Focused -> Pressed
-> Released -> Focused). To do that we modify `await':
┌────
│ let rec await triggers handler =
│ try handler (EffectHandlers.perform (Await triggers)) with
│ | Repeat -> await triggers handler
└────
In the end this results in this main method for the text input, with
only minor simplifications:
┌────
│ method execute =
│ (* Represent the Pressed state.
│ We await the Mouse_release and handle Mouse_motion while we wait. *)
│ let pressed (x,_) =
│ selection <- Point x;
│ await [`Mouse_release; `Mouse_motion] @@ function
│ | `Mouse_release (_, LMB) ->
│ ()
│ | `Mouse_motion (x,_) ->
│ self#select x;
│ raise Repeat (* This restarts the await function *)
│ | _ ->
│ raise Repeat
│ in
│
│ (* We start in the Unfocused state *)
│ begin
│ await [`Mouse_press; `Key_press] @@ function
│ | `Mouse_press (pos, LMB) ->
│ (* We have registered the press, but only when it is released
│ will we be focused. *)
│ pressed pos
│ | `Key_press Tab ->
│ selection <- Area (0, List.length keys)
│ | _ -> raise Repeat
│ end;
│
│ (* We move into the Focused state *)
│ begin
│ await [`Codepoint; `Key_press; `Mouse_press] @@ function
│ | `Key_press Tab | `Key_press Return ->
│ () (* The only path without raising Repeat.
│ Therefore we only leave this await when a tab or return occurs *)
│ | `Mouse_press (pos, LMB) ->
│ pressed pos;
│ raise Repeat
│ | `Key_press c ->
│ self#insert c;
│ raise Repeat
│ | _ -> raise Repeat
│ end;
│ (* We have reached the finished state. We can now return the entered text. *)
│ self#text
└────
I think that this method captures the automaton above quite nicely and
can be relatively easily understood (hopefully even when one is
unfamiliar with the framework and accepts that some magic is happening
in the background (: ). Implementing automatons in terms of effect
handlers seems to work quite well, at least for games and UIs. What
these automatons have in common is that they can be thought of as
flows, starting at some state and ending at one of multiple final
states and only have few edges that don't fit this scheme, turning
them into 'directed almost acyclic graphs'.
There is obviously a lot more necessary for a UI framework
(e.g. resizing the window/widgets, delegating the events to the
correct widget, composing widgets, drawing on the screen etc.) and I
plan to write about it at some point in the future. But for that I
will first need to actually solve these problems as right now their
implementation is quite barebones. The code can be found here for
those interested (still very early in development!):
<https://github.com/Willenbrink/bogue/>
[Concur]
<https://ajnsit.github.io/concur-documentation/ch02-01-anatomy-of-a-widget.html>
Awesome Multicore OCaml and Multicore Monorepo
══════════════════════════════════════════════
Archive:
<https://discuss.ocaml.org/t/awesome-multicore-ocaml-and-multicore-monorepo/9515/1>
Patrick Ferris announced
────────────────────────
A short announcement of two repositories which some people may or may
not have seen. Firstly, [Awesome Multicore OCaml], a place for
gathering all of the rapidly changing experiments, ideas, libraries
and resources for Multicore OCaml (including some of the discuss
threads). If you are working on something or feel anything is missing
please open a PR!
Secondly, a [Multicore Monorepo] which aims to provide a very quick
and easy way to try out effects and parallelism with quite a few
libraries (such as Eio, Dream etc.). The breaking changes introduced
by OCaml 5 can make it frustrating to get such a setup in place,
although this is less and less true thanks to the [alpha
repository]. The idea is that you should just be able to clone this
repository, create a new `5.0.0+trunk' switch, install `dune' and
start hacking. If that's not the case please do open an issue.
[Awesome Multicore OCaml]
<https://github.com/patricoferris/awesome-multicore-ocaml>
[Multicore Monorepo]
<https://github.com/patricoferris/ocaml-multicore-monorepo>
[alpha repository]
<https://github.com/kit-ty-kate/opam-alpha-repository>
ppx_viewpattern initial release
═══════════════════════════════
Archive:
<https://discuss.ocaml.org/t/ann-ppx-viewpattern-initial-release/9516/1>
Simmo Saan announced
────────────────────
I'm glad to announce the initial release of [ppx_viewpattern] –
transformation for view patterns in OCaml.
It _attempts to_ imitate [Haskell view patterns]. I wrote this ppx
rewriter mostly out of curiosity, rather than need, but it turned out
neat enough that others might find it interesting or even useful.
[ppx_viewpattern] <https://github.com/sim642/ppx_viewpattern>
[Haskell view patterns]
<https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/view_patterns.html>
Syntax
╌╌╌╌╌╌
Use `[%view? pat when exp]' as a pattern to apply `exp' to whatever
the pattern is matching and match the result of the `exp' application
against `pat'. This is analogous to the Haskell view pattern `exp ->
pat'.
The above extension node payload syntax is the best I could come up
with to combine an expression and a pattern. Honestly, I was even
surprised that `when exp' is attached to a pattern in the AST (not a
case), because normally it isn't part of the pattern itself.
Example
╌╌╌╌╌╌╌
This allows one to write
┌────
│ (* These cases are exactly like reduction rules! *)
│ let rec reduce = function
│ | Add (Int n1, Int n2) -> Some (Int (n1 + n2))
│ | Add ([%view? Some p1' when reduce], p2) -> Some (Add (p1', p2))
│ | Add (p1, [%view? Some p2' when reduce]) -> Some (Add (p1, p2'))
│ (* ... *)
│ | _ -> None
└────
instead of
┌────
│ (* These nested cases are so annoying! *)
│ let rec reduce = function
│ | Add (Int n1, Int n2) -> Some (Int (n1 + n2))
│ | Add (p1, p2) ->
│ begin match reduce p1 with
│ | Some p1' -> Some (Add (p1', p2))
│ | None ->
│ begin match reduce p2 with
│ | Some p2' -> Some (Add (p1, p2'))
│ | None -> None
│ end
│ end
│ (* ... *)
│ | _ -> None
└────
See [`examples/' on GitHub] for more.
[`examples/' on GitHub]
<https://github.com/sim642/ppx_viewpattern/tree/master/example>
Old CWN
═══════
If you happen to miss a CWN, you can [send me a message] and I'll mail
it to you, or go take a look at [the archive] or the [RSS feed of the
archives].
If you also wish to receive it every week by mail, you may subscribe
[online].
[Alan Schmitt]
[send me a message] <mailto:alan.schmitt@polytechnique.org>
[the archive] <https://alan.petitepomme.net/cwn/>
[RSS feed of the archives] <https://alan.petitepomme.net/cwn/cwn.rss>
[online] <http://lists.idyll.org/listinfo/caml-news-weekly/>
[Alan Schmitt] <https://alan.petitepomme.net/>
[-- Attachment #2: Type: text/html, Size: 33372 bytes --]
next reply other threads:[~2022-03-15 9:59 UTC|newest]
Thread overview: 236+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-03-15 9:59 Alan Schmitt [this message]
-- strict thread matches above, loose matches on Subject: below --
2025-04-15 9:51 Alan Schmitt
2025-04-08 13:14 Alan Schmitt
2025-04-01 9:12 Alan Schmitt
2025-03-25 8:06 Alan Schmitt
2025-03-18 10:18 Alan Schmitt
2025-03-11 15:00 Alan Schmitt
2025-03-04 14:01 Alan Schmitt
2025-02-25 10:36 Alan Schmitt
2025-02-18 14:33 Alan Schmitt
2025-02-11 7:17 Alan Schmitt
2025-02-04 12:05 Alan Schmitt
2025-01-28 13:24 Alan Schmitt
2025-01-21 15:47 Alan Schmitt
2025-01-14 8:20 Alan Schmitt
2025-01-07 17:26 Alan Schmitt
2024-12-31 8:03 Alan Schmitt
2024-12-24 8:55 Alan Schmitt
2024-12-17 13:05 Alan Schmitt
2024-12-10 13:48 Alan Schmitt
2024-12-03 14:44 Alan Schmitt
2024-11-26 8:30 Alan Schmitt
2024-11-19 6:52 Alan Schmitt
2024-11-12 15:00 Alan Schmitt
2024-11-05 13:22 Alan Schmitt
2024-10-29 13:30 Alan Schmitt
2024-10-22 12:42 Alan Schmitt
2024-10-15 13:31 Alan Schmitt
2024-10-08 10:56 Alan Schmitt
2024-10-01 13:37 Alan Schmitt
2024-09-24 13:18 Alan Schmitt
2024-09-17 14:02 Alan Schmitt
2024-09-10 13:55 Alan Schmitt
2024-09-03 8:24 Alan Schmitt
2024-08-27 9:02 Alan Schmitt
2024-08-20 9:29 Alan Schmitt
2024-08-13 13:21 Alan Schmitt
2024-08-06 9:00 Alan Schmitt
2024-07-30 13:26 Alan Schmitt
2024-07-23 13:30 Alan Schmitt
2024-07-16 6:24 Alan Schmitt
2024-07-09 9:19 Alan Schmitt
2024-07-02 7:30 Alan Schmitt
2024-06-25 13:58 Alan Schmitt
2024-06-18 13:05 Alan Schmitt
2024-06-11 15:04 Alan Schmitt
2024-06-04 13:26 Alan Schmitt
2024-05-28 9:07 Alan Schmitt
2024-05-21 13:07 Alan Schmitt
2024-05-14 13:25 Alan Schmitt
2024-05-07 7:30 Alan Schmitt
2024-04-30 7:22 Alan Schmitt
2024-04-23 12:17 Alan Schmitt
2024-04-16 12:00 Alan Schmitt
2024-04-09 9:15 Alan Schmitt
2024-04-02 14:31 Alan Schmitt
2024-03-26 6:32 Alan Schmitt
2024-03-19 15:09 Alan Schmitt
2024-03-12 10:31 Alan Schmitt
2024-03-05 14:50 Alan Schmitt
2024-02-27 13:53 Alan Schmitt
2024-02-20 9:12 Alan Schmitt
2024-02-13 8:42 Alan Schmitt
2024-02-06 15:14 Alan Schmitt
2024-01-30 14:16 Alan Schmitt
2024-01-23 9:45 Alan Schmitt
2024-01-16 10:01 Alan Schmitt
2024-01-09 13:40 Alan Schmitt
2024-01-02 8:59 Alan Schmitt
2023-12-26 10:12 Alan Schmitt
2023-12-19 10:10 Alan Schmitt
2023-12-12 10:20 Alan Schmitt
2023-12-05 10:13 Alan Schmitt
2023-11-28 9:09 Alan Schmitt
2023-11-21 7:47 Alan Schmitt
2023-11-14 13:42 Alan Schmitt
2023-11-07 10:31 Alan Schmitt
2023-10-31 10:43 Alan Schmitt
2023-10-24 9:17 Alan Schmitt
2023-10-17 7:46 Alan Schmitt
2023-10-10 7:48 Alan Schmitt
2023-10-03 13:00 Alan Schmitt
2023-09-19 8:54 Alan Schmitt
2023-09-12 13:21 Alan Schmitt
2023-09-05 9:00 Alan Schmitt
2023-08-29 13:04 Alan Schmitt
2023-08-22 9:20 Alan Schmitt
2023-08-15 16:33 Alan Schmitt
2023-08-08 8:53 Alan Schmitt
2023-08-01 7:13 Alan Schmitt
2023-07-25 8:45 Alan Schmitt
2023-07-11 8:45 Alan Schmitt
2023-07-04 9:18 Alan Schmitt
2023-06-27 8:38 Alan Schmitt
2023-06-20 9:52 Alan Schmitt
2023-06-13 7:09 Alan Schmitt
2023-06-06 14:22 Alan Schmitt
2023-05-30 15:43 Alan Schmitt
2023-05-23 9:41 Alan Schmitt
2023-05-16 13:05 Alan Schmitt
2023-05-09 11:49 Alan Schmitt
2023-05-02 8:01 Alan Schmitt
2023-04-25 9:25 Alan Schmitt
2023-04-18 8:50 Alan Schmitt
2023-04-11 12:41 Alan Schmitt
2023-04-04 8:45 Alan Schmitt
2023-03-28 7:21 Alan Schmitt
2023-03-21 10:07 Alan Schmitt
2023-03-14 9:52 Alan Schmitt
2023-03-07 9:02 Alan Schmitt
2023-02-28 14:38 Alan Schmitt
2023-02-21 10:19 Alan Schmitt
2023-02-14 8:12 Alan Schmitt
2023-02-07 8:16 Alan Schmitt
2023-01-31 6:44 Alan Schmitt
2023-01-24 8:57 Alan Schmitt
2023-01-17 8:37 Alan Schmitt
2022-11-29 14:53 Alan Schmitt
2022-09-27 7:17 Alan Schmitt
2022-09-20 14:01 Alan Schmitt
2022-09-13 8:40 Alan Schmitt
2022-08-23 8:06 Alan Schmitt
2022-08-16 8:51 Alan Schmitt
2022-08-09 8:02 Alan Schmitt
2022-08-02 9:51 Alan Schmitt
2022-07-26 17:54 Alan Schmitt
2022-07-19 8:58 Alan Schmitt
2022-07-12 7:59 Alan Schmitt
2022-07-05 7:42 Alan Schmitt
2022-06-28 7:37 Alan Schmitt
2022-06-21 8:06 Alan Schmitt
2022-06-14 9:29 Alan Schmitt
2022-06-07 10:15 Alan Schmitt
2022-05-31 12:29 Alan Schmitt
2022-05-24 8:04 Alan Schmitt
2022-05-17 7:12 Alan Schmitt
2022-05-10 12:30 Alan Schmitt
2022-05-03 9:11 Alan Schmitt
2022-04-26 6:44 Alan Schmitt
2022-04-19 5:34 Alan Schmitt
2022-04-12 8:10 Alan Schmitt
2022-04-05 11:50 Alan Schmitt
2022-03-29 7:42 Alan Schmitt
2022-03-22 13:01 Alan Schmitt
2022-03-01 13:54 Alan Schmitt
2022-02-22 12:43 Alan Schmitt
2022-02-08 13:16 Alan Schmitt
2022-02-01 13:00 Alan Schmitt
2022-01-25 12:44 Alan Schmitt
2022-01-11 8:20 Alan Schmitt
2022-01-04 7:56 Alan Schmitt
2021-12-28 8:59 Alan Schmitt
2021-12-21 9:11 Alan Schmitt
2021-12-14 11:02 Alan Schmitt
2021-11-30 10:51 Alan Schmitt
2021-11-16 8:41 Alan Schmitt
2021-11-09 10:08 Alan Schmitt
2021-11-02 8:50 Alan Schmitt
2021-10-19 8:23 Alan Schmitt
2021-09-28 6:37 Alan Schmitt
2021-09-21 9:09 Alan Schmitt
2021-09-07 13:23 Alan Schmitt
2021-08-24 13:44 Alan Schmitt
2021-08-17 6:24 Alan Schmitt
2021-08-10 16:47 Alan Schmitt
2021-07-27 8:54 Alan Schmitt
2021-07-20 12:58 Alan Schmitt
2021-07-06 12:33 Alan Schmitt
2021-06-29 12:24 Alan Schmitt
2021-06-22 9:04 Alan Schmitt
2021-06-01 9:23 Alan Schmitt
2021-05-25 7:30 Alan Schmitt
2021-05-11 14:47 Alan Schmitt
2021-05-04 8:57 Alan Schmitt
2021-04-27 14:26 Alan Schmitt
2021-04-20 9:07 Alan Schmitt
2021-04-06 9:42 Alan Schmitt
2021-03-30 14:55 Alan Schmitt
2021-03-23 9:05 Alan Schmitt
2021-03-16 10:31 Alan Schmitt
2021-03-09 10:58 Alan Schmitt
2021-02-23 9:51 Alan Schmitt
2021-02-16 13:53 Alan Schmitt
2021-02-02 13:56 Alan Schmitt
2021-01-26 13:25 Alan Schmitt
2021-01-19 14:28 Alan Schmitt
2021-01-12 9:47 Alan Schmitt
2021-01-05 11:22 Alan Schmitt
2020-12-29 9:59 Alan Schmitt
2020-12-22 8:48 Alan Schmitt
2020-12-15 9:51 Alan Schmitt
2020-12-01 8:54 Alan Schmitt
2020-11-03 15:15 Alan Schmitt
2020-10-27 8:43 Alan Schmitt
2020-10-20 8:15 Alan Schmitt
2020-10-06 7:22 Alan Schmitt
2020-09-29 7:02 Alan Schmitt
2020-09-22 7:27 Alan Schmitt
2020-09-08 13:11 Alan Schmitt
2020-09-01 7:55 Alan Schmitt
2020-08-18 7:25 Alan Schmitt
2020-07-28 16:57 Alan Schmitt
2020-07-21 14:42 Alan Schmitt
2020-07-14 9:54 Alan Schmitt
2020-07-07 10:04 Alan Schmitt
2020-06-30 7:00 Alan Schmitt
2020-06-16 8:36 Alan Schmitt
2020-06-09 8:28 Alan Schmitt
2020-05-19 9:52 Alan Schmitt
2020-05-12 7:45 Alan Schmitt
2020-05-05 7:45 Alan Schmitt
2020-04-28 12:44 Alan Schmitt
2020-04-21 8:58 Alan Schmitt
2020-04-14 7:28 Alan Schmitt
2020-04-07 7:51 Alan Schmitt
2020-03-31 9:54 Alan Schmitt
2020-03-24 9:31 Alan Schmitt
2020-03-17 11:04 Alan Schmitt
2020-03-10 14:28 Alan Schmitt
2020-03-03 8:00 Alan Schmitt
2020-02-25 8:51 Alan Schmitt
2020-02-18 8:18 Alan Schmitt
2020-02-04 8:47 Alan Schmitt
2020-01-28 10:53 Alan Schmitt
2020-01-21 14:08 Alan Schmitt
2020-01-14 14:16 Alan Schmitt
2020-01-07 13:43 Alan Schmitt
2019-12-31 9:18 Alan Schmitt
2019-12-17 8:52 Alan Schmitt
2019-12-10 8:21 Alan Schmitt
2019-12-03 15:42 Alan Schmitt
2019-11-26 8:33 Alan Schmitt
2019-11-12 13:21 Alan Schmitt
2019-11-05 6:55 Alan Schmitt
2019-10-15 7:28 Alan Schmitt
2019-09-03 7:35 Alan Schmitt
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87a6dr1qpx.fsf@m4x.org \
--to=alan.schmitt@polytechnique.org \
--cc=caml-list@inria.fr \
--cc=cwn@lists.idyll.org \
--cc=lwn@lwn.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox