From e4f400962f7963e578e4ab125d2b5ad1855183d9 Mon Sep 17 00:00:00 2001 From: haikdc Date: Sat, 18 Apr 2026 02:35:08 -0700 Subject: [PATCH] [hAIk]: (add frontend bridge files for all backend sub-apps) (fix: collapse multi-param async thunk payloads into single objects) --- frontend/src/shared/backend/apps/agents.ts | 116 ++++---- .../src/shared/backend/apps/app_builder.ts | 240 +++++++++++++++++ .../src/shared/backend/apps/dashboards.ts | 121 +++++++++ frontend/src/shared/backend/apps/health.ts | 22 ++ frontend/src/shared/backend/apps/modes.ts | 136 ++++++++++ frontend/src/shared/backend/apps/settings.ts | 69 +++++ frontend/src/shared/backend/apps/skills.ts | 194 ++++++++++++++ .../src/shared/backend/apps/subscriptions.ts | 113 ++++++++ frontend/src/shared/backend/apps/tools.ts | 247 ++++++++++++++++++ 9 files changed, 1209 insertions(+), 49 deletions(-) create mode 100644 frontend/src/shared/backend/apps/app_builder.ts create mode 100644 frontend/src/shared/backend/apps/dashboards.ts create mode 100644 frontend/src/shared/backend/apps/health.ts create mode 100644 frontend/src/shared/backend/apps/modes.ts create mode 100644 frontend/src/shared/backend/apps/settings.ts create mode 100644 frontend/src/shared/backend/apps/skills.ts create mode 100644 frontend/src/shared/backend/apps/subscriptions.ts create mode 100644 frontend/src/shared/backend/apps/tools.ts diff --git a/frontend/src/shared/backend/apps/agents.ts b/frontend/src/shared/backend/apps/agents.ts index 7d4d04d7..c118ce97 100644 --- a/frontend/src/shared/backend/apps/agents.ts +++ b/frontend/src/shared/backend/apps/agents.ts @@ -65,11 +65,14 @@ export const LAUNCH_AGENT = createAsyncThunk( const update_system_prompt_endpoint: string = `${AGENTS_API}/update_system_prompt`; -async function update_system_prompt_function(sessionId: string, systemPrompt: string): Promise { +async function update_system_prompt_function(payload: { + sessionId: string; + systemPrompt: string; +}): Promise { const res = await fetch(update_system_prompt_endpoint, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ session_id: sessionId, system_prompt: systemPrompt }), + body: JSON.stringify({ session_id: payload.sessionId, system_prompt: payload.systemPrompt }), }); const data = await res.json(); return data.ok as string; @@ -105,34 +108,34 @@ export const DELETE_SESSION = createAsyncThunk( const send_message_endpoint: string = `${AGENTS_API}/send_message`; -async function send_message_function( - sessionId: string, - prompt: string, - mode: string, - model: string, - provider: string, - images: string[], - contextPaths: string[], - forcedTools: string[], - attachedSkills: string[], - hidden: boolean, - selectedBrowserIds: string[] -): Promise { +async function send_message_function(payload: { + sessionId: string; + prompt: string; + mode: string; + model: string; + provider: string; + images: string[]; + contextPaths: string[]; + forcedTools: string[]; + attachedSkills: string[]; + hidden: boolean; + selectedBrowserIds: string[]; +}): Promise { const res = await fetch(send_message_endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - session_id: sessionId, - prompt: prompt, - mode: mode, - model: model, - provider: provider, - images: images, - context_paths: contextPaths, - forced_tools: forcedTools, - attached_skills: attachedSkills, - hidden: hidden, - selected_browser_ids: selectedBrowserIds, + session_id: payload.sessionId, + prompt: payload.prompt, + mode: payload.mode, + model: payload.model, + provider: payload.provider, + images: payload.images, + context_paths: payload.contextPaths, + forced_tools: payload.forcedTools, + attached_skills: payload.attachedSkills, + hidden: payload.hidden, + selected_browser_ids: payload.selectedBrowserIds, }), }); const data = await res.json(); @@ -163,24 +166,24 @@ export const STOP_AGENT = createAsyncThunk( const handle_approval_endpoint: string = `${AGENTS_API}/handle_approval`; -async function handle_approval_function( - requestId: string, - behavior: 'allow' | 'deny', - message?: string, - updatedInput?: Record -): Promise<{ requestId: string; behavior: 'allow' | 'deny' }> { +async function handle_approval_function(payload: { + requestId: string; + behavior: 'allow' | 'deny'; + message?: string; + updatedInput?: Record; +}): Promise<{ requestId: string; behavior: 'allow' | 'deny' }> { const res = await fetch(handle_approval_endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - request_id: requestId, - behavior: behavior, - message: message, - updated_input: updatedInput, + request_id: payload.requestId, + behavior: payload.behavior, + message: payload.message, + updated_input: payload.updatedInput, }), }); if (!res.ok) throw new Error(`Approval request failed (${res.status})`); - return { requestId, behavior }; + return { requestId: payload.requestId, behavior: payload.behavior }; } export const HANDLE_APPROVAL = createAsyncThunk( handle_approval_endpoint, @@ -197,14 +200,18 @@ export const HANDLE_APPROVAL = createAsyncThunk( const edit_message_endpoint: string = `${AGENTS_API}/edit_message`; -async function edit_message_function(sessionId: string, messageId: string, content: string): Promise { +async function edit_message_function(payload: { + sessionId: string; + messageId: string; + content: string; +}): Promise { const res = await fetch(edit_message_endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - session_id: sessionId, - message_id: messageId, - content: content, + session_id: payload.sessionId, + message_id: payload.messageId, + content: payload.content, }), }); const data = await res.json(); @@ -217,11 +224,14 @@ export const EDIT_MESSAGE = createAsyncThunk( const switch_branch_endpoint: string = `${AGENTS_API}/switch_branch`; -async function switch_branch_function(sessionId: string, branchId: string): Promise { +async function switch_branch_function(payload: { + sessionId: string; + branchId: string; +}): Promise { const res = await fetch(switch_branch_endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ session_id: sessionId, branch_id: branchId }), + body: JSON.stringify({ session_id: payload.sessionId, branch_id: payload.branchId }), }); const data = await res.json(); return data.session as AgentSession; @@ -274,14 +284,18 @@ export const RESUME_SESSION = createAsyncThunk( const duplicate_session_endpoint: string = `${AGENTS_API}/duplicate_session`; -async function duplicate_session_function(sessionId: string, dashboardId: string, upToMessageId: string): Promise { +async function duplicate_session_function(payload: { + sessionId: string; + dashboardId: string; + upToMessageId: string; +}): Promise { const res = await fetch(duplicate_session_endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - session_id: sessionId, - dashboard_id: dashboardId, - up_to_message_id: upToMessageId, + session_id: payload.sessionId, + dashboard_id: payload.dashboardId, + up_to_message_id: payload.upToMessageId, }), }); const data = await res.json(); @@ -295,11 +309,15 @@ export const DUPLICATE_SESSION = createAsyncThunk( const get_history_endpoint: string = `${AGENTS_API}/get_history`; -async function get_history_function(q: string, limit: number, offset: number): Promise { +async function get_history_function(payload: { + q: string; + limit: number; + offset: number; +}): Promise { const res = await fetch(get_history_endpoint, { method: 'GET', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ q, limit, offset }), + body: JSON.stringify({ q: payload.q, limit: payload.limit, offset: payload.offset }), }); const data = await res.json(); return data.sessions as HistorySession[]; diff --git a/frontend/src/shared/backend/apps/app_builder.ts b/frontend/src/shared/backend/apps/app_builder.ts new file mode 100644 index 00000000..60a0233e --- /dev/null +++ b/frontend/src/shared/backend/apps/app_builder.ts @@ -0,0 +1,240 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; +import { API_BASE } from '@/shared/backend/base_routes'; + +const APP_BUILDER_API: string = `${API_BASE}/app_builder`; + +// --------------------------------------------------------------------------- +// File serving +// --------------------------------------------------------------------------- + + +const serve_app_source_file_endpoint: string = `${APP_BUILDER_API}/app/source_dir`; +async function serve_app_source_file_function(appId: string, filepath: string): Promise { + const res = await fetch(`${APP_BUILDER_API}/app/${appId}/source_dir/${filepath}`, { + method: 'GET', + }); + const text = await res.text(); + return text; +} +export const SERVE_APP_SOURCE_FILE = createAsyncThunk( + serve_app_source_file_endpoint, + serve_app_source_file_function, +); + + +const serve_app_file_endpoint: string = `${APP_BUILDER_API}/serve`; +async function serve_app_file_function(appId: string, filepath: string): Promise { + const res = await fetch(`${APP_BUILDER_API}/${appId}/serve/${filepath}`, { + method: 'GET', + }); + const text = await res.text(); + return text; +} +export const SERVE_APP_FILE = createAsyncThunk( + serve_app_file_endpoint, + serve_app_file_function, +); + + +// --------------------------------------------------------------------------- +// Workspace management +// --------------------------------------------------------------------------- + + +const read_app_endpoint: string = `${APP_BUILDER_API}/app`; +async function read_app_function(appId: string): Promise<{ files: Record; meta: Record | null }> { + const res = await fetch(`${APP_BUILDER_API}/app/${appId}`, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { files: Record; meta: Record | null }; +} +export const READ_APP = createAsyncThunk( + read_app_endpoint, + read_app_function, +); + + +const seed_app_endpoint: string = `${APP_BUILDER_API}/app/seed`; +async function seed_app_function(body: { + app_id: string; + files?: Record | null; + meta?: Record | null; +}): Promise<{ path: string }> { + const res = await fetch(seed_app_endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + const data = await res.json(); + return data as { path: string }; +} +export const SEED_APP = createAsyncThunk( + seed_app_endpoint, + seed_app_function, +); + + +const write_app_file_endpoint: string = `${APP_BUILDER_API}/app/file`; +async function write_app_file_function(appId: string, filepath: string, content: string): Promise<{ ok: boolean }> { + const res = await fetch(`${APP_BUILDER_API}/app/${appId}/file/${filepath}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ content }), + }); + const data = await res.json(); + return data as { ok: boolean }; +} +export const WRITE_APP_FILE = createAsyncThunk( + write_app_file_endpoint, + write_app_file_function, +); + + +const delete_app_file_endpoint: string = `${APP_BUILDER_API}/app/file/delete`; +async function delete_app_file_function(appId: string, filepath: string): Promise<{ ok: boolean }> { + const res = await fetch(`${APP_BUILDER_API}/app/${appId}/file/${filepath}`, { + method: 'DELETE', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { ok: boolean }; +} +export const DELETE_APP_FILE = createAsyncThunk( + delete_app_file_endpoint, + delete_app_file_function, +); + + +// --------------------------------------------------------------------------- +// App CRUD +// --------------------------------------------------------------------------- + + +const list_apps_endpoint: string = `${APP_BUILDER_API}/list`; +async function list_apps_function(): Promise<{ apps: Record[] }> { + const res = await fetch(list_apps_endpoint, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { apps: Record[] }; +} +export const LIST_APPS = createAsyncThunk( + list_apps_endpoint, + list_apps_function, +); + + +const get_app_endpoint: string = `${APP_BUILDER_API}/get`; +async function get_app_function(appId: string): Promise> { + const res = await fetch(`${APP_BUILDER_API}/${appId}`, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as Record; +} +export const GET_APP = createAsyncThunk( + get_app_endpoint, + get_app_function, +); + + +const create_app_endpoint: string = `${APP_BUILDER_API}/create`; +async function create_app_function(body: { + name: string; + description?: string; + icon?: string; + files?: Record | null; + thumbnail?: string | null; +}): Promise<{ ok: boolean; app: Record }> { + const res = await fetch(create_app_endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + const data = await res.json(); + return data as { ok: boolean; app: Record }; +} +export const CREATE_APP = createAsyncThunk( + create_app_endpoint, + create_app_function, +); + + +const update_app_endpoint: string = `${APP_BUILDER_API}/update`; +async function update_app_function( + appId: string, + updates: { + name?: string; + description?: string; + icon?: string; + thumbnail?: string | null; + }, +): Promise<{ ok: boolean; app: Record }> { + const res = await fetch(`${APP_BUILDER_API}/${appId}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(updates), + }); + const data = await res.json(); + return data as { ok: boolean; app: Record }; +} +export const UPDATE_APP = createAsyncThunk( + update_app_endpoint, + update_app_function, +); + + +const delete_app_endpoint: string = `${APP_BUILDER_API}/delete`; +async function delete_app_function(appId: string): Promise<{ ok: boolean }> { + const res = await fetch(`${APP_BUILDER_API}/${appId}`, { + method: 'DELETE', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { ok: boolean }; +} +export const DELETE_APP = createAsyncThunk( + delete_app_endpoint, + delete_app_function, +); + + +// --------------------------------------------------------------------------- +// Execution +// --------------------------------------------------------------------------- + + +const execute_app_endpoint: string = `${APP_BUILDER_API}/execute`; +async function execute_app_function(appId: string): Promise<{ + app_id: string; + app_name: string; + frontend_code: string; + backend_result: Record | null; + stdout: string | null; + stderr: string | null; + error: string | null; +}> { + const res = await fetch(execute_app_endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ app_id: appId }), + }); + const data = await res.json(); + return data as { + app_id: string; + app_name: string; + frontend_code: string; + backend_result: Record | null; + stdout: string | null; + stderr: string | null; + error: string | null; + }; +} +export const EXECUTE_APP = createAsyncThunk( + execute_app_endpoint, + execute_app_function, +); diff --git a/frontend/src/shared/backend/apps/dashboards.ts b/frontend/src/shared/backend/apps/dashboards.ts new file mode 100644 index 00000000..19678734 --- /dev/null +++ b/frontend/src/shared/backend/apps/dashboards.ts @@ -0,0 +1,121 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; +import { API_BASE } from '@/shared/backend/base_routes'; + +const DASHBOARDS_API: string = `${API_BASE}/dashboards`; + +// --------------------------------------------------------------------------- +// Dashboard CRUD +// --------------------------------------------------------------------------- + + +const list_dashboards_endpoint: string = `${DASHBOARDS_API}/list`; +async function list_dashboards_function(): Promise[]> { + const res = await fetch(list_dashboards_endpoint, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data.dashboards as Record[]; +} +export const LIST_DASHBOARDS = createAsyncThunk( + list_dashboards_endpoint, + list_dashboards_function, +); + + +const create_dashboard_endpoint: string = `${DASHBOARDS_API}/create`; +async function create_dashboard_function(name: string = 'Untitled Dashboard'): Promise> { + const res = await fetch(create_dashboard_endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ name }), + }); + const data = await res.json(); + return data as Record; +} +export const CREATE_DASHBOARD = createAsyncThunk( + create_dashboard_endpoint, + create_dashboard_function, +); + + +const generate_dashboard_name_endpoint: string = `${DASHBOARDS_API}/generate-name`; +async function generate_dashboard_name_function(dashboardId: string): Promise<{ name: string; auto_named: boolean }> { + const res = await fetch(`${DASHBOARDS_API}/${dashboardId}/generate-name`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { name: string; auto_named: boolean }; +} +export const GENERATE_DASHBOARD_NAME = createAsyncThunk( + generate_dashboard_name_endpoint, + generate_dashboard_name_function, +); + + +const get_dashboard_endpoint: string = `${DASHBOARDS_API}/get`; +async function get_dashboard_function(dashboardId: string): Promise> { + const res = await fetch(`${DASHBOARDS_API}/${dashboardId}`, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as Record; +} +export const GET_DASHBOARD = createAsyncThunk( + get_dashboard_endpoint, + get_dashboard_function, +); + + +const update_dashboard_endpoint: string = `${DASHBOARDS_API}/update`; +async function update_dashboard_function(args: { + dashboardId: string; + name?: string; + layout?: Record; + thumbnail?: string; +}): Promise> { + const { dashboardId, ...updates } = args; + const res = await fetch(`${DASHBOARDS_API}/${dashboardId}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(updates), + }); + const data = await res.json(); + return data as Record; +} +export const UPDATE_DASHBOARD = createAsyncThunk( + update_dashboard_endpoint, + update_dashboard_function, +); + + +const delete_dashboard_endpoint: string = `${DASHBOARDS_API}/delete`; +async function delete_dashboard_function(dashboardId: string): Promise<{ ok: boolean }> { + const res = await fetch(`${DASHBOARDS_API}/${dashboardId}`, { + method: 'DELETE', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { ok: boolean }; +} +export const DELETE_DASHBOARD = createAsyncThunk( + delete_dashboard_endpoint, + delete_dashboard_function, +); + + +const duplicate_dashboard_endpoint: string = `${DASHBOARDS_API}/duplicate`; +async function duplicate_dashboard_function(dashboardId: string): Promise> { + const res = await fetch(`${DASHBOARDS_API}/${dashboardId}/duplicate`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as Record; +} +export const DUPLICATE_DASHBOARD = createAsyncThunk( + duplicate_dashboard_endpoint, + duplicate_dashboard_function, +); diff --git a/frontend/src/shared/backend/apps/health.ts b/frontend/src/shared/backend/apps/health.ts new file mode 100644 index 00000000..79be8b96 --- /dev/null +++ b/frontend/src/shared/backend/apps/health.ts @@ -0,0 +1,22 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; +import { API_BASE } from '@/shared/backend/base_routes'; + +const HEALTH_API: string = `${API_BASE}/health`; + +// --------------------------------------------------------------------------- +// Health Check +// --------------------------------------------------------------------------- + + +const check_health_endpoint: string = `${HEALTH_API}/check`; +async function check_health_function(): Promise { + const res = await fetch(check_health_endpoint, { + method: 'GET', + }); + const text = await res.text(); + return text; +} +export const CHECK_HEALTH = createAsyncThunk( + check_health_endpoint, + check_health_function, +); diff --git a/frontend/src/shared/backend/apps/modes.ts b/frontend/src/shared/backend/apps/modes.ts new file mode 100644 index 00000000..00b66ea5 --- /dev/null +++ b/frontend/src/shared/backend/apps/modes.ts @@ -0,0 +1,136 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; +import { API_BASE } from '@/shared/backend/base_routes'; + +const MODES_API: string = `${API_BASE}/modes`; + +// --------------------------------------------------------------------------- +// Mode CRUD +// --------------------------------------------------------------------------- + + +const list_modes_endpoint: string = `${MODES_API}/list`; +async function list_modes_function(): Promise<{ modes: Record[]; builtin_defaults: Record> }> { + const res = await fetch(list_modes_endpoint, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { modes: Record[]; builtin_defaults: Record> }; +} +export const LIST_MODES = createAsyncThunk( + list_modes_endpoint, + list_modes_function, +); + + +const get_mode_endpoint: string = `${MODES_API}/get`; +async function get_mode_function(modeId: string): Promise> { + const res = await fetch(`${MODES_API}/${modeId}`, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as Record; +} +export const GET_MODE = createAsyncThunk( + get_mode_endpoint, + get_mode_function, +); + + +const create_mode_endpoint: string = `${MODES_API}/create`; +async function create_mode_function(body: { + name: string; + description?: string; + system_prompt?: string | null; + tools?: string[] | null; + default_next_mode?: string | null; + icon?: string; + color?: string; + default_folder?: string | null; +}): Promise<{ ok: boolean; mode: Record }> { + const res = await fetch(create_mode_endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + const data = await res.json(); + return data as { ok: boolean; mode: Record }; +} +export const CREATE_MODE = createAsyncThunk( + create_mode_endpoint, + create_mode_function, +); + + +const update_mode_endpoint: string = `${MODES_API}/update`; +async function update_mode_function(args: { + modeId: string; + name?: string; + description?: string; + system_prompt?: string | null; + tools?: string[] | null; + default_next_mode?: string | null; + icon?: string; + color?: string; + default_folder?: string | null; +}): Promise<{ ok: boolean; mode: Record }> { + const { modeId, ...updates } = args; + const res = await fetch(`${MODES_API}/${modeId}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(updates), + }); + const data = await res.json(); + return data as { ok: boolean; mode: Record }; +} +export const UPDATE_MODE = createAsyncThunk( + update_mode_endpoint, + update_mode_function, +); + + +const reset_mode_endpoint: string = `${MODES_API}/reset`; +async function reset_mode_function(modeId: string): Promise<{ ok: boolean; mode: Record }> { + const res = await fetch(`${MODES_API}/${modeId}/reset`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { ok: boolean; mode: Record }; +} +export const RESET_MODE = createAsyncThunk( + reset_mode_endpoint, + reset_mode_function, +); + + +const delete_mode_endpoint: string = `${MODES_API}/delete`; +async function delete_mode_function(modeId: string): Promise<{ ok: boolean }> { + const res = await fetch(`${MODES_API}/${modeId}`, { + method: 'DELETE', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { ok: boolean }; +} +export const DELETE_MODE = createAsyncThunk( + delete_mode_endpoint, + delete_mode_function, +); + + +const get_mode_by_id_endpoint: string = `${MODES_API}/get_mode_by_id`; +async function get_mode_by_id_function(modeId: string): Promise | null> { + const res = await fetch(get_mode_by_id_endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ mode_id: modeId }), + }); + const data = await res.json(); + return data as Record | null; +} +export const GET_MODE_BY_ID = createAsyncThunk( + get_mode_by_id_endpoint, + get_mode_by_id_function, +); diff --git a/frontend/src/shared/backend/apps/settings.ts b/frontend/src/shared/backend/apps/settings.ts new file mode 100644 index 00000000..d3c461f2 --- /dev/null +++ b/frontend/src/shared/backend/apps/settings.ts @@ -0,0 +1,69 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; +import { API_BASE } from '@/shared/backend/base_routes'; + +const SETTINGS_API: string = `${API_BASE}/settings`; + +// --------------------------------------------------------------------------- +// Settings +// --------------------------------------------------------------------------- + + +const get_settings_endpoint: string = SETTINGS_API; +async function get_settings_function(): Promise> { + const res = await fetch(get_settings_endpoint, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as Record; +} +export const GET_SETTINGS = createAsyncThunk( + get_settings_endpoint, + get_settings_function, +); + + +const update_settings_endpoint: string = SETTINGS_API; +async function update_settings_function(body: { + default_system_prompt?: string; + default_folder?: string | null; + default_model?: string; + default_mode?: string; + default_max_turns?: number | null; + anthropic_api_key?: string | null; + zoom_sensitivity?: number; + theme?: string; + new_agent_shortcut?: string; + browser_homepage?: string; + auto_select_mode_on_new_agent?: boolean; + expand_new_chats_in_dashboard?: boolean; + auto_reveal_sub_agents?: boolean; + dev_mode?: boolean; +}): Promise<{ ok: boolean; settings: Record }> { + const res = await fetch(update_settings_endpoint, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + const data = await res.json(); + return data as { ok: boolean; settings: Record }; +} +export const UPDATE_SETTINGS = createAsyncThunk( + update_settings_endpoint, + update_settings_function, +); + + +const reset_system_prompt_endpoint: string = `${SETTINGS_API}/reset-system-prompt`; +async function reset_system_prompt_function(): Promise<{ ok: boolean; settings: Record }> { + const res = await fetch(reset_system_prompt_endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { ok: boolean; settings: Record }; +} +export const RESET_SYSTEM_PROMPT = createAsyncThunk( + reset_system_prompt_endpoint, + reset_system_prompt_function, +); diff --git a/frontend/src/shared/backend/apps/skills.ts b/frontend/src/shared/backend/apps/skills.ts new file mode 100644 index 00000000..542269aa --- /dev/null +++ b/frontend/src/shared/backend/apps/skills.ts @@ -0,0 +1,194 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; +import { API_BASE } from '@/shared/backend/base_routes'; + +const SKILLS_API: string = `${API_BASE}/skills`; + +// --------------------------------------------------------------------------- +// Local skill CRUD +// --------------------------------------------------------------------------- + + +const list_skills_endpoint: string = `${SKILLS_API}/list`; +async function list_skills_function(): Promise[]> { + const res = await fetch(list_skills_endpoint, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data.skills as Record[]; +} +export const LIST_SKILLS = createAsyncThunk( + list_skills_endpoint, + list_skills_function, +); + + +const read_skill_workspace_endpoint: string = `${SKILLS_API}/workspace`; +async function read_skill_workspace_function(workspaceId: string): Promise<{ skill_content: string | null; meta: Record | null; frontmatter: Record }> { + const res = await fetch(`${SKILLS_API}/workspace/${workspaceId}`, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { skill_content: string | null; meta: Record | null; frontmatter: Record }; +} +export const READ_SKILL_WORKSPACE = createAsyncThunk( + read_skill_workspace_endpoint, + read_skill_workspace_function, +); + + +const seed_skill_workspace_endpoint: string = `${SKILLS_API}/workspace/seed`; +async function seed_skill_workspace_function(body: { + workspace_id: string; + skill_content?: string | null; + meta?: Record | null; +}): Promise<{ path: string }> { + const res = await fetch(seed_skill_workspace_endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + const data = await res.json(); + return data as { path: string }; +} +export const SEED_SKILL_WORKSPACE = createAsyncThunk( + seed_skill_workspace_endpoint, + seed_skill_workspace_function, +); + + +const get_skill_endpoint: string = `${SKILLS_API}/detail`; +async function get_skill_function(skillId: string): Promise> { + const res = await fetch(`${SKILLS_API}/detail/${skillId}`, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as Record; +} +export const GET_SKILL = createAsyncThunk( + get_skill_endpoint, + get_skill_function, +); + + +const create_skill_endpoint: string = `${SKILLS_API}/create`; +async function create_skill_function(body: { + name: string; + description?: string; + content: string; + command?: string; +}): Promise<{ ok: boolean; skill: Record }> { + const res = await fetch(create_skill_endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + const data = await res.json(); + return data as { ok: boolean; skill: Record }; +} +export const CREATE_SKILL = createAsyncThunk( + create_skill_endpoint, + create_skill_function, +); + + +const update_skill_endpoint: string = `${SKILLS_API}/update`; +async function update_skill_function(args: { + skillId: string; + name?: string; + description?: string; + content?: string; + command?: string; +}): Promise<{ ok: boolean; skill: Record }> { + const { skillId, ...updates } = args; + const res = await fetch(`${SKILLS_API}/${skillId}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(updates), + }); + const data = await res.json(); + return data as { ok: boolean; skill: Record }; +} +export const UPDATE_SKILL = createAsyncThunk( + update_skill_endpoint, + update_skill_function, +); + + +const delete_skill_endpoint: string = `${SKILLS_API}/delete`; +async function delete_skill_function(skillId: string): Promise<{ ok: boolean }> { + const res = await fetch(`${SKILLS_API}/${skillId}`, { + method: 'DELETE', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { ok: boolean }; +} +export const DELETE_SKILL = createAsyncThunk( + delete_skill_endpoint, + delete_skill_function, +); + + +// --------------------------------------------------------------------------- +// Registry +// --------------------------------------------------------------------------- + + +const registry_stats_endpoint: string = `${SKILLS_API}/registry/stats`; +async function registry_stats_function(): Promise<{ total: number; categories: Record; lastUpdated: string | null }> { + const res = await fetch(registry_stats_endpoint, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { total: number; categories: Record; lastUpdated: string | null }; +} +export const REGISTRY_STATS = createAsyncThunk( + registry_stats_endpoint, + registry_stats_function, +); + + +const registry_search_endpoint: string = `${SKILLS_API}/registry/search`; +async function registry_search_function(params: { + q?: string; + limit?: number; + offset?: number; + sort?: string; + category?: string; +}): Promise<{ skills: Record[]; total: number; offset: number; limit: number }> { + const query = new URLSearchParams(); + if (params.q) query.set('q', params.q); + if (params.limit !== undefined) query.set('limit', String(params.limit)); + if (params.offset !== undefined) query.set('offset', String(params.offset)); + if (params.sort) query.set('sort', params.sort); + if (params.category) query.set('category', params.category); + const res = await fetch(`${registry_search_endpoint}?${query.toString()}`, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { skills: Record[]; total: number; offset: number; limit: number }; +} +export const REGISTRY_SEARCH = createAsyncThunk( + registry_search_endpoint, + registry_search_function, +); + + +const registry_detail_endpoint: string = `${SKILLS_API}/registry/detail`; +async function registry_detail_function(skillName: string): Promise<{ skill: Record }> { + const res = await fetch(`${SKILLS_API}/registry/detail/${skillName}`, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { skill: Record }; +} +export const REGISTRY_DETAIL = createAsyncThunk( + registry_detail_endpoint, + registry_detail_function, +); diff --git a/frontend/src/shared/backend/apps/subscriptions.ts b/frontend/src/shared/backend/apps/subscriptions.ts new file mode 100644 index 00000000..c786bb17 --- /dev/null +++ b/frontend/src/shared/backend/apps/subscriptions.ts @@ -0,0 +1,113 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; +import { API_BASE } from '@/shared/backend/base_routes'; + +const SUBSCRIPTIONS_API: string = `${API_BASE}/subscriptions`; + +// --------------------------------------------------------------------------- +// Subscription management +// --------------------------------------------------------------------------- + + +const subscriptions_status_endpoint: string = `${SUBSCRIPTIONS_API}/status`; +async function subscriptions_status_function(): Promise<{ running: boolean; providers: unknown[]; models: unknown[] }> { + const res = await fetch(subscriptions_status_endpoint, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { running: boolean; providers: unknown[]; models: unknown[] }; +} +export const SUBSCRIPTIONS_STATUS = createAsyncThunk( + subscriptions_status_endpoint, + subscriptions_status_function, +); + + +const subscriptions_connect_endpoint: string = `${SUBSCRIPTIONS_API}/connect`; +async function subscriptions_connect_function(provider: string): Promise> { + const res = await fetch(subscriptions_connect_endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ provider }), + }); + const data = await res.json(); + return data as Record; +} +export const SUBSCRIPTIONS_CONNECT = createAsyncThunk( + subscriptions_connect_endpoint, + subscriptions_connect_function, +); + + +const subscriptions_poll_endpoint: string = `${SUBSCRIPTIONS_API}/poll`; +async function subscriptions_poll_function(body: { + provider: string; + device_code: string; + code_verifier?: string; + extra_data?: Record; +}): Promise> { + const res = await fetch(subscriptions_poll_endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + const data = await res.json(); + return data as Record; +} +export const SUBSCRIPTIONS_POLL = createAsyncThunk( + subscriptions_poll_endpoint, + subscriptions_poll_function, +); + + +const subscriptions_disconnect_endpoint: string = `${SUBSCRIPTIONS_API}/disconnect`; +async function subscriptions_disconnect_function(provider: string): Promise<{ ok: boolean; error?: string }> { + const res = await fetch(subscriptions_disconnect_endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ provider }), + }); + const data = await res.json(); + return data as { ok: boolean; error?: string }; +} +export const SUBSCRIPTIONS_DISCONNECT = createAsyncThunk( + subscriptions_disconnect_endpoint, + subscriptions_disconnect_function, +); + + +const subscriptions_pending_endpoint: string = `${SUBSCRIPTIONS_API}/pending`; +async function subscriptions_pending_function(state: string): Promise<{ provider: string; code_verifier: string; redirect_uri: string }> { + const res = await fetch(`${SUBSCRIPTIONS_API}/pending/${state}`, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { provider: string; code_verifier: string; redirect_uri: string }; +} +export const SUBSCRIPTIONS_PENDING = createAsyncThunk( + subscriptions_pending_endpoint, + subscriptions_pending_function, +); + + +const subscriptions_callback_endpoint: string = `${SUBSCRIPTIONS_API}/callback`; +async function subscriptions_callback_function(params: { + code?: string; + state?: string; + error?: string; +}): Promise { + const query = new URLSearchParams(); + if (params.code) query.set('code', params.code); + if (params.state) query.set('state', params.state); + if (params.error) query.set('error', params.error); + const res = await fetch(`${subscriptions_callback_endpoint}?${query.toString()}`, { + method: 'GET', + }); + const html = await res.text(); + return html; +} +export const SUBSCRIPTIONS_CALLBACK = createAsyncThunk( + subscriptions_callback_endpoint, + subscriptions_callback_function, +); diff --git a/frontend/src/shared/backend/apps/tools.ts b/frontend/src/shared/backend/apps/tools.ts new file mode 100644 index 00000000..ff59a367 --- /dev/null +++ b/frontend/src/shared/backend/apps/tools.ts @@ -0,0 +1,247 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; +import { API_BASE } from '@/shared/backend/base_routes'; + +const TOOLS_API: string = `${API_BASE}/tools`; + +// --------------------------------------------------------------------------- +// Builtin tools +// --------------------------------------------------------------------------- + + +const list_builtin_tools_endpoint: string = `${TOOLS_API}/builtin`; +async function list_builtin_tools_function(): Promise<{ tools: Record[] }> { + const res = await fetch(list_builtin_tools_endpoint, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { tools: Record[] }; +} +export const LIST_BUILTIN_TOOLS = createAsyncThunk( + list_builtin_tools_endpoint, + list_builtin_tools_function, +); + + +const get_builtin_permissions_endpoint: string = `${TOOLS_API}/builtin/permissions`; +async function get_builtin_permissions_function(): Promise<{ permissions: Record }> { + const res = await fetch(get_builtin_permissions_endpoint, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { permissions: Record }; +} +export const GET_BUILTIN_PERMISSIONS = createAsyncThunk( + get_builtin_permissions_endpoint, + get_builtin_permissions_function, +); + + +const update_builtin_permissions_endpoint: string = `${TOOLS_API}/builtin/permissions`; +async function update_builtin_permissions_function(permissions: Record): Promise<{ permissions: Record }> { + const res = await fetch(update_builtin_permissions_endpoint, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ permissions }), + }); + const data = await res.json(); + return data as { permissions: Record }; +} +export const UPDATE_BUILTIN_PERMISSIONS = createAsyncThunk( + update_builtin_permissions_endpoint, + update_builtin_permissions_function, +); + + +// --------------------------------------------------------------------------- +// User-installed tool CRUD +// --------------------------------------------------------------------------- + + +const list_tools_endpoint: string = `${TOOLS_API}/list`; +async function list_tools_function(): Promise<{ tools: Record[] }> { + const res = await fetch(list_tools_endpoint, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { tools: Record[] }; +} +export const LIST_TOOLS = createAsyncThunk( + list_tools_endpoint, + list_tools_function, +); + + +const create_tool_endpoint: string = `${TOOLS_API}/create`; +async function create_tool_function(body: { + name: string; + description?: string; + command?: string; + mcp_config?: Record; + credentials?: Record; + auth_type?: string; + auth_status?: string; + oauth_provider?: string | null; +}): Promise<{ ok: boolean; tool: Record }> { + const res = await fetch(create_tool_endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + const data = await res.json(); + return data as { ok: boolean; tool: Record }; +} +export const CREATE_TOOL = createAsyncThunk( + create_tool_endpoint, + create_tool_function, +); + + +const get_tool_endpoint: string = `${TOOLS_API}/get`; +async function get_tool_function(toolId: string): Promise> { + const res = await fetch(`${TOOLS_API}/${toolId}`, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as Record; +} +export const GET_TOOL = createAsyncThunk( + get_tool_endpoint, + get_tool_function, +); + + +const update_tool_endpoint: string = `${TOOLS_API}/update`; +async function update_tool_function(args: { + toolId: string; + name?: string; + description?: string; + command?: string; + mcp_config?: Record; + credentials?: Record; + auth_type?: string; + auth_status?: string; + oauth_provider?: string | null; + oauth_tokens?: Record; + tool_permissions?: Record; + connected_account_email?: string | null; + enabled?: boolean; +}): Promise<{ ok: boolean; tool: Record }> { + const { toolId, ...updates } = args; + const res = await fetch(`${TOOLS_API}/${toolId}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(updates), + }); + const data = await res.json(); + return data as { ok: boolean; tool: Record }; +} +export const UPDATE_TOOL = createAsyncThunk( + update_tool_endpoint, + update_tool_function, +); + + +const delete_tool_endpoint: string = `${TOOLS_API}/delete`; +async function delete_tool_function(toolId: string): Promise<{ ok: boolean }> { + const res = await fetch(`${TOOLS_API}/${toolId}`, { + method: 'DELETE', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { ok: boolean }; +} +export const DELETE_TOOL = createAsyncThunk( + delete_tool_endpoint, + delete_tool_function, +); + + +// --------------------------------------------------------------------------- +// Discovery +// --------------------------------------------------------------------------- + + +const discover_tool_endpoint: string = `${TOOLS_API}/discover`; +async function discover_tool_function(toolId: string): Promise<{ ok: boolean; tool: Record }> { + const res = await fetch(`${TOOLS_API}/${toolId}/discover`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { ok: boolean; tool: Record }; +} +export const DISCOVER_TOOL = createAsyncThunk( + discover_tool_endpoint, + discover_tool_function, +); + + +const load_user_toolkit_endpoint: string = `${TOOLS_API}/load_user_toolkit`; +async function load_user_toolkit_function(): Promise | null> { + const res = await fetch(load_user_toolkit_endpoint, { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as Record | null; +} +export const LOAD_USER_TOOLKIT = createAsyncThunk( + load_user_toolkit_endpoint, + load_user_toolkit_function, +); + + +// --------------------------------------------------------------------------- +// OAuth +// --------------------------------------------------------------------------- + + +const oauth_callback_endpoint: string = `${TOOLS_API}/oauth/callback`; +async function oauth_callback_function(params: { code: string; state?: string }): Promise { + const query = new URLSearchParams(); + query.set('code', params.code); + if (params.state) query.set('state', params.state); + const res = await fetch(`${oauth_callback_endpoint}?${query.toString()}`, { + method: 'GET', + }); + const html = await res.text(); + return html; +} +export const OAUTH_CALLBACK = createAsyncThunk( + oauth_callback_endpoint, + oauth_callback_function, +); + + +const oauth_start_endpoint: string = `${TOOLS_API}/oauth/start`; +async function oauth_start_function(toolId: string): Promise<{ auth_url: string }> { + const res = await fetch(`${TOOLS_API}/${toolId}/oauth/start`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { auth_url: string }; +} +export const OAUTH_START = createAsyncThunk( + oauth_start_endpoint, + oauth_start_function, +); + + +const oauth_disconnect_endpoint: string = `${TOOLS_API}/oauth/disconnect`; +async function oauth_disconnect_function(toolId: string): Promise<{ ok: boolean; tool: Record }> { + const res = await fetch(`${TOOLS_API}/${toolId}/oauth/disconnect`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + }); + const data = await res.json(); + return data as { ok: boolean; tool: Record }; +} +export const OAUTH_DISCONNECT = createAsyncThunk( + oauth_disconnect_endpoint, + oauth_disconnect_function, +);