mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
- Simplified test scripts in package.json by removing specific config references for vitest. - Updated @tauri-apps/api dependency version to 2.10.1. - Removed unused dependencies from yarn.lock and updated Cargo.toml and Cargo.lock for tinyhumansai to version 0.1.4. - Enhanced memory management in Conversations and Login components by ensuring async token synchronization. - Introduced recall_memory command in Tauri for improved context retrieval from memory.
613 lines
15 KiB
TypeScript
613 lines
15 KiB
TypeScript
/**
|
|
* Tauri Commands
|
|
*
|
|
* Helper functions for invoking Tauri commands from the frontend.
|
|
*/
|
|
import { isTauri as coreIsTauri, invoke } from '@tauri-apps/api/core';
|
|
|
|
import { injectAll } from '../lib/ai/injector';
|
|
import type { Message } from '../lib/ai/providers/interface';
|
|
|
|
// Check if we're running in Tauri
|
|
export const isTauri = (): boolean => {
|
|
// Tauri v2: prefer the official runtime check over window globals.
|
|
return coreIsTauri();
|
|
};
|
|
|
|
/**
|
|
* Exchange a login token for a session token
|
|
*/
|
|
export async function exchangeToken(
|
|
backendUrl: string,
|
|
token: string
|
|
): Promise<{ sessionToken: string; user: object }> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
|
|
return await invoke('exchange_token', { backendUrl, token });
|
|
}
|
|
|
|
/**
|
|
* Get the current authentication state from Rust
|
|
*/
|
|
export async function getAuthState(): Promise<{ is_authenticated: boolean; user: object | null }> {
|
|
if (!isTauri()) {
|
|
return { is_authenticated: false, user: null };
|
|
}
|
|
|
|
return await invoke('get_auth_state');
|
|
}
|
|
|
|
/**
|
|
* Get the session token from secure storage
|
|
*/
|
|
export async function getSessionToken(): Promise<string | null> {
|
|
if (!isTauri()) {
|
|
return null;
|
|
}
|
|
|
|
return await invoke('get_session_token');
|
|
}
|
|
|
|
/**
|
|
* Logout and clear session
|
|
*/
|
|
export async function logout(): Promise<void> {
|
|
if (!isTauri()) {
|
|
return;
|
|
}
|
|
|
|
await invoke('logout');
|
|
}
|
|
|
|
/**
|
|
* Store session in secure storage
|
|
*/
|
|
export async function storeSession(token: string, user: object): Promise<void> {
|
|
if (!isTauri()) {
|
|
return;
|
|
}
|
|
|
|
await invoke('store_session', { token, user });
|
|
}
|
|
|
|
/**
|
|
* Show the main window
|
|
*/
|
|
export async function showWindow(): Promise<void> {
|
|
if (!isTauri()) {
|
|
return;
|
|
}
|
|
|
|
await invoke('show_window');
|
|
}
|
|
|
|
/**
|
|
* Hide the main window
|
|
*/
|
|
export async function hideWindow(): Promise<void> {
|
|
if (!isTauri()) {
|
|
return;
|
|
}
|
|
|
|
await invoke('hide_window');
|
|
}
|
|
|
|
/**
|
|
* Toggle window visibility
|
|
*/
|
|
export async function toggleWindow(): Promise<void> {
|
|
if (!isTauri()) {
|
|
return;
|
|
}
|
|
|
|
await invoke('toggle_window');
|
|
}
|
|
|
|
/**
|
|
* Check if window is visible
|
|
*/
|
|
export async function isWindowVisible(): Promise<boolean> {
|
|
if (!isTauri()) {
|
|
return true; // In browser, window is always visible
|
|
}
|
|
|
|
return await invoke('is_window_visible');
|
|
}
|
|
|
|
/**
|
|
* Minimize the window
|
|
*/
|
|
export async function minimizeWindow(): Promise<void> {
|
|
if (!isTauri()) {
|
|
return;
|
|
}
|
|
|
|
await invoke('minimize_window');
|
|
}
|
|
|
|
/**
|
|
* Maximize or unmaximize the window
|
|
*/
|
|
export async function maximizeWindow(): Promise<void> {
|
|
if (!isTauri()) {
|
|
return;
|
|
}
|
|
|
|
await invoke('maximize_window');
|
|
}
|
|
|
|
/**
|
|
* Close the window (minimizes to tray on macOS)
|
|
*/
|
|
export async function closeWindow(): Promise<void> {
|
|
if (!isTauri()) {
|
|
return;
|
|
}
|
|
|
|
await invoke('close_window');
|
|
}
|
|
|
|
/**
|
|
* Set the window title
|
|
*/
|
|
export async function setWindowTitle(title: string): Promise<void> {
|
|
if (!isTauri()) {
|
|
document.title = title;
|
|
return;
|
|
}
|
|
|
|
await invoke('set_window_title', { title });
|
|
}
|
|
|
|
// --- Memory Commands ---
|
|
|
|
/**
|
|
* Initialise the TinyHumans memory client in Rust with the user's JWT token
|
|
* (sourced from `authSlice.token` in Redux). Call this after login and after
|
|
* Redux Persist rehydration.
|
|
*/
|
|
export async function syncMemoryClientToken(token: string): Promise<void> {
|
|
console.debug(
|
|
'[memory] syncMemoryClientToken: entry (token_present=%s, is_tauri=%s)',
|
|
!!token,
|
|
isTauri()
|
|
);
|
|
if (!isTauri() || !token) {
|
|
console.debug('[memory] syncMemoryClientToken: exit — skipped (not Tauri or empty token)');
|
|
return;
|
|
}
|
|
try {
|
|
console.debug(
|
|
'[memory] syncMemoryClientToken: payload → init_memory_client { jwtToken: <redacted, len=%d> }',
|
|
token.length
|
|
);
|
|
await invoke('init_memory_client', { jwtToken: token });
|
|
console.info('[memory] syncMemoryClientToken: exit — ok');
|
|
} catch (err) {
|
|
console.warn('[memory] syncMemoryClientToken: exit — error:', err);
|
|
}
|
|
}
|
|
|
|
// --- Alphahuman Commands ---
|
|
|
|
export type DoctorSeverity = 'Ok' | 'Warn' | 'Error';
|
|
export type ModelProbeOutcome = 'Ok' | 'Skipped' | 'AuthOrAccess' | 'Error';
|
|
export type IntegrationStatus = 'Available' | 'Active' | 'ComingSoon';
|
|
export type IntegrationCategory =
|
|
| 'Chat'
|
|
| 'AiModel'
|
|
| 'Productivity'
|
|
| 'MusicAudio'
|
|
| 'SmartHome'
|
|
| 'ToolsAutomation'
|
|
| 'MediaCreative'
|
|
| 'Social'
|
|
| 'Platform';
|
|
export type ModelRefreshSource = 'Live' | 'CacheFresh' | 'CacheStaleFallback';
|
|
export type ServiceState = 'Running' | 'Stopped' | 'NotInstalled' | { Unknown: string };
|
|
export type HardwareTransport = 'Native' | 'Serial' | 'Probe' | 'None';
|
|
|
|
export interface CommandResponse<T> {
|
|
result: T;
|
|
logs: string[];
|
|
}
|
|
|
|
export interface SkillSnapshot {
|
|
skill_id: string;
|
|
name: string;
|
|
status: unknown;
|
|
tools: Array<{ name: string; description: string; input_schema?: unknown }>;
|
|
error?: string | null;
|
|
state?: Record<string, unknown>;
|
|
}
|
|
|
|
export interface DoctorReport {
|
|
items: { severity: DoctorSeverity; category: string; message: string }[];
|
|
summary: { ok: number; warnings: number; errors: number };
|
|
}
|
|
|
|
export interface ModelProbeReport {
|
|
entries: { provider: string; outcome: ModelProbeOutcome; message?: string | null }[];
|
|
summary: { ok: number; skipped: number; auth_or_access: number; errors: number };
|
|
}
|
|
|
|
export interface IntegrationInfo {
|
|
name: string;
|
|
description: string;
|
|
category: IntegrationCategory;
|
|
status: IntegrationStatus;
|
|
setup_hints: string[];
|
|
}
|
|
|
|
export interface ModelRefreshResult {
|
|
provider: string;
|
|
models: string[];
|
|
source: ModelRefreshSource;
|
|
cache_age_secs?: number | null;
|
|
warnings: string[];
|
|
}
|
|
|
|
export interface MigrationStats {
|
|
from_sqlite: number;
|
|
from_markdown: number;
|
|
imported: number;
|
|
skipped_unchanged: number;
|
|
renamed_conflicts: number;
|
|
}
|
|
|
|
export interface MigrationReport {
|
|
source_workspace: string;
|
|
target_workspace: string;
|
|
dry_run: boolean;
|
|
stats: MigrationStats;
|
|
warnings: string[];
|
|
}
|
|
|
|
export interface DiscoveredDevice {
|
|
name: string;
|
|
detail?: string | null;
|
|
device_path?: string | null;
|
|
transport: HardwareTransport;
|
|
}
|
|
|
|
export interface HardwareIntrospect {
|
|
path: string;
|
|
vid?: number | null;
|
|
pid?: number | null;
|
|
board_name?: string | null;
|
|
architecture?: string | null;
|
|
memory_map_note: string;
|
|
}
|
|
|
|
export interface ServiceStatus {
|
|
state: ServiceState;
|
|
unit_path?: string | null;
|
|
label: string;
|
|
details?: string | null;
|
|
}
|
|
|
|
export interface ConfigSnapshot {
|
|
config: Record<string, unknown>;
|
|
workspace_dir: string;
|
|
config_path: string;
|
|
}
|
|
|
|
export interface ModelSettingsUpdate {
|
|
api_key?: string | null;
|
|
api_url?: string | null;
|
|
default_provider?: string | null;
|
|
default_model?: string | null;
|
|
default_temperature?: number | null;
|
|
}
|
|
|
|
export interface MemorySettingsUpdate {
|
|
backend?: string | null;
|
|
auto_save?: boolean | null;
|
|
embedding_provider?: string | null;
|
|
embedding_model?: string | null;
|
|
embedding_dimensions?: number | null;
|
|
}
|
|
|
|
export interface GatewaySettingsUpdate {
|
|
host?: string | null;
|
|
port?: number | null;
|
|
require_pairing?: boolean | null;
|
|
allow_public_bind?: boolean | null;
|
|
}
|
|
|
|
export interface RuntimeSettingsUpdate {
|
|
kind?: string | null;
|
|
reasoning_enabled?: boolean | null;
|
|
}
|
|
|
|
export interface BrowserSettingsUpdate {
|
|
enabled?: boolean | null;
|
|
}
|
|
|
|
export interface RuntimeFlags {
|
|
browser_allow_all: boolean;
|
|
log_prompts: boolean;
|
|
}
|
|
|
|
export interface TunnelConfig {
|
|
provider: string;
|
|
cloudflare?: { token: string } | null;
|
|
tailscale?: { funnel?: boolean; hostname?: string | null } | null;
|
|
ngrok?: { auth_token: string; domain?: string | null } | null;
|
|
custom?: {
|
|
start_command: string;
|
|
health_url?: string | null;
|
|
url_pattern?: string | null;
|
|
} | null;
|
|
}
|
|
|
|
export async function alphahumanGetConfig(): Promise<CommandResponse<ConfigSnapshot>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_get_config');
|
|
}
|
|
|
|
export async function alphahumanUpdateModelSettings(
|
|
update: ModelSettingsUpdate
|
|
): Promise<CommandResponse<ConfigSnapshot>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_update_model_settings', { update });
|
|
}
|
|
|
|
export async function alphahumanUpdateMemorySettings(
|
|
update: MemorySettingsUpdate
|
|
): Promise<CommandResponse<ConfigSnapshot>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_update_memory_settings', { update });
|
|
}
|
|
|
|
export async function alphahumanUpdateGatewaySettings(
|
|
update: GatewaySettingsUpdate
|
|
): Promise<CommandResponse<ConfigSnapshot>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_update_gateway_settings', { update });
|
|
}
|
|
|
|
export async function alphahumanUpdateTunnelSettings(
|
|
tunnel: TunnelConfig
|
|
): Promise<CommandResponse<ConfigSnapshot>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_update_tunnel_settings', { tunnel });
|
|
}
|
|
|
|
export async function alphahumanUpdateRuntimeSettings(
|
|
update: RuntimeSettingsUpdate
|
|
): Promise<CommandResponse<ConfigSnapshot>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_update_runtime_settings', { update });
|
|
}
|
|
|
|
export async function alphahumanUpdateBrowserSettings(
|
|
update: BrowserSettingsUpdate
|
|
): Promise<CommandResponse<ConfigSnapshot>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_update_browser_settings', { update });
|
|
}
|
|
|
|
export async function alphahumanGetRuntimeFlags(): Promise<CommandResponse<RuntimeFlags>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_get_runtime_flags');
|
|
}
|
|
|
|
export async function alphahumanSetBrowserAllowAll(
|
|
enabled: boolean
|
|
): Promise<CommandResponse<RuntimeFlags>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_set_browser_allow_all', { enabled });
|
|
}
|
|
|
|
export async function alphahumanAgentChat(
|
|
message: string,
|
|
providerOverride?: string,
|
|
modelOverride?: string,
|
|
temperature?: number,
|
|
options: { injectSoul?: boolean } = { injectSoul: true }
|
|
): Promise<CommandResponse<string>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
|
|
let processedMessage = message;
|
|
|
|
if (options.injectSoul) {
|
|
try {
|
|
const userMessage: Message = { role: 'user', content: [{ type: 'text', text: message }] };
|
|
|
|
const injectedMessage = await injectAll(userMessage, {
|
|
mode: 'context-block',
|
|
includeMetadata: false,
|
|
});
|
|
|
|
// Extract the processed text
|
|
const textContent = injectedMessage.content
|
|
.filter(block => block.type === 'text')
|
|
.map(block => (block as { text: string }).text)
|
|
.join('\n');
|
|
|
|
processedMessage = textContent;
|
|
console.log('✅ SOUL + TOOLS injection successful in alphahumanAgentChat');
|
|
} catch (error) {
|
|
console.warn('⚠️ SOUL + TOOLS injection failed in alphahumanAgentChat:', error);
|
|
}
|
|
}
|
|
|
|
return await invoke('alphahuman_agent_chat', {
|
|
message: processedMessage,
|
|
providerOverride,
|
|
modelOverride,
|
|
temperature,
|
|
});
|
|
}
|
|
|
|
export async function alphahumanEncryptSecret(plaintext: string): Promise<CommandResponse<string>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_encrypt_secret', { plaintext });
|
|
}
|
|
|
|
export async function alphahumanDecryptSecret(
|
|
ciphertext: string
|
|
): Promise<CommandResponse<string>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_decrypt_secret', { ciphertext });
|
|
}
|
|
|
|
export async function alphahumanDoctorReport(): Promise<CommandResponse<DoctorReport>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_doctor_report');
|
|
}
|
|
|
|
export async function alphahumanDoctorModels(
|
|
providerOverride?: string,
|
|
useCache = true
|
|
): Promise<CommandResponse<ModelProbeReport>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_doctor_models', { providerOverride, useCache });
|
|
}
|
|
|
|
export async function alphahumanListIntegrations(): Promise<CommandResponse<IntegrationInfo[]>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_list_integrations');
|
|
}
|
|
|
|
export async function alphahumanGetIntegrationInfo(
|
|
name: string
|
|
): Promise<CommandResponse<IntegrationInfo>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_get_integration_info', { name });
|
|
}
|
|
|
|
export async function alphahumanModelsRefresh(
|
|
providerOverride?: string,
|
|
force = false
|
|
): Promise<CommandResponse<ModelRefreshResult>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_models_refresh', { providerOverride, force });
|
|
}
|
|
|
|
export async function alphahumanMigrateOpenclaw(
|
|
sourceWorkspace?: string,
|
|
dryRun = true
|
|
): Promise<CommandResponse<MigrationReport>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_migrate_openclaw', { sourceWorkspace, dryRun });
|
|
}
|
|
|
|
export async function alphahumanHardwareDiscover(): Promise<CommandResponse<DiscoveredDevice[]>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_hardware_discover');
|
|
}
|
|
|
|
export async function alphahumanHardwareIntrospect(
|
|
path: string
|
|
): Promise<CommandResponse<HardwareIntrospect>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_hardware_introspect', { path });
|
|
}
|
|
|
|
export async function alphahumanServiceInstall(): Promise<CommandResponse<ServiceStatus>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_service_install');
|
|
}
|
|
|
|
export async function alphahumanServiceStart(): Promise<CommandResponse<ServiceStatus>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_service_start');
|
|
}
|
|
|
|
export async function alphahumanServiceStop(): Promise<CommandResponse<ServiceStatus>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_service_stop');
|
|
}
|
|
|
|
export async function alphahumanServiceStatus(): Promise<CommandResponse<ServiceStatus>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_service_status');
|
|
}
|
|
|
|
export async function alphahumanServiceUninstall(): Promise<CommandResponse<ServiceStatus>> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('alphahuman_service_uninstall');
|
|
}
|
|
|
|
export async function runtimeListSkills(): Promise<SkillSnapshot[]> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('runtime_list_skills');
|
|
}
|
|
|
|
export async function runtimeIsSkillEnabled(skillId: string): Promise<boolean> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
return await invoke('runtime_is_skill_enabled', { skill_id: skillId });
|
|
}
|
|
|
|
export async function runtimeEnableSkill(skillId: string): Promise<void> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
await invoke('runtime_enable_skill', { skill_id: skillId });
|
|
}
|
|
|
|
export async function runtimeDisableSkill(skillId: string): Promise<void> {
|
|
if (!isTauri()) {
|
|
throw new Error('Not running in Tauri');
|
|
}
|
|
await invoke('runtime_disable_skill', { skill_id: skillId });
|
|
}
|