Download src/hk_api_browser_h.erl from Snapkitty/hyperkitty-chromium: direct link, hf CLI and curl.
- Browser
- Download file 7.66 kB
-
https://huggingface.co/Snapkitty/hyperkitty-chromium/resolve/main/src/hk_api_browser_h.erl
- Command line
-
hf download hf://Snapkitty/hyperkitty-chromium/src/hk_api_browser_h.erl
-
curl -L -o hk_api_browser_h.erl https://huggingface.co/Snapkitty/hyperkitty-chromium/resolve/main/src/hk_api_browser_h.erl
7.66 kB
| % SPDX-License-Identifier: LicenseRef-Sovereign-Leviathan-SL-AGPL3-001 | |
| % Licensing: see LICENSE in the repository root. | |
| %%%------------------------------------------------------------------- | |
| %%% @doc `/browser' API domain -- a direct, session/tab-scoped path | |
| %%% to the same typed operations `hk_agent_toolbox' calls for an | |
| %%% agent. This is for operator/manual use (opening a session from | |
| %%% the frontend's "browser" view without an agent involved) and for | |
| %%% tests; it goes through the *same* `hk_browser_session' module | |
| %%% and therefore the *same* structured events as agent-driven | |
| %%% browser use -- there is exactly one code path that talks to | |
| %%% Chromium, regardless of who asked. | |
| %%% | |
| %%% POST /api/browser/sessions create | |
| %%% GET /api/browser/sessions list | |
| %%% GET /api/browser/sessions/:id describe | |
| %%% POST /api/browser/sessions/:id/close close | |
| %%% POST /api/browser/sessions/:id/tabs open a tab | |
| %%% POST /api/browser/sessions/:id/tabs/:tab_id/navigate navigate | |
| %%% POST /api/browser/sessions/:id/tabs/:tab_id/actions {op, args}: | |
| %%% op in back|forward|reload|read_page|query_element|click|type| | |
| %%% scroll|screenshot|extract_links|close | |
| %%% @end | |
| %%%------------------------------------------------------------------- | |
| -module(hk_api_browser_h). | |
| -export([init/2]). | |
| init(Req0 = #{method := <<"POST">>}, collection) -> | |
| RequestId = hk_api_util:request_id(Req0), | |
| {ok, Body, Req1} = hk_api_util:read_json_body(Req0), | |
| OwnerAgentId = maps:get(<<"owner_agent_id">>, Body, undefined), | |
| Profile = maps:get(<<"profile">>, Body, <<"manual">>), | |
| Req = hk_api_util:run_operation(browser_create_session, RequestId, fun() -> | |
| SessionId = hk_id:new(<<"sess">>), | |
| case hk_browser_session_sup:start_session(#{session_id => SessionId, | |
| owner_agent_id => OwnerAgentId, profile => Profile}) of | |
| {ok, _Pid} -> {ok, {browser_session, SessionId}, #{session_id => SessionId}}; | |
| {error, Reason} -> {error, Reason} | |
| end | |
| end, Req1), | |
| {ok, Req, collection}; | |
| init(Req0 = #{method := <<"GET">>}, collection) -> | |
| Sessions = [describe_or_skip(Id) || {Id, _Pid} <- hk_browser_registry:list_sessions()], | |
| Req = hk_api_util:reply_json(200, #{sessions => [M || {true, M} <- Sessions]}, Req0), | |
| {ok, Req, collection}; | |
| init(Req0 = #{method := <<"GET">>}, item) -> | |
| SessionId = cowboy_req:binding(id, Req0), | |
| Req = case hk_browser_registry:lookup(SessionId) of | |
| {ok, Pid} -> | |
| {ok, Session} = hk_browser_session:describe(Pid), | |
| hk_api_util:reply_json(200, hk_schema:to_map(Session), Req0); | |
| {error, not_found} -> | |
| hk_api_util:reply_error(404, session_not_found, Req0) | |
| end, | |
| {ok, Req, item}; | |
| init(Req0 = #{method := <<"POST">>}, close) -> | |
| SessionId = cowboy_req:binding(id, Req0), | |
| RequestId = hk_api_util:request_id(Req0), | |
| Req = hk_api_util:run_operation(browser_close_session, RequestId, fun() -> | |
| with_session(SessionId, fun(Pid) -> | |
| hk_browser_session:close_session(Pid), | |
| {ok, {browser_session, SessionId}, #{}} | |
| end) | |
| end, Req0), | |
| {ok, Req, close}; | |
| init(Req0 = #{method := <<"POST">>}, tabs) -> | |
| SessionId = cowboy_req:binding(id, Req0), | |
| RequestId = hk_api_util:request_id(Req0), | |
| {ok, Body, Req1} = hk_api_util:read_json_body(Req0), | |
| Url = maps:get(<<"url">>, Body, <<"about:blank">>), | |
| Req = hk_api_util:run_operation(browser_open_tab, RequestId, fun() -> | |
| with_session(SessionId, fun(Pid) -> | |
| case hk_browser_session:open_tab(Pid, Url) of | |
| {ok, TabId} -> {ok, {browser_tab, TabId}, #{tab_id => TabId}}; | |
| {error, Reason} -> {error, Reason} | |
| end | |
| end) | |
| end, Req1), | |
| {ok, Req, tabs}; | |
| init(Req0 = #{method := <<"POST">>}, navigate) -> | |
| SessionId = cowboy_req:binding(id, Req0), | |
| TabId = cowboy_req:binding(tab_id, Req0), | |
| RequestId = hk_api_util:request_id(Req0), | |
| {ok, Body, Req1} = hk_api_util:read_json_body(Req0), | |
| Url = maps:get(<<"url">>, Body, <<>>), | |
| Req = hk_api_util:run_operation(browser_navigate, RequestId, fun() -> | |
| with_session(SessionId, fun(Pid) -> | |
| case hk_browser_session:navigate(Pid, TabId, Url) of | |
| ok -> {ok, {browser_tab, TabId}, #{}}; | |
| {error, Reason} -> {error, Reason} | |
| end | |
| end) | |
| end, Req1), | |
| {ok, Req, navigate}; | |
| init(Req0 = #{method := <<"POST">>}, actions) -> | |
| SessionId = cowboy_req:binding(id, Req0), | |
| TabId = cowboy_req:binding(tab_id, Req0), | |
| RequestId = hk_api_util:request_id(Req0), | |
| {ok, Body, Req1} = hk_api_util:read_json_body(Req0), | |
| Op = binary_to_atom(maps:get(<<"op">>, Body, <<"read_page">>), utf8), | |
| Args = maps:get(<<"args">>, Body, #{}), | |
| Req = hk_api_util:run_operation(browser_tab_action, RequestId, fun() -> | |
| with_session(SessionId, fun(Pid) -> dispatch(Pid, Op, TabId, Args) end) | |
| end, Req1), | |
| {ok, Req, actions}; | |
| init(Req0, State) -> | |
| Req = hk_api_util:reply_error(405, method_not_allowed, Req0), | |
| {ok, Req, State}. | |
| %%% internal | |
| with_session(undefined, _Fun) -> {error, missing_session_id}; | |
| with_session(SessionId, Fun) -> | |
| case hk_browser_registry:lookup(SessionId) of | |
| {ok, Pid} -> Fun(Pid); | |
| {error, not_found} -> {error, session_not_found} | |
| end. | |
| dispatch(Pid, back, TabId, _Args) -> as_op(hk_browser_session:back(Pid, TabId), TabId); | |
| dispatch(Pid, forward, TabId, _Args) -> as_op(hk_browser_session:forward(Pid, TabId), TabId); | |
| dispatch(Pid, reload, TabId, _Args) -> as_op(hk_browser_session:reload(Pid, TabId), TabId); | |
| dispatch(Pid, close, TabId, _Args) -> as_op(hk_browser_session:close_tab(Pid, TabId), TabId); | |
| dispatch(Pid, read_page, TabId, _Args) -> | |
| as_result(hk_browser_session:read_page(Pid, TabId), TabId, text); | |
| dispatch(Pid, query_element, TabId, Args) -> | |
| Selector = maps:get(<<"selector">>, Args, <<>>), | |
| as_result(hk_browser_session:query_element(Pid, TabId, Selector), TabId, element); | |
| dispatch(Pid, click, TabId, Args) -> | |
| Selector = maps:get(<<"selector">>, Args, <<>>), | |
| as_result(hk_browser_session:click(Pid, TabId, Selector), TabId, click); | |
| dispatch(Pid, type, TabId, Args) -> | |
| Selector = maps:get(<<"selector">>, Args, <<>>), | |
| Text = maps:get(<<"text">>, Args, <<>>), | |
| as_result(hk_browser_session:type(Pid, TabId, Selector, Text), TabId, type); | |
| dispatch(Pid, scroll, TabId, Args) -> | |
| Dy = maps:get(<<"delta_y">>, Args, 0), | |
| as_result(hk_browser_session:scroll(Pid, TabId, Dy), TabId, scroll); | |
| dispatch(Pid, screenshot, TabId, _Args) -> | |
| as_result(hk_browser_session:screenshot(Pid, TabId), TabId, screenshot_base64); | |
| dispatch(Pid, extract_links, TabId, _Args) -> | |
| as_result(hk_browser_session:extract_links(Pid, TabId), TabId, links); | |
| dispatch(_Pid, Op, _TabId, _Args) -> | |
| {error, {unsupported_op, Op}}. | |
| as_op(ok, TabId) -> {ok, {browser_tab, TabId}, #{}}; | |
| as_op({error, Reason}, _TabId) -> {error, Reason}. | |
| as_result({ok, Value}, TabId, Key) -> {ok, {browser_tab, TabId}, #{Key => Value}}; | |
| as_result({error, Reason}, _TabId, _Key) -> {error, Reason}. | |
| describe_or_skip(SessionId) -> | |
| case hk_browser_registry:lookup(SessionId) of | |
| {ok, Pid} -> | |
| case catch hk_browser_session:describe(Pid) of | |
| {ok, Session} -> {true, hk_schema:to_map(Session)}; | |
| _ -> false | |
| end; | |
| _ -> false | |
| end. | |