+1
claudeio/claude.opam
+1
claudeio/claude.opam
+1
claudeio/dune-project
+1
claudeio/dune-project
+3
claudeio/lib/claude.ml
+3
claudeio/lib/claude.ml
···
+9
claudeio/lib/claude.mli
+9
claudeio/lib/claude.mli
······
+80
-1
claudeio/lib/client.ml
+80
-1
claudeio/lib/client.ml
············
+193
-3
claudeio/lib/client.mli
+193
-3
claudeio/lib/client.mli
···
+1
-1
claudeio/lib/dune
+1
-1
claudeio/lib/dune
+65
-21
claudeio/lib/message.ml
+65
-21
claudeio/lib/message.ml
·····················+let assistant ~content ~model ?error () = Assistant (Assistant.create ~content ~model ?error ())···
+59
-33
claudeio/lib/message.mli
+59
-33
claudeio/lib/message.mli
···············+val assistant : content:Content_block.t list -> model:string -> ?error:Assistant.error -> unit -> t+(** [assistant_text ~text ~model ?error ()] creates an assistant message with only text content. *)···
+50
-7
claudeio/lib/options.ml
+50
-7
claudeio/lib/options.ml
························+let with_fallback_model_string model t = { t with fallback_model = Some (Model.of_string model) }············
+177
-10
claudeio/lib/options.mli
+177
-10
claudeio/lib/options.mli
························
+91
-4
claudeio/lib/sdk_control.ml
+91
-4
claudeio/lib/sdk_control.ml
·····················+Fmt.pf fmt "@[<2>ServerInfo@ { version = %S;@ capabilities = [%a];@ commands = [%a];@ output_styles = [%a] }@]"
+149
-6
claudeio/lib/sdk_control.mli
+149
-6
claudeio/lib/sdk_control.mli
···············
+45
-9
claudeio/lib/transport.ml
+45
-9
claudeio/lib/transport.ml
·········
+1
-1
claudeio/test/camel_jokes.ml
+1
-1
claudeio/test/camel_jokes.ml
···+let options = Claude.Options.create ~model:(Claude.Model.of_string "sonnet") ~allowed_tools:[] () in
+1
-1
claudeio/test/discovery_demo.ml
+1
-1
claudeio/test/discovery_demo.ml
···
+27
-1
claudeio/test/dune
+27
-1
claudeio/test/dune
···
+1
-1
claudeio/test/hooks_example.ml
+1
-1
claudeio/test/hooks_example.ml
+1
-1
claudeio/test/permission_demo.ml
+1
-1
claudeio/test/permission_demo.ml
···
+1
-1
claudeio/test/simple_permission_test.ml
+1
-1
claudeio/test/simple_permission_test.ml
+1
-1
claudeio/test/test_permissions.ml
+1
-1
claudeio/test/test_permissions.ml
+1
stack/.gitignore
+1
stack/.gitignore
···
-9
stack/bushel/.gitignore
-9
stack/bushel/.gitignore
-1
stack/bushel/.ocamlformat
-1
stack/bushel/.ocamlformat
···
-127
stack/bushel/bin/bushel_bibtex.ml
-127
stack/bushel/bin/bushel_bibtex.ml
···
-67
stack/bushel/bin/bushel_common.ml
-67
stack/bushel/bin/bushel_common.ml
···-(** TODO:claude Get default base directory from BUSHEL_DATA env variable or current directory *)-let doc = "Base directory containing Bushel data (defaults to BUSHEL_DATA env var or current directory)" in
-295
stack/bushel/bin/bushel_doi.ml
-295
stack/bushel/bin/bushel_doi.ml
···-(* Extract publisher URLs from notes (Elsevier, ScienceDirect, IEEE, Nature, ACM, Sage, UPenn, Springer, Taylor & Francis, OUP) *)-(* Matches publisher URLs: linkinghub.elsevier.com, sciencedirect.com/science/article, ieeexplore.ieee.org, academic.oup.com, nature.com, journals.sagepub.com, garfield.library.upenn.edu, link.springer.com, tandfonline.com/doi, and dl.acm.org/doi/10.* URLs *)-let publisher_pattern = Re.Perl.compile_pat "https?://(?:(?:www\\.)?(?:linkinghub\\.elsevier\\.com|(?:www\\.)?sciencedirect\\.com/science/article|ieeexplore\\.ieee\\.org|academic\\.oup\\.com|nature\\.com|journals\\.sagepub\\.com|garfield\\.library\\.upenn\\.edu|link\\.springer\\.com)/[^)\\s\"'>]+|(?:dl\\.acm\\.org|(?:www\\.)?tandfonline\\.com)/doi(?:/pdf)?/10\\.[^)\\s\"'>]+)" in-let entry = Bushel.Doi_entry.create_resolved ~doi ~title ~authors ~year ~bibtype ~publisher ~source_urls:[doi_url] () in-Lwt.return (Bushel.Doi_entry.create_failed ~doi ~error:(Printexc.to_string e) ~source_urls:[doi_url] ())-Lwt.return (Bushel.Doi_entry.create_failed ~doi ~error:(Printexc.to_string exn) ~source_urls:[doi_url] ())-Lwt.return (Bushel.Doi_entry.create_failed ~doi:url ~error:"Empty response" ~source_urls:[url] ())-let entry = Bushel.Doi_entry.create_resolved ~doi ~title ~authors ~year ~bibtype ~publisher ~source_urls () in-Lwt.return (Bushel.Doi_entry.create_failed ~doi:url ~error:(Printexc.to_string e) ~source_urls:[url] ())-Lwt.return (Bushel.Doi_entry.create_failed ~doi:url ~error:(Printexc.to_string exn) ~source_urls:[url] ())-Printf.printf "Resolving %d DOI(s) and %d URL(s)...\n%!" (List.length dois_to_resolve) (List.length urls_to_resolve);-match Bushel.Doi_entry.find_by_doi_including_ignored !merged new_entry.Bushel.Doi_entry.doi with-(* DOI already exists - merge the entries by combining source_urls and preserving ignore flag *)-merged := combined :: (List.filter (fun e -> e.Bushel.Doi_entry.doi <> new_entry.Bushel.Doi_entry.doi) !merged)
-182
stack/bushel/bin/bushel_faces.ml
-182
stack/bushel/bin/bushel_faces.ml
···-Lwt.return (`Skipped (sprintf "Thumbnail for '%s' already exists at %s" (List.hd names) output_path))-let ok_count = List.length (List.filter (fun (_, r) -> match r with `Ok _ -> true | _ -> false) results) in-let error_count = List.length (List.filter (fun (_, r) -> match r with `Error _ -> true | _ -> false) results) in-let skipped_count = List.length (List.filter (fun (_, r) -> match r with `Skipped _ -> true | _ -> false) results) in-) $ Bushel_common.base_dir $ Bushel_common.output_dir ~default:"." $ Bushel_common.handle_opt $-Bushel_common.url_term ~default:"https://photos.recoil.org" ~doc:"Base URL of the Immich instance")-let info = Cmd.info "faces" ~doc:"Retrieve face thumbnails for Bushel contacts from Immich" in
-77
stack/bushel/bin/bushel_ideas.ml
-77
stack/bushel/bin/bushel_ideas.ml
···
-227
stack/bushel/bin/bushel_info.ml
-227
stack/bushel/bin/bushel_info.ml
···-Fmt.pr "%a@," (Fmt.styled `Bold Fmt.string) (if notes_only then "Available notes:" else "Available entries:");-let doc = "The slug of the entry to display (with or without leading ':'), or contact handle (with '@' prefix). If not provided, lists all available slugs." in
-549
stack/bushel/bin/bushel_links.ml
-549
stack/bushel/bin/bushel_links.ml
···-print_endline (Fmt.str "Including only domains: %s" (String.concat ", " include_domains_list));-let merged_links = Bushel.Link.merge_links ~prefer_new_date:true existing_links !extracted_links in-let upload_to_karakeep base_url api_key_opt links_file tag max_concurrent delay_seconds limit verbose =-(batch_num + 1) batch_successes (List.length batch) new_total (new_total + (List.length links_to_upload - new_total));-Cmd.v info Term.(const update_from_karakeep $ base_url_arg $ api_key_arg $ tag_arg $ links_file_arg $ download_assets_arg)-Cmd.v info Term.(const update_from_bushel $ base_dir_arg $ links_file_arg $ include_domains_arg $ exclude_domains_arg)-Cmd.v info Term.(const upload_to_karakeep $ base_url_arg $ api_key_arg $ links_file_arg $ tag_arg $ concurrent_arg $ delay_arg $ limit_arg $ verbose_arg)
-119
stack/bushel/bin/bushel_main.ml
-119
stack/bushel/bin/bushel_main.ml
···
-186
stack/bushel/bin/bushel_missing.ml
-186
stack/bushel/bin/bushel_missing.ml
···-Fmt.pr "@.%a (%d):@," (Fmt.styled `Bold Fmt.string) title (List.length entries_with_broken_refs);-let missing_cmd base_dir check_thumbnails check_synopsis check_tags check_refs _env _xdg _profile =-`P "This command scans all entries and reports any that are missing thumbnails, synopsis, tags, or have broken slugs/contact handles.";
-131
stack/bushel/bin/bushel_note_doi.ml
-131
stack/bushel/bin/bushel_note_doi.ml
···
-88
stack/bushel/bin/bushel_obsidian.ml
-88
stack/bushel/bin/bushel_obsidian.ml
···
-74
stack/bushel/bin/bushel_paper.ml
-74
stack/bushel/bin/bushel_paper.ml
···
-57
stack/bushel/bin/bushel_paper_classify.ml
-57
stack/bushel/bin/bushel_paper_classify.ml
···
-325
stack/bushel/bin/bushel_paper_tex.ml
-325
stack/bushel/bin/bushel_paper_tex.ml
···-let journal_name = try journal paper |> clean_venue_name |> escape_latex with _ -> "Journal" in-let conf_name = try booktitle paper |> clean_venue_name |> escape_latex with _ -> "Conference" in-let journal_str = try Bushel.Paper.journal paper |> clean_venue_name |> escape_latex with _ -> "" in-let booktitle_str = try Bushel.Paper.booktitle paper |> clean_venue_name |> escape_latex with _ -> "" in-let conf_name = try Bushel.Paper.booktitle paper |> clean_venue_name |> escape_latex with _ -> "" in-let journal_str = try Bushel.Paper.journal paper |> clean_venue_name |> escape_latex with _ -> "" in-(* Add DOI or PDF link if available, but not for in-press papers unless they have explicit URL *)-let non_selected_papers = List.filter (fun p -> not (Bushel.Paper.selected p)) latest_papers in-Printf.printf "Generated %s/papers_full.tex with %d entries\n" output_dir (List.length sorted_full);-Printf.printf "Generated %s/papers_short.tex with %d entries\n" output_dir (List.length sorted_short);-Printf.printf "Generated %s/papers_preprint.tex with %d entries\n" output_dir (List.length sorted_preprint);-Printf.printf "Generated %s/papers_selected.tex with %d entries\n" output_dir (List.length sorted_selected);
-48
stack/bushel/bin/bushel_search.ml
-48
stack/bushel/bin/bushel_search.ml
···-Printf.eprintf "Error: API key is required. Use TYPESENSE_API_KEY environment variable or create .typesense-key file.\n";-let combined_response = Bushel.Typesense.combine_multisearch_results multisearch_resp ~limit ~offset () in-Printf.printf "Found %d results (%.2fms)\n\n" combined_response.total combined_response.query_time;-Printf.printf "%d. %s (score: %.2f)\n" (i + 1) (Bushel.Typesense.pp_search_result_oneline hit) hit.Bushel.Typesense.score
-70
stack/bushel/bin/bushel_thumbs.ml
-70
stack/bushel/bin/bushel_thumbs.ml
···-sprintf "magick -density 600 -quality 100 %s[0] -gravity North -crop 100%%x50%%+0+0 -resize %s %s"-Term.(const (fun base_dir output_dir _env _xdg _profile -> process_papers base_dir output_dir; 0) $
-188
stack/bushel/bin/bushel_typesense.ml
-188
stack/bushel/bin/bushel_typesense.ml
···-Printf.eprintf "Error: API key is required. Use TYPESENSE_API_KEY environment variable or create .typesense-key file.\n";-Printf.eprintf "Error: OpenAI API key is required for embeddings. Use OPENAI_API_KEY environment variable or create .openrouter-api file.\n";-Printf.eprintf "Error: API key is required. Use TYPESENSE_API_KEY environment variable or create .typesense-key file.\n";-Printf.eprintf "Error: API key is required. Use TYPESENSE_API_KEY environment variable or create .typesense-key file.\n";-let doc = "Specific collection to search (contacts, papers, projects, notes, videos, ideas)" in-`P "The API key can be read from .typesense-key file or TYPESENSE_API_KEY environment variable.";-`P "Upload all bushel object types (contacts, papers, projects, notes, videos, ideas) to a Typesense search engine instance.";
-138
stack/bushel/bin/bushel_video.ml
-138
stack/bushel/bin/bushel_video.ml
···-Lwt_main.run (process_videos output_dir overwrite base_url channel fetch_thumbs thumbs_dir); 0)
-81
stack/bushel/bin/bushel_video_thumbs.ml
-81
stack/bushel/bin/bushel_video_thumbs.ml
···
-20
stack/bushel/bin/dune
-20
stack/bushel/bin/dune
···-(modules bushel_main bushel_bibtex bushel_ideas bushel_info bushel_missing bushel_note_doi bushel_obsidian bushel_paper_classify bushel_paper_tex bushel_thumbs bushel_search)-(libraries bushel bushel_common cmdliner eio eio_main eiocmd yaml ezjsonm zotero-translation fmt cmarkit uri unix ptime.clock.os crockford))
-51
stack/bushel/bushel.opam
-51
stack/bushel/bushel.opam
···
-3
stack/bushel/bushel.opam.template
-3
stack/bushel/bushel.opam.template
-35
stack/bushel/dune-project
-35
stack/bushel/dune-project
···
-79
stack/bushel/lib/bushel.ml
-79
stack/bushel/lib/bushel.ml
···-let entries = Entry.v ~images ~papers ~notes ~projects ~ideas ~videos ~contacts ~data_dir:(base ^ "/data") in
-27
stack/bushel/lib/bushel.mli
-27
stack/bushel/lib/bushel.mli
···
-172
stack/bushel/lib/contact.ml
-172
stack/bushel/lib/contact.ml
···-pf ppf "%a: @[<h>%a@]@," (styled `Bold string) "Aliases" (list ~sep:comma string) (List.tl ns);
-25
stack/bushel/lib/contact.mli
-25
stack/bushel/lib/contact.mli
···
-72
stack/bushel/lib/description.ml
-72
stack/bushel/lib/description.ml
···
-19
stack/bushel/lib/description.mli
-19
stack/bushel/lib/description.mli
···-val note_description : Note.t -> date_str:string -> lookup_fn:(string -> string option) -> string-val video_description : Video.t -> date_str:string -> lookup_fn:(string -> string option) -> string
-147
stack/bushel/lib/doi_entry.ml
-147
stack/bushel/lib/doi_entry.ml
···-{ doi; title; authors; year; bibtype; publisher; resolved_at; source_urls; status = Resolved; ignore = false }-(* Support both old source_url (single) and new source_urls (list) for backwards compatibility *)-{ doi; title; authors; year; bibtype; publisher; resolved_at; source_urls; status = Resolved; ignore }
-51
stack/bushel/lib/doi_entry.mli
-51
stack/bushel/lib/doi_entry.mli
···-source_urls: string list; (** All URLs that resolve to this DOI (publisher links, doi.org URLs, etc) *)
-19
stack/bushel/lib/dune
-19
stack/bushel/lib/dune
-449
stack/bushel/lib/entry.ml
-449
stack/bushel/lib/entry.ml
···-{ slugs; papers; old_papers; notes; projects; ideas; videos; images; contacts; doi_entries; data_dir }-(* Use titleimage if set, otherwise extract first image from body, then try video, otherwise use slug_ent's thumbnail *)
-79
stack/bushel/lib/entry.mli
-79
stack/bushel/lib/entry.mli
···
-223
stack/bushel/lib/idea.ml
-223
stack/bushel/lib/idea.ml
···
-55
stack/bushel/lib/idea.mli
-55
stack/bushel/lib/idea.mli
···
-296
stack/bushel/lib/link.ml
-296
stack/bushel/lib/link.ml
···
-34
stack/bushel/lib/link.mli
-34
stack/bushel/lib/link.mli
···
-317
stack/bushel/lib/link_graph.ml
-317
stack/bushel/lib/link_graph.ml
···-Fmt.pf ppf "@[<v>Internal links: %d@,External links: %d@,Entries with outbound: %d@,Entries with backlinks: %d@]"
-781
stack/bushel/lib/md.ml
-781
stack/bushel/lib/md.ml
···-let mapper = Mapper.make ~inline:(make_validation_mapper entries broken_slugs broken_contacts) () in-(* Scan body for publisher URLs (Elsevier, ScienceDirect, IEEE, Nature, ACM, Sage, UPenn, Springer, Taylor & Francis, OUP) and resolve from cache *)-let publisher_pattern = Re.Perl.compile_pat "https?://(?:(?:www\\.)?(?:linkinghub\\.elsevier\\.com|(?:www\\.)?sciencedirect\\.com/science/article|ieeexplore\\.ieee\\.org|academic\\.oup\\.com|nature\\.com|journals\\.sagepub\\.com|garfield\\.library\\.upenn\\.edu|link\\.springer\\.com)/[^)\\s\"'>]+|(?:dl\\.acm\\.org|(?:www\\.)?tandfonline\\.com)/doi(?:/pdf)?/10\\.[^)\\s\"'>]+)" in
-73
stack/bushel/lib/md.mli
-73
stack/bushel/lib/md.mli
···-val note_references : Entry.t -> Contact.t -> Note.t -> (string * string * reference_source) list
-230
stack/bushel/lib/note.ml
-230
stack/bushel/lib/note.ml
···-{ title; draft; date; slug; synopsis; titleimage; index_page; perma; doi; body; via; updated; tags; sidebar; slug_ent; source; url; author; category }-[("name", string "type"); ("type", string "string"); ("facet", bool true); ("optional", bool true)];-[("name", string "status"); ("type", string "string"); ("facet", bool true); ("optional", bool true)];-[("name", string "source"); ("type", string "string"); ("facet", bool true); ("optional", bool true)];-[("name", string "category"); ("type", string "string"); ("facet", bool true); ("optional", bool true)];
-49
stack/bushel/lib/note.mli
-49
stack/bushel/lib/note.mli
···
-373
stack/bushel/lib/paper.ml
-373
stack/bushel/lib/paper.ml
···-let journal = try key paper "journal" |> J.get_string |> String.lowercase_ascii with _ -> "" in-let booktitle = try key paper "booktitle" |> J.get_string |> String.lowercase_ascii with _ -> "" in-let title_str = try key paper "title" |> J.get_string |> String.lowercase_ascii with _ -> "" in-if contains_any journal ["arxiv"] || contains_any booktitle ["arxiv"] || bibtype_lower = "misc" || bibtype_lower = "techreport"-Re.replace_string (Re.compile (Re.seq [Re.char '\n'; Re.char '\n'; Re.rep1 (Re.char '\n')])) ~by:"\n\n" trimmed_abs-pf ppf "%a: @[<h>%a@]@," (styled `Bold string) "Authors" (list ~sep:comma string) (authors p);
-55
stack/bushel/lib/paper.mli
-55
stack/bushel/lib/paper.mli
···
-100
stack/bushel/lib/project.ml
-100
stack/bushel/lib/project.ml
···-[("name", string "languages"); ("type", string "string[]"); ("facet", bool true); ("optional", bool true)];-[("name", string "license"); ("type", string "string"); ("facet", bool true); ("optional", bool true)];-[("name", string "status"); ("type", string "string"); ("facet", bool true); ("optional", bool true)];
-21
stack/bushel/lib/project.mli
-21
stack/bushel/lib/project.mli
···
-44
stack/bushel/lib/srcsetter.ml
-44
stack/bushel/lib/srcsetter.ml
···
-21
stack/bushel/lib/srcsetter.mli
-21
stack/bushel/lib/srcsetter.mli
···
-114
stack/bushel/lib/tags.ml
-114
stack/bushel/lib/tags.ml
···
-25
stack/bushel/lib/tags.mli
-25
stack/bushel/lib/tags.mli
···
-527
stack/bushel/lib/typesense.ml
-527
stack/bushel/lib/typesense.ml
···-let body = if body = "" then None else Some (Requests.Body.of_string Requests.Mime.json body) in-let abstract = Md.markdown_to_plaintext entries (Paper.abstract paper) |> truncate_for_embedding in-let description = Md.markdown_to_plaintext entries (Project.body project) |> truncate_for_embedding in-let description = Md.markdown_to_plaintext entries (Video.body video) |> truncate_for_embedding in-let description = Md.markdown_to_plaintext entries (Idea.body idea) |> truncate_for_embedding in-("contacts", add_embedding_field_to_schema Contact.typesense_schema config ["name"; "names"], (List.map contact_to_document contacts : Ezjsonm.value list));-("papers", add_embedding_field_to_schema Paper.typesense_schema config ["title"; "abstract"; "authors"], (List.map (paper_to_document entries) papers : Ezjsonm.value list));-("videos", add_embedding_field_to_schema Video.typesense_schema config ["title"; "description"], (List.map (video_to_document entries) videos : Ezjsonm.value list));-("projects", add_embedding_field_to_schema Project.typesense_schema config ["title"; "description"; "tags"], (List.map (project_to_document entries) projects : Ezjsonm.value list));-("notes", add_embedding_field_to_schema Note.typesense_schema config ["title"; "content"; "tags"], (List.map (note_to_document entries) notes : Ezjsonm.value list));-("ideas", add_embedding_field_to_schema Idea.typesense_schema config ["title"; "description"; "tags"], (List.map (idea_to_document entries) ideas : Ezjsonm.value list));-let upload_collection ((name, schema, documents) : string * Ezjsonm.value * Ezjsonm.value list) =-if String.contains line ':' && Str.string_match (Str.regexp ".*success.*true.*") line 0 then acc + 1 else acc) 0 lines in-if String.contains line ':' && Str.string_match (Str.regexp ".*success.*false.*") line 0 then acc + 1 else acc) 0 lines in-let failed_lines = List.filter (fun line -> Str.string_match (Str.regexp ".*success.*false.*") line 0) lines in-let search_collection ~sw ~env (config : config) collection_name query ?(limit=10) ?(offset=0) () =-let result = Typesense_client.search_collection client collection_name query ~limit ~offset () in-let combined_response = Typesense_client.combine_multisearch_results multisearch_resp ~limit ~offset () in-let combine_multisearch_results (multisearch_resp : multisearch_response) ?(limit=10) ?(offset=0) () =
-168
stack/bushel/lib/typesense.mli
-168
stack/bushel/lib/typesense.mli
···-let config = { endpoint = "https://search.example.com"; api_key = "xyz123"; openai_key = "sk-..." } in-env:< clock: [> float Eio.Time.clock_ty ] Eio.Resource.t; net: [> [> `Generic ] Eio.Net.ty ] Eio.Resource.t; .. > ->-env:< clock: [> float Eio.Time.clock_ty ] Eio.Resource.t; net: [> [> `Generic ] Eio.Net.ty ] Eio.Resource.t; .. > ->-env:< clock: [> float Eio.Time.clock_ty ] Eio.Resource.t; net: [> [> `Generic ] Eio.Net.ty ] Eio.Resource.t; .. > ->-env:< clock: [> float Eio.Time.clock_ty ] Eio.Resource.t; net: [> [> `Generic ] Eio.Net.ty ] Eio.Resource.t; .. > ->-env:< clock: [> float Eio.Time.clock_ty ] Eio.Resource.t; net: [> [> `Generic ] Eio.Net.ty ] Eio.Resource.t; .. > ->-env:< clock: float Eio.Time.clock_ty Eio.Resource.t; fs: Eio.Fs.dir_ty Eio.Path.t; net: [`Generic | `Unix] Eio.Net.ty Eio.Resource.t; .. > ->-env:< clock: float Eio.Time.clock_ty Eio.Resource.t; fs: Eio.Fs.dir_ty Eio.Path.t; net: [`Generic | `Unix] Eio.Net.ty Eio.Resource.t; .. > ->-env:< clock: float Eio.Time.clock_ty Eio.Resource.t; fs: Eio.Fs.dir_ty Eio.Path.t; net: [`Generic | `Unix] Eio.Net.ty Eio.Resource.t; .. > ->-val combine_multisearch_results : multisearch_response -> ?limit:int -> ?offset:int -> unit -> search_response-env:< clock: float Eio.Time.clock_ty Eio.Resource.t; fs: Eio.Fs.dir_ty Eio.Path.t; net: [`Generic | `Unix] Eio.Net.ty Eio.Resource.t; .. > ->
-80
stack/bushel/lib/util.ml
-80
stack/bushel/lib/util.ml
···
-166
stack/bushel/lib/video.ml
-166
stack/bushel/lib/video.ml
···-[("name", string "channel"); ("type", string "string"); ("facet", bool true); ("optional", bool true)];-[("name", string "platform"); ("type", string "string"); ("facet", bool true); ("optional", bool true)];
-32
stack/bushel/lib/video.mli
-32
stack/bushel/lib/video.mli
···
+2
-1
stack/cacheio/cacheio.opam
+2
-1
stack/cacheio/cacheio.opam
+2
-1
stack/cacheio/dune-project
+2
-1
stack/cacheio/dune-project
+1
-1
stack/cacheio/lib/dune
+1
-1
stack/cacheio/lib/dune
···+(libraries eio eio_main digestif jsont jsont.bytesrw ptime ptime.clock.os logs fmt xdge cstruct))
+23
-1
stack/cacheio/lib/entry.ml
+23
-1
stack/cacheio/lib/entry.ml
···
+6
-1
stack/cacheio/lib/entry.mli
+6
-1
stack/cacheio/lib/entry.mli
+31
-1
stack/cacheio/lib/flags.ml
+31
-1
stack/cacheio/lib/flags.ml
···
+6
-1
stack/cacheio/lib/flags.mli
+6
-1
stack/cacheio/lib/flags.mli
+26
-1
stack/cacheio/lib/stats.ml
+26
-1
stack/cacheio/lib/stats.ml
···
+6
-1
stack/cacheio/lib/stats.mli
+6
-1
stack/cacheio/lib/stats.mli
+1
-1
stack/immich/dune
+1
-1
stack/immich/dune
+2
-1
stack/immich/dune-project
+2
-1
stack/immich/dune-project
+46
-40
stack/immich/immich.ml
+46
-40
stack/immich/immich.ml
···-requests_session: (float Eio.Time.clock_ty Eio.Resource.t, 'net Eio.Net.ty Eio.Resource.t) Requests.t;·········
+3
-1
stack/immich/immich.mli
+3
-1
stack/immich/immich.mli
·········-requests_session:(float Eio.Time.clock_ty Eio.Resource.t, [`Generic | `Unix] Eio.Net.ty Eio.Resource.t) Requests.t ->
+2
-1
stack/immich/immich.opam
+2
-1
stack/immich/immich.opam
+42
-37
stack/karakeep/bin/karakeep_cli.ml
+42
-37
stack/karakeep/bin/karakeep_cli.ml
···············
+1
-1
stack/karakeep/dune
+1
-1
stack/karakeep/dune
-1
stack/karakeep/dune-project
-1
stack/karakeep/dune-project
+315
-275
stack/karakeep/karakeep.ml
+315
-275
stack/karakeep/karakeep.ml
···-http_client: (float Eio.Time.clock_ty Eio.Resource.t, 'net Eio.Net.ty Eio.Resource.t) Requests.t;+Log.debug (fun m -> m "Successfully parsed format 1: %d bookmarks" (List.length response.data));+Log.debug (fun m -> m "Successfully parsed format 2: %d bookmarks" (List.length response.data));···············let create_bookmark client ~url ?title ?note ?tags ?(favourited=false) ?(archived=false) () =···+| Error e -> failwith (Fmt.str "Failed to parse created bookmark: %s" (Jsont.Error.to_string e))···failwith (Fmt.str "Failed to create bookmark. HTTP error: %d. Details: %s" status_code error_body)
+23
-28
stack/karakeep/karakeep.mli
+23
-28
stack/karakeep/karakeep.mli
······
-1
stack/karakeep/karakeep.opam
-1
stack/karakeep/karakeep.opam
+8
-4
stack/peertubee/bin/peertubee_cli.ml
+8
-4
stack/peertubee/bin/peertubee_cli.ml
······
+1
-1
stack/peertubee/dune
+1
-1
stack/peertubee/dune
-1
stack/peertubee/dune-project
-1
stack/peertubee/dune-project
+87
-59
stack/peertubee/peertubee.ml
+87
-59
stack/peertubee/peertubee.ml
···-requests_session: (float Eio.Time.clock_ty Eio.Resource.t, 'net Eio.Net.ty Eio.Resource.t) Requests.t;···+|> Jsont.Object.opt_mem "originallyPublishedAt" Rfc3339.jsont ~enc:video_originally_published_at······
+34
-6
stack/peertubee/peertubee.mli
+34
-6
stack/peertubee/peertubee.mli
···-requests_session:(float Eio.Time.clock_ty Eio.Resource.t, [`Generic | `Unix] Eio.Net.ty Eio.Resource.t) Requests.t ->···
-1
stack/peertubee/peertubee.opam
-1
stack/peertubee/peertubee.opam
+1
-1
stack/requests/bin/dune
+1
-1
stack/requests/bin/dune
+17
-16
stack/requests/bin/ocurl.ml
+17
-16
stack/requests/bin/ocurl.ml
·········-let run_request env sw persist_cookies verify_tls enable_cache timeout follow_redirects max_redirects······-run_request env sw persist_cookies verify_tls enable_cache timeout follow_redirects max_redirects···`Pre " $(tname) -H 'Accept: application/json' -H 'X-Api-Key: secret' https://api.example.com";···
-1
stack/requests/dune-project
-1
stack/requests/dune-project
+28
-150
stack/requests/lib/body.ml
+28
-150
stack/requests/lib/body.ml
···+let content = match Jsont_bytesrw.encode_string' ~format:Jsont.Minify Jsont.json json_value with+let content = match Jsont_bytesrw.encode_string' ~format:Jsont.Minify Jsont.json json_value with
+14
-31
stack/requests/lib/body.mli
+14
-31
stack/requests/lib/body.mli
···
-420
stack/requests/lib/cache.ml
-420
stack/requests/lib/cache.ml
···-let create ~sw ~enabled ?(cache_get_requests=true) ?(cache_range_requests=true) ~cache_dir () =
+2
-4
stack/requests/lib/dune
+2
-4
stack/requests/lib/dune
+41
-76
stack/requests/lib/requests.ml
+41
-76
stack/requests/lib/requests.ml
·····················let make_request_internal t ?headers ?body ?auth ?timeout ?follow_redirects ?max_redirects ~method_ url =···-Response.Private.make ~sw:t.sw ~status ~headers:resp_headers ~body:body_flow ~url ~elapsed:0.0······+let request (T t) ?headers ?body ?auth ?timeout ?follow_redirects ?max_redirects ~method_ url =···
+37
-29
stack/requests/lib/requests.mli
+37
-29
stack/requests/lib/requests.mli
······< clock: 'clock Eio.Resource.t; net: 'net Eio.Resource.t; fs: Eio.Fs.dir_ty Eio.Path.t; .. > ->···-@param xdg XDG directory context for cookies/cache (required if persist_cookies or enable_cache)······························-val create : config -> < clock: ([> float Eio.Time.clock_ty ] as 'clock) Eio.Resource.t; net: ([> [>`Generic] Eio.Net.ty ] as 'net) Eio.Resource.t; fs: Eio.Fs.dir_ty Eio.Path.t; .. > -> Eio.Switch.t -> ('clock Eio.Resource.t, 'net Eio.Resource.t) t···-val requests_term : string -> < clock: ([> float Eio.Time.clock_ty ] as 'clock) Eio.Resource.t; net: ([> [>`Generic] Eio.Net.ty ] as 'net) Eio.Resource.t; fs: Eio.Fs.dir_ty Eio.Path.t; .. > -> Eio.Switch.t -> ('clock Eio.Resource.t, 'net Eio.Resource.t) t Cmdliner.Term.t(** [requests_term app_name env sw] creates a term that directly produces a requests instance.···
-1
stack/requests/requests.opam
-1
stack/requests/requests.opam
-18
stack/requests/test/dune
-18
stack/requests/test/dune
···
-52
stack/requests/test/test_connection_pool.ml
-52
stack/requests/test/test_connection_pool.ml
···
-899
stack/requests/test/test_requests.ml
-899
stack/requests/test/test_requests.ml
···-let response = Requests.request req ~follow_redirects:false ~method_:`GET (base_url ^ "/redirect") in
+2
-1
stack/requests_json_api/dune-project
+2
-1
stack/requests_json_api/dune-project
+1
-1
stack/requests_json_api/lib/dune
+1
-1
stack/requests_json_api/lib/dune
+83
-15
stack/requests_json_api/lib/requests_json_api.ml
+83
-15
stack/requests_json_api/lib/requests_json_api.ml
···
+51
-13
stack/requests_json_api/lib/requests_json_api.mli
+51
-13
stack/requests_json_api/lib/requests_json_api.mli
···-val get_json_exn : (_ Eio.Time.clock, _ Eio.Net.t) Requests.t -> string -> (Ezjsonm.value -> 'a) -> 'a-val get_json : (_ Eio.Time.clock, _ Eio.Net.t) Requests.t -> string -> (Ezjsonm.value -> 'a) ->-val post_json : (_ Eio.Time.clock, _ Eio.Net.t) Requests.t -> string -> Ezjsonm.value -> Requests.Response.t+(** [post_json session url codec value] encodes [value] using the Jsont codec and POSTs it to the URL.-val post_json_exn : (_ Eio.Time.clock, _ Eio.Net.t) Requests.t -> string -> Ezjsonm.value -> string-val post_json_result : (_ Eio.Time.clock, _ Eio.Net.t) Requests.t -> string -> Ezjsonm.value ->+(** [post_json_decode_exn session url ~req req_value ~resp] encodes [req_value] using the [req] codec,···-val get_result : (_ Eio.Time.clock, _ Eio.Net.t) Requests.t -> string -> (string, int * string) result
+2
-1
stack/requests_json_api/requests_json_api.opam
+2
-1
stack/requests_json_api/requests_json_api.opam
+1
-1
stack/river/bin/dune
+1
-1
stack/river/bin/dune
+36
-38
stack/river/bin/river_cli.ml
+36
-38
stack/river/bin/river_cli.ml
···
+1
-2
stack/river/dune-project
+1
-2
stack/river/dune-project
+1
-2
stack/river/lib/client.ml
+1
-2
stack/river/lib/client.ml
+1
-2
stack/river/lib/client.mli
+1
-2
stack/river/lib/client.mli
+1
-1
stack/river/lib/dune
+1
-1
stack/river/lib/dune
···-(libraries eio eio_main requests requests_json_api logs str syndic lambdasoup uri ptime jsonfeed jsont bytesrw))+(libraries eio eio_main requests requests_json_api logs str syndic lambdasoup uri ptime jsonfeed jsont jsont.bytesrw cacheio xdge))
+264
-189
stack/river/lib/river_store.ml
+264
-189
stack/river/lib/river_store.ml
············+Some (Uri.to_string (List.find (fun l -> l.Syndic.Atom.rel = Syndic.Atom.Alternate) atom_entry.links).href)··················-| Some t -> List.filter (fun e -> Ptime.is_later e.updated ~than:t || Ptime.equal e.updated t) entries-| Some t -> List.filter (fun e -> Ptime.is_earlier e.updated ~than:t || Ptime.equal e.updated t) entries·····················+let updated = Jsonfeed.Item.date_modified item |> Option.value ~default:entry.meta.stored_at in···let feed_id = Uri.of_string ("urn:river:archive:" ^ (Digest.string feed_url |> Digest.to_hex)) in···-Format.fprintf fmt "Link: %s@," (match entry.link with Some u -> Uri.to_string u | None -> "none");+Format.fprintf fmt "Title: %s@," (Jsonfeed.Item.title item |> Option.value ~default:"(no title)");···
+13
-38
stack/river/lib/river_store.mli
+13
-38
stack/river/lib/river_store.mli
···
+1
-2
stack/river/river.opam
+1
-2
stack/river/river.opam
+1
-1
stack/typesense-client/dune
+1
-1
stack/typesense-client/dune
+2
-1
stack/typesense-client/dune-project
+2
-1
stack/typesense-client/dune-project
+2
-1
stack/typesense-client/typesense-client.opam
+2
-1
stack/typesense-client/typesense-client.opam
+178
-100
stack/typesense-client/typesense_client.ml
+178
-100
stack/typesense-client/typesense_client.ml
···-requests_session: (float Eio.Time.clock_ty Eio.Resource.t, 'net Eio.Net.ty Eio.Resource.t) Requests.t;·········-(match Requests_json_api.parse_json_result (parse_search_response collection_name) response_str with······-let body = Ezjsonm.dict [("searches", Ezjsonm.list (fun x -> x) searches)] |> Ezjsonm.value_to_string inlet combine_multisearch_results (multisearch_resp : multisearch_response) ?(limit=10) ?(offset=0) () =···(** Extract field value from JSON document as string list or return empty list if not found *)
+13
-6
stack/typesense-client/typesense_client.mli
+13
-6
stack/typesense-client/typesense_client.mli
···-requests_session:(float Eio.Time.clock_ty Eio.Resource.t, [`Generic | `Unix] Eio.Net.ty Eio.Resource.t) Requests.t ->······
+1
-1
stack/zotero-translation/dune
+1
-1
stack/zotero-translation/dune
+2
-1
stack/zotero-translation/dune-project
+2
-1
stack/zotero-translation/dune-project
+2
-1
stack/zotero-translation/zotero-translation.opam
+2
-1
stack/zotero-translation/zotero-translation.opam
+107
-60
stack/zotero-translation/zotero_translation.ml
+107
-60
stack/zotero-translation/zotero_translation.ml
···(* From the ZTS source code: https://github.com/zotero/translation-server/blob/master/src/formats.js···-failwith "Zotero_translation.v is deprecated. Use Zotero_translation.create ~sw ~env base_uri instead"············let f = Bibtex.fields bib |> Bibtex.SM.bindings |> List.map (fun (k,v) -> k, (unescape_bibtex v)) in+let v = List.fold_left (fun acc (k,v) -> ((k, Jsont.Meta.none), Jsont.String (v, Jsont.Meta.none))::acc)+(match keywords with [] -> None | _ -> Some (Jsont.Array (keywords, Jsont.Meta.none))) json' in-let authors m = add_if_present "author" (fun j -> J.get_list J.get_string j |> String.concat " and ") m in+let authors m = add_if_present "author" (fun j -> get_list get_string j |> String.concat " and ") m inlet fields = authors fields |> string "title" |> string "doi" |> string "month" |> string "year" |> string "url" in| "article" -> string "journal" fields |> string "volume" |> string "number" |> string "pages"···
+15
-23
stack/zotero-translation/zotero_translation.mli
+15
-23
stack/zotero-translation/zotero_translation.mli
······-val resolve_doi: ([> float Eio.Time.clock_ty ] Eio.Resource.t, [> [> `Generic ] Eio.Net.ty ] Eio.Resource.t) t ->-val resolve_url: ([> float Eio.Time.clock_ty ] Eio.Resource.t, [> [> `Generic ] Eio.Net.ty ] Eio.Resource.t) t ->-val search_id: ([> float Eio.Time.clock_ty ] Eio.Resource.t, [> [> `Generic ] Eio.Net.ty ] Eio.Resource.t) t ->-val export: ([> float Eio.Time.clock_ty ] Eio.Resource.t, [> [> `Generic ] Eio.Net.ty ] Eio.Resource.t) t ->-val json_of_doi : ([> float Eio.Time.clock_ty ] Eio.Resource.t, [> [> `Generic ] Eio.Net.ty ] Eio.Resource.t) t ->
+217
stack/zulip/ARCHITECTURE.md
+217
stack/zulip/ARCHITECTURE.md
···+The Zulip OCaml library follows a clean, layered architecture that separates protocol types, encoding concerns, and HTTP communication.+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ+|> Jsont.Object.mem "history_public_to_subscribers" Jsont.bool ~enc:history_public_to_subscribers+The `Encode` module provides clean utilities for converting between OCaml types and wire formats:
-689
stack/zulip/CLAUDE.md
-689
stack/zulip/CLAUDE.md
···-The library follows OCaml best practices with abstract types (`type t`) per module, comprehensive constructors/accessors, and proper pretty printers. Each core concept gets its own module with a clean interface.-val edit : Client.t -> message_id:int -> ?content:string -> ?topic:string -> unit -> (unit, Error.t) result-val get_events : t -> Client.t -> ?last_event_id:int -> unit -> (Event.t list, Error.t) result-- https://github.com/zulip/python-zulip-api/blob/main/zulip_botserver/zulip_botserver/server.py-Bot handlers require direct access to the EIO environment for legitimate I/O operations beyond HTTP requests to Zulip:-Based on analysis, the current EIO environment plumbing is **essential** and should be cleaned up:-val handle_message : t -> #Eio.Env.t -> Message_context.t -> (Response.t, Zulip.Error.t) result-This design maintains flexibility for real-world bot functionality while providing clean, type-safe interfaces.-- Bot framework: https://github.com/zulip/python-zulip-api/blob/main/zulip_bots/zulip_bots/lib.py-- Bot server: https://github.com/zulip/python-zulip-api/blob/main/zulip_botserver/zulip_botserver/server.py-The design adapts these Python patterns to idiomatic OCaml with abstract types, proper error handling, and EIO's structured concurrency for robust, type-safe Zulip integration.
+2
-2
stack/zulip/dune-project
+2
-2
stack/zulip/dune-project
·········
+2
-5
stack/zulip/examples/example.ml
+2
-5
stack/zulip/examples/example.ml
···
+4
-3
stack/zulip/examples/test_client.ml
+4
-3
stack/zulip/examples/test_client.ml
···
+25
-33
stack/zulip/lib/zulip/lib/channel.ml
+25
-33
stack/zulip/lib/zulip/lib/channel.ml
···-Error (Zulip_types.create_error ~code:(Other "json_parse_error") ~msg:"Channel JSON must be an object" ())-Error (Zulip_types.create_error ~code:(Other "json_parse_error") ~msg:("Channel JSON parsing failed: " ^ Printexc.to_string exn) ())+|> Jsont.Object.mem "history_public_to_subscribers" Jsont.bool ~enc:history_public_to_subscribers
+17
-6
stack/zulip/lib/zulip/lib/channel.mli
+17
-6
stack/zulip/lib/zulip/lib/channel.mli
···
+53
-43
stack/zulip/lib/zulip/lib/channels.ml
+53
-43
stack/zulip/lib/zulip/lib/channels.ml
···match Client.request client ~method_:`DELETE ~path:("/api/v1/streams/" ^ encoded_name) () with-| _ -> Error (Zulip_types.create_error ~code:(Other "api_error") ~msg:"Invalid streams response format" ()))-| _ -> Error (Zulip_types.create_error ~code:(Other "api_error") ~msg:"Streams response must be an object" ()))-match Client.request client ~method_:`POST ~path:"/api/v1/users/me/subscriptions" ~body () with+match Client.request client ~method_:`POST ~path:"/api/v1/users/me/subscriptions" ~body ~content_type () with-match Client.request client ~method_:`DELETE ~path:"/api/v1/users/me/subscriptions" ~body () with+match Client.request client ~method_:`DELETE ~path:"/api/v1/users/me/subscriptions" ~body ~content_type () with
+17
-16
stack/zulip/lib/zulip/lib/client.ml
+17
-16
stack/zulip/lib/zulip/lib/client.ml
·········
+1
-1
stack/zulip/lib/zulip/lib/dune
+1
-1
stack/zulip/lib/zulip/lib/dune
+56
stack/zulip/lib/zulip/lib/encode.ml
+56
stack/zulip/lib/zulip/lib/encode.ml
···
+21
stack/zulip/lib/zulip/lib/encode.mli
+21
stack/zulip/lib/zulip/lib/encode.mli
···
+35
-24
stack/zulip/lib/zulip/lib/event.ml
+35
-24
stack/zulip/lib/zulip/lib/event.ml
···-Error (Zulip_types.create_error ~code:(Other "json_parse_error") ~msg:"Event JSON must be an object" ())-Error (Zulip_types.create_error ~code:(Other "json_parse_error") ~msg:("Event JSON parsing failed: " ^ Printexc.to_string exn) ())
+76
-42
stack/zulip/lib/zulip/lib/event_queue.ml
+76
-42
stack/zulip/lib/zulip/lib/event_queue.ml
···-| _ -> Error (Zulip_types.create_error ~code:(Other "api_error") ~msg:"Invalid register response: missing queue_id" ()))-| _ -> Error (Zulip_types.create_error ~code:(Other "api_error") ~msg:"Register response must be an object" ()))+| Some types -> Log.debug (fun m -> m "Registering with event_types: %s" (String.concat "," types))+match Client.request client ~method_:`POST ~path:"/api/v1/register" ~body ~content_type () with+|> Jsont.Object.keep_unknown Jsont.json_mems ~enc:(fun _ -> Jsont.Object ([], Jsont.Meta.none))-Error (Zulip_types.create_error ~code:(Other "api_error") ~msg:"Invalid events response format" ())-| _ -> Error (Zulip_types.create_error ~code:(Other "api_error") ~msg:"Events response must be an object" ()))
-206
stack/zulip/lib/zulip/lib/jsonu.ml
-206
stack/zulip/lib/zulip/lib/jsonu.ml
···-| Some _ -> Error (Zulip_types.create_error ~code:(Other "json_type_error") ~msg:(Printf.sprintf "Field '%s' is not a string" key) ())-| None -> Error (Zulip_types.create_error ~code:(Other "json_missing_field") ~msg:(Printf.sprintf "Field '%s' not found" key) ())-| None -> Error (Zulip_types.create_error ~code:(Other "json_missing_field") ~msg:(Printf.sprintf "Field '%s' not found" key) ())-| _ -> Error (Zulip_types.create_error ~code:(Other "json_type_error") ~msg:(Printf.sprintf "Field '%s' is not a valid float" key) ()))-| Some _ -> Error (Zulip_types.create_error ~code:(Other "json_type_error") ~msg:(Printf.sprintf "Field '%s' is not a float" key) ())-| None -> Error (Zulip_types.create_error ~code:(Other "json_missing_field") ~msg:(Printf.sprintf "Field '%s' not found" key) ())-| Some _ -> Error (Zulip_types.create_error ~code:(Other "json_type_error") ~msg:(Printf.sprintf "Field '%s' is not a boolean" key) ())-| None -> Error (Zulip_types.create_error ~code:(Other "json_missing_field") ~msg:(Printf.sprintf "Field '%s' not found" key) ())-| Some _ -> Error (Zulip_types.create_error ~code:(Other "json_type_error") ~msg:(Printf.sprintf "Field '%s' is not an object" key) ())-| None -> Error (Zulip_types.create_error ~code:(Other "json_missing_field") ~msg:(Printf.sprintf "Field '%s' not found" key) ())-| Some _ -> Error (Zulip_types.create_error ~code:(Other "json_type_error") ~msg:(Printf.sprintf "Field '%s' is not an array" key) ())-| None -> Error (Zulip_types.create_error ~code:(Other "json_missing_field") ~msg:(Printf.sprintf "Field '%s' not found" key) ())-| _ -> Error (Zulip_types.create_error ~code:(Other "json_type_error") ~msg:(Printf.sprintf "%s: expected JSON object" context) ())-| _ -> Error (Zulip_types.create_error ~code:(Other "json_type_error") ~msg:(Printf.sprintf "%s: expected JSON array" context) ())-Zulip_types.create_error ~code:(Other "json_missing_field") ~msg:(Printf.sprintf "Required field '%s' not found" field) ()-Zulip_types.create_error ~code:(Other "json_type_error") ~msg:(Printf.sprintf "Field '%s' type mismatch: expected %s" field expected) ()-| Failure msg -> Error (Zulip_types.create_error ~code:(Other "json_parse_error") ~msg:(Printf.sprintf "%s: %s" context msg) ())-| exn -> Error (Zulip_types.create_error ~code:(Other "json_parse_error") ~msg:(Printf.sprintf "%s: %s" context (Printexc.to_string exn)) ())
-117
stack/zulip/lib/zulip/lib/jsonu.mli
-117
stack/zulip/lib/zulip/lib/jsonu.mli
···-val get_object : (string * json) list -> string -> ((string * json) list, Zulip_types.zerror) result-val with_object : string -> ((string * json) list -> ('a, Zulip_types.zerror) result) -> json -> ('a, Zulip_types.zerror) result-val with_array : string -> (json -> ('a, Zulip_types.zerror) result) -> json -> ('a list, Zulip_types.zerror) result-val parse_with_error : string -> (unit -> ('a, Zulip_types.zerror) result) -> ('a, Zulip_types.zerror) result
-144
stack/zulip/lib/zulip/lib/jsonu_syntax.ml
-144
stack/zulip/lib/zulip/lib/jsonu_syntax.ml
···-| None -> Error (Zulip_types.create_error ~code:(Other "missing_field") ~msg:(Printf.sprintf "Required field '%s' not found" name) ())-with _ -> Error (Zulip_types.create_error ~code:(Other "type_error") ~msg:"Expected integer" ()))-with _ -> Error (Zulip_types.create_error ~code:(Other "type_error") ~msg:"Expected float" ()))-| Error e -> Error (Zulip_types.create_error ~code:(Zulip_types.error_code e) ~msg:(Printf.sprintf "%s: %s" ctx (Zulip_types.error_message e)) ())
-96
stack/zulip/lib/zulip/lib/jsonu_syntax.mli
-96
stack/zulip/lib/zulip/lib/jsonu_syntax.mli
···-val ( and+++ ) : ('a * 'b * 'c, 'e) result -> ('d, 'e) result -> ('a * 'b * 'c * 'd, 'e) result-val ( and++++ ) : ('a * 'b * 'c * 'd, 'e) result -> ('f, 'e) result -> ('a * 'b * 'c * 'd * 'f, 'e) result-val field_opt : (string * json) list -> string -> 'a parser -> ('a option, Zulip_types.zerror) result-val field_or : (string * json) list -> string -> 'a parser -> 'a -> ('a, Zulip_types.zerror) result-val traverse : ('a -> ('b, Zulip_types.zerror) result) -> 'a list -> ('b list, Zulip_types.zerror) result
+35
-21
stack/zulip/lib/zulip/lib/message.ml
+35
-21
stack/zulip/lib/zulip/lib/message.ml
···
+18
-9
stack/zulip/lib/zulip/lib/message.mli
+18
-9
stack/zulip/lib/zulip/lib/message.mli
······
+20
-8
stack/zulip/lib/zulip/lib/message_response.ml
+20
-8
stack/zulip/lib/zulip/lib/message_response.ml
···+|> Jsont.Object.opt_mem "automatic_new_visibility_policy" Jsont.string ~enc:automatic_new_visibility_policy-let automatic_new_visibility_policy = Jsonu.get_string_opt fields "automatic_new_visibility_policy" in
+5
stack/zulip/lib/zulip/lib/message_response.mli
+5
stack/zulip/lib/zulip/lib/message_response.mli
···
+15
-62
stack/zulip/lib/zulip/lib/messages.ml
+15
-62
stack/zulip/lib/zulip/lib/messages.ml
···match Client.request client ~method_:`PATCH ~path:("/api/v1/messages/" ^ string_of_int message_id) ~params () with···-Buffer.add_string body ("Content-Disposition: form-data; name=\"file\"; filename=\"" ^ basename ^ "\"\r\n");-| _ -> Error (Zulip_types.create_error ~code:(Zulip_types.Other "upload_error") ~msg:"Failed to parse upload response" ()))
+25
-18
stack/zulip/lib/zulip/lib/user.ml
+25
-18
stack/zulip/lib/zulip/lib/user.ml
···
+18
-7
stack/zulip/lib/zulip/lib/user.mli
+18
-7
stack/zulip/lib/zulip/lib/user.mli
······
+32
-28
stack/zulip/lib/zulip/lib/users.ml
+32
-28
stack/zulip/lib/zulip/lib/users.ml
···-| _ -> Error (Zulip_types.create_error ~code:(Other "api_error") ~msg:"Invalid users response format" ()))-| _ -> Error (Zulip_types.create_error ~code:(Other "api_error") ~msg:"Users response must be an object" ()))···
+1
-5
stack/zulip/lib/zulip/lib/zulip.ml
+1
-5
stack/zulip/lib/zulip/lib/zulip.ml
+2
-10
stack/zulip/lib/zulip/lib/zulip.mli
+2
-10
stack/zulip/lib/zulip/lib/zulip.mli
···
+32
-15
stack/zulip/lib/zulip/lib/zulip_types.ml
+32
-15
stack/zulip/lib/zulip/lib/zulip_types.ml
···-type json = [`Null | `Bool of bool | `Float of float | `String of string | `A of json list | `O of (string * json) list]···
+6
-1
stack/zulip/lib/zulip/lib/zulip_types.mli
+6
-1
stack/zulip/lib/zulip/lib/zulip_types.mli
···-type json = [`Null | `Bool of bool | `Float of float | `String of string | `A of json list | `O of (string * json) list]···
+6
-5
stack/zulip/lib/zulip_bot/lib/bot_runner.ml
+6
-5
stack/zulip/lib/zulip_bot/lib/bot_runner.ml
······
+54
-30
stack/zulip/lib/zulip_bot/lib/bot_storage.ml
+54
-30
stack/zulip/lib/zulip_bot/lib/bot_storage.ml
·····················
+1
-1
stack/zulip/lib/zulip_bot/lib/dune
+1
-1
stack/zulip/lib/zulip_bot/lib/dune
+192
-88
stack/zulip/lib/zulip_bot/lib/message.ml
+192
-88
stack/zulip/lib/zulip_bot/lib/message.ml
··················+(match (get_int "id", get_int "sender_id", get_string "sender_email", get_string "sender_full_name") with-let submessages = Zulip.Jsonu.get_array_opt fields "submessages" |> Option.value ~default:[] in+| _ -> Error (Zulip.create_error ~code:(Other "json_parse_error") ~msg:"Missing required message fields" ()))+| _ -> Error (Zulip.create_error ~code:(Other "json_parse_error") ~msg:"Expected JSON object for message" ())-Log.warn (fun m -> m "Failed to parse user in display_recipient: %s" (Zulip.error_message err));+Log.warn (fun m -> m "Failed to parse user in display_recipient: %s" (Zulip.error_message err));···
+10
stack/zulip/lib/zulip_bot/lib/message.mli
+10
stack/zulip/lib/zulip_bot/lib/message.mli
······
+30
stack/zulip/zulip.opam
+30
stack/zulip/zulip.opam
···
+26
stack/zulip/zulip_bot.opam
+26
stack/zulip/zulip_bot.opam
···
+29
stack/zulip/zulip_botserver.opam
+29
stack/zulip/zulip_botserver.opam
···