mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
refactor: clean up code formatting and improve readability across multiple components
- Standardized import statements and formatting in various files, including `WalletInfoSection`, `AgentChatPanel`, and `SkillsPanel`. - Enhanced readability by adjusting line breaks and spacing in JSX elements and function definitions. - Improved consistency in the use of arrow functions and destructuring across components. - Updated type definitions and ensured proper alignment of code for better maintainability.
This commit is contained in:
@@ -54,16 +54,16 @@ AlphaHuman is designed to be simpler to deploy, cheaper to run, and more intelli
|
||||
|
||||
> **Early Beta** — AlphaHuman is under active development. Expect rough edges.
|
||||
|
||||
| Platform | Variant | Download |
|
||||
| ----------- | --------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
||||
| Platform | Variant | Download |
|
||||
| ----------- | --------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
||||
| **macOS** | Apple Silicon (M1/M2/M3/M4) | [`.dmg` (aarch64)](https://github.com/alphahumanai/alphahuman/releases/latest/download/AlphaHuman_aarch64.dmg) |
|
||||
| **macOS** | Intel | [`.dmg` (x64)](https://github.com/alphahumanai/alphahuman/releases/latest/download/AlphaHuman_x64.dmg) |
|
||||
| **Windows** | x64 | [`.msi`](https://github.com/alphahumanai/alphahuman/releases/latest/download/AlphaHuman_x64_en-US.msi) |
|
||||
| **Linux** | Debian / Ubuntu | [`.deb` (amd64)](https://github.com/alphahumanai/alphahuman/releases/latest/download/AlphaHuman_amd64.deb) |
|
||||
| **Linux** | Fedora / RHEL | [`.rpm` (x86_64)](https://github.com/alphahumanai/alphahuman/releases/latest/download/AlphaHuman_x86_64.rpm) |
|
||||
| **Linux** | Universal | [`.AppImage`](https://github.com/alphahumanai/alphahuman/releases/latest/download/AlphaHuman_amd64.AppImage) |
|
||||
| **Android** | — | Coming soon |
|
||||
| **iOS** | — | Coming soon |
|
||||
| **Android** | — | Coming soon |
|
||||
| **iOS** | — | Coming soon |
|
||||
|
||||
Browse all releases: [github.com/alphahumanai/alphahuman/releases](https://github.com/alphahumanai/alphahuman/releases)
|
||||
|
||||
|
||||
+5
-5
@@ -4,11 +4,11 @@
|
||||
|
||||
We provide security updates for the following versions of AlphaHuman:
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| Latest | :white_check_mark: |
|
||||
| Previous minor | :white_check_mark: |
|
||||
| Older | :x: |
|
||||
| Version | Supported |
|
||||
| -------------- | ------------------ |
|
||||
| Latest | :white_check_mark: |
|
||||
| Previous minor | :white_check_mark: |
|
||||
| Older | :x: |
|
||||
|
||||
We recommend always running the [latest release](https://github.com/alphahumanxyz/alphahuman/releases/latest). AlphaHuman is in early beta; older versions may not receive patches.
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { skillManager } from '../lib/skills/manager';
|
||||
import { useSkillConnectionStatus } from '../lib/skills/hooks';
|
||||
import { useAppSelector } from '../store/hooks';
|
||||
import { useUser } from '../hooks/useUser';
|
||||
import { useSkillConnectionStatus } from '../lib/skills/hooks';
|
||||
import { skillManager } from '../lib/skills/manager';
|
||||
import { useAppSelector } from '../store/hooks';
|
||||
|
||||
function truncateAddress(address: string): string {
|
||||
if (!address || address.length < 12) return address;
|
||||
@@ -64,8 +64,8 @@ export default function WalletInfoSection() {
|
||||
};
|
||||
const networks = Array.isArray(listData.networks) ? listData.networks : [];
|
||||
// Prefer Ethereum Mainnet (skill always has it); else first EVM, else first network
|
||||
const ethMainnet = networks.find((n) => n?.chain_id === '1' && n?.chain_type === 'evm');
|
||||
const firstEvm = networks.find((n) => n && n.chain_type === 'evm');
|
||||
const ethMainnet = networks.find(n => n?.chain_id === '1' && n?.chain_type === 'evm');
|
||||
const firstEvm = networks.find(n => n && n.chain_type === 'evm');
|
||||
const chosen = ethMainnet ?? firstEvm ?? networks.find(Boolean);
|
||||
if (!chosen || cancelledRef.current) {
|
||||
if (!cancelledRef.current) setLoading(false);
|
||||
@@ -121,9 +121,13 @@ export default function WalletInfoSection() {
|
||||
} catch (e) {
|
||||
if (!cancelledRef.current) {
|
||||
const msg = e instanceof Error ? e.message : String(e);
|
||||
const isTransient = msg.includes('not running') || msg.includes('not started') || msg.includes('transport');
|
||||
const isTransient =
|
||||
msg.includes('not running') || msg.includes('not started') || msg.includes('transport');
|
||||
if (isTransient && attempt < 3) {
|
||||
retryTimerRef.current = setTimeout(() => fetchWalletInfoRef.current(address, attempt + 1), 2000);
|
||||
retryTimerRef.current = setTimeout(
|
||||
() => fetchWalletInfoRef.current(address, attempt + 1),
|
||||
2000
|
||||
);
|
||||
return;
|
||||
}
|
||||
console.error('[WalletInfoSection] Failed to load wallet info:', e);
|
||||
@@ -197,7 +201,7 @@ export default function WalletInfoSection() {
|
||||
) : error ? (
|
||||
<span className="text-coral-500 text-xs">{error}</span>
|
||||
) : (
|
||||
networkName ?? '—'
|
||||
(networkName ?? '—')
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
@@ -209,7 +213,7 @@ export default function WalletInfoSection() {
|
||||
) : error ? (
|
||||
'—'
|
||||
) : (
|
||||
balance ?? '—'
|
||||
(balance ?? '—')
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { alphahumanAgentChat } from '../../../utils/tauriCommands';
|
||||
import SettingsHeader from '../components/SettingsHeader';
|
||||
import { useSettingsNavigation } from '../hooks/useSettingsNavigation';
|
||||
import { alphahumanAgentChat } from '../../../utils/tauriCommands';
|
||||
|
||||
type ChatMessage = { role: 'user' | 'agent'; text: string };
|
||||
|
||||
@@ -46,12 +46,7 @@ const AgentChatPanel = () => {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const payload = {
|
||||
messages,
|
||||
providerOverride,
|
||||
modelOverride,
|
||||
temperature,
|
||||
};
|
||||
const payload = { messages, providerOverride, modelOverride, temperature };
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(payload));
|
||||
} catch {
|
||||
@@ -65,7 +60,7 @@ const AgentChatPanel = () => {
|
||||
setError('');
|
||||
setSending(true);
|
||||
setInput('');
|
||||
setMessages((prev) => [...prev, { role: 'user', text }]);
|
||||
setMessages(prev => [...prev, { role: 'user', text }]);
|
||||
try {
|
||||
const response = await alphahumanAgentChat(
|
||||
text,
|
||||
@@ -73,7 +68,7 @@ const AgentChatPanel = () => {
|
||||
modelOverride.trim() ? modelOverride : undefined,
|
||||
Number.isFinite(Number(temperature)) ? Number(temperature) : undefined
|
||||
);
|
||||
setMessages((prev) => [...prev, { role: 'agent', text: response.result }]);
|
||||
setMessages(prev => [...prev, { role: 'agent', text: response.result }]);
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
setError(message);
|
||||
@@ -96,7 +91,7 @@ const AgentChatPanel = () => {
|
||||
className="input input-bordered w-full text-slate-900 bg-white"
|
||||
placeholder="openai"
|
||||
value={providerOverride}
|
||||
onChange={(event) => setProviderOverride(event.target.value)}
|
||||
onChange={event => setProviderOverride(event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label className="space-y-2 text-sm text-gray-300">
|
||||
@@ -105,7 +100,7 @@ const AgentChatPanel = () => {
|
||||
className="input input-bordered w-full text-slate-900 bg-white"
|
||||
placeholder="gpt-4.1-mini"
|
||||
value={modelOverride}
|
||||
onChange={(event) => setModelOverride(event.target.value)}
|
||||
onChange={event => setModelOverride(event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label className="space-y-2 text-sm text-gray-300">
|
||||
@@ -114,7 +109,7 @@ const AgentChatPanel = () => {
|
||||
className="input input-bordered w-full text-slate-900 bg-white"
|
||||
placeholder="0.7"
|
||||
value={temperature}
|
||||
onChange={(event) => setTemperature(event.target.value)}
|
||||
onChange={event => setTemperature(event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
@@ -139,8 +134,7 @@ const AgentChatPanel = () => {
|
||||
<div
|
||||
className={`text-sm whitespace-pre-wrap ${
|
||||
message.role === 'user' ? 'text-white' : 'text-emerald-200'
|
||||
}`}
|
||||
>
|
||||
}`}>
|
||||
{message.text}
|
||||
</div>
|
||||
</div>
|
||||
@@ -151,7 +145,7 @@ const AgentChatPanel = () => {
|
||||
className="textarea textarea-bordered w-full min-h-[140px] text-slate-900 bg-white"
|
||||
placeholder="Ask the agent anything..."
|
||||
value={input}
|
||||
onChange={(event) => setInput(event.target.value)}
|
||||
onChange={event => setInput(event.target.value)}
|
||||
/>
|
||||
<button className="btn btn-primary" onClick={sendMessage} disabled={sending}>
|
||||
{sending ? 'Sending…' : 'Send Message'}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import {
|
||||
alphahumanListIntegrations,
|
||||
alphahumanGetConfig,
|
||||
alphahumanGetRuntimeFlags,
|
||||
alphahumanListIntegrations,
|
||||
alphahumanSetBrowserAllowAll,
|
||||
alphahumanUpdateBrowserSettings,
|
||||
type IntegrationCategory,
|
||||
@@ -50,7 +50,7 @@ const SkillsPanel = () => {
|
||||
const runtimeFlags = await alphahumanGetRuntimeFlags();
|
||||
setBrowserAllowAll(runtimeFlags.result.browser_allow_all);
|
||||
const entries = await Promise.all(
|
||||
response.result.map(async (integration) => {
|
||||
response.result.map(async integration => {
|
||||
const skillId = integrationSkillId(integration);
|
||||
try {
|
||||
const enabled =
|
||||
@@ -99,7 +99,8 @@ const SkillsPanel = () => {
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-white">Browser Access</h3>
|
||||
<p className="text-xs text-stone-400">
|
||||
Allow the browser tool to visit any public domain (private and file URLs are still blocked).
|
||||
Allow the browser tool to visit any public domain (private and file URLs are still
|
||||
blocked).
|
||||
</p>
|
||||
</div>
|
||||
<label className="flex items-center gap-3 text-sm text-stone-300">
|
||||
@@ -108,7 +109,7 @@ const SkillsPanel = () => {
|
||||
className="checkbox checkbox-primary"
|
||||
checked={browserAllowAll}
|
||||
disabled={browserAllowAllBusy}
|
||||
onChange={async (event) => {
|
||||
onChange={async event => {
|
||||
const next = event.target.checked;
|
||||
setBrowserAllowAllBusy(true);
|
||||
try {
|
||||
@@ -136,9 +137,7 @@ const SkillsPanel = () => {
|
||||
</div>
|
||||
)}
|
||||
<div className="rounded-xl border border-stone-800/60 bg-black/40">
|
||||
{loading && (
|
||||
<div className="p-4 text-sm text-stone-400">Loading integrations...</div>
|
||||
)}
|
||||
{loading && <div className="p-4 text-sm text-stone-400">Loading integrations...</div>}
|
||||
{!loading && integrations.length === 0 && (
|
||||
<div className="p-4 text-sm text-stone-400">
|
||||
No integrations registered in Alphahuman.
|
||||
@@ -159,9 +158,9 @@ const SkillsPanel = () => {
|
||||
enabled={enabledMap[integration.name] ?? false}
|
||||
busy={toggleBusy[integration.name] ?? false}
|
||||
toggleable={isIntegrationToggleable(integration)}
|
||||
onToggle={async (nextEnabled) => {
|
||||
onToggle={async nextEnabled => {
|
||||
const key = integration.name;
|
||||
setToggleBusy((prev) => ({ ...prev, [key]: true }));
|
||||
setToggleBusy(prev => ({ ...prev, [key]: true }));
|
||||
try {
|
||||
if (integration.name === 'Browser') {
|
||||
await alphahumanUpdateBrowserSettings({ enabled: nextEnabled });
|
||||
@@ -173,12 +172,12 @@ const SkillsPanel = () => {
|
||||
await runtimeDisableSkill(skillId);
|
||||
}
|
||||
}
|
||||
setEnabledMap((prev) => ({ ...prev, [key]: nextEnabled }));
|
||||
setEnabledMap(prev => ({ ...prev, [key]: nextEnabled }));
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
setError(message);
|
||||
} finally {
|
||||
setToggleBusy((prev) => ({ ...prev, [key]: false }));
|
||||
setToggleBusy(prev => ({ ...prev, [key]: false }));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@@ -218,31 +217,23 @@ function IntegrationRow({
|
||||
<div
|
||||
className={`flex items-center justify-between gap-4 p-4 ${
|
||||
isLast ? '' : 'border-b border-stone-800/60'
|
||||
}`}
|
||||
>
|
||||
}`}>
|
||||
<div className="flex items-center gap-3 text-left flex-1 min-w-0">
|
||||
<div className="w-6 h-6 flex items-center justify-center text-white/70">
|
||||
<span className="text-xs font-semibold uppercase">
|
||||
{integration.name.slice(0, 2)}
|
||||
</span>
|
||||
<span className="text-xs font-semibold uppercase">{integration.name.slice(0, 2)}</span>
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<div className="text-sm font-semibold text-white truncate">{integration.name}</div>
|
||||
<div className="text-xs text-stone-400 line-clamp-2">
|
||||
{integration.description}
|
||||
</div>
|
||||
<div className="text-xs text-stone-400 line-clamp-2">{integration.description}</div>
|
||||
{integration.setup_hints.length > 0 && (
|
||||
<div className="mt-1 text-[11px] text-stone-500">
|
||||
{integration.setup_hints[0]}
|
||||
</div>
|
||||
<div className="mt-1 text-[11px] text-stone-500">{integration.setup_hints[0]}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<span
|
||||
className={`px-2 py-1 text-[11px] font-semibold uppercase border rounded-full ${statusStyle}`}
|
||||
>
|
||||
className={`px-2 py-1 text-[11px] font-semibold uppercase border rounded-full ${statusStyle}`}>
|
||||
{integration.status}
|
||||
</span>
|
||||
<label className="flex items-center gap-2 text-xs text-stone-300">
|
||||
@@ -251,7 +242,7 @@ function IntegrationRow({
|
||||
className="checkbox checkbox-primary"
|
||||
checked={enabled}
|
||||
disabled={busy || !toggleable}
|
||||
onChange={(event) => onToggle(event.target.checked)}
|
||||
onChange={event => onToggle(event.target.checked)}
|
||||
/>
|
||||
{busy ? 'Saving…' : enabled ? 'Enabled' : toggleable ? 'Disabled' : 'Managed'}
|
||||
</label>
|
||||
@@ -274,8 +265,5 @@ function isIntegrationToggleable(integration: IntegrationInfo): boolean {
|
||||
if (integration.name === 'Browser') {
|
||||
return true;
|
||||
}
|
||||
return (
|
||||
integration.category === 'Chat' ||
|
||||
integration.category === 'ToolsAutomation'
|
||||
);
|
||||
return integration.category === 'Chat' || integration.category === 'ToolsAutomation';
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { CheckIcon, ExclamationTriangleIcon } from '@heroicons/react/24/outline';
|
||||
import React from 'react';
|
||||
|
||||
interface ActionPanelProps {
|
||||
children: React.ReactNode;
|
||||
@@ -14,7 +14,7 @@ const ActionPanel: React.FC<ActionPanelProps> = ({
|
||||
hasChanges = false,
|
||||
success = false,
|
||||
error,
|
||||
className = ''
|
||||
className = '',
|
||||
}) => {
|
||||
return (
|
||||
<div className={`space-y-6 ${className}`}>
|
||||
@@ -60,21 +60,24 @@ const PrimaryButton: React.FC<PrimaryButtonProps> = ({
|
||||
disabled = false,
|
||||
variant = 'primary',
|
||||
children,
|
||||
className = ''
|
||||
className = '',
|
||||
}) => {
|
||||
const baseClasses = 'px-6 py-3 rounded-lg font-medium transition-all duration-200 focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed';
|
||||
const baseClasses =
|
||||
'px-6 py-3 rounded-lg font-medium transition-all duration-200 focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed';
|
||||
const variantClasses = {
|
||||
primary: 'bg-primary-600 hover:bg-primary-500 active:bg-primary-700 text-white shadow-soft hover:shadow-lg hover:shadow-primary-500/25 focus:ring-2 focus:ring-primary-500/50 focus:ring-offset-2 focus:ring-offset-black',
|
||||
secondary: 'bg-stone-800 hover:bg-stone-700 active:bg-stone-600 text-white border border-stone-600 focus:ring-2 focus:ring-primary-500/50 focus:ring-offset-2 focus:ring-offset-black',
|
||||
outline: 'border border-stone-600 text-white hover:bg-white/10 active:bg-white/20 focus:ring-2 focus:ring-primary-500/50 focus:ring-offset-2 focus:ring-offset-black'
|
||||
primary:
|
||||
'bg-primary-600 hover:bg-primary-500 active:bg-primary-700 text-white shadow-soft hover:shadow-lg hover:shadow-primary-500/25 focus:ring-2 focus:ring-primary-500/50 focus:ring-offset-2 focus:ring-offset-black',
|
||||
secondary:
|
||||
'bg-stone-800 hover:bg-stone-700 active:bg-stone-600 text-white border border-stone-600 focus:ring-2 focus:ring-primary-500/50 focus:ring-offset-2 focus:ring-offset-black',
|
||||
outline:
|
||||
'border border-stone-600 text-white hover:bg-white/10 active:bg-white/20 focus:ring-2 focus:ring-primary-500/50 focus:ring-offset-2 focus:ring-offset-black',
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
className={`${baseClasses} ${variantClasses[variant]} ${className}`}
|
||||
onClick={onClick}
|
||||
disabled={disabled || loading}
|
||||
>
|
||||
disabled={disabled || loading}>
|
||||
{loading && (
|
||||
<div className="h-4 w-4 border-2 border-white/20 border-t-white rounded-full animate-spin mr-2" />
|
||||
)}
|
||||
@@ -84,4 +87,4 @@ const PrimaryButton: React.FC<PrimaryButtonProps> = ({
|
||||
};
|
||||
|
||||
export default ActionPanel;
|
||||
export { PrimaryButton };
|
||||
export { PrimaryButton };
|
||||
|
||||
@@ -11,23 +11,19 @@ const InputGroup: React.FC<InputGroupProps> = ({
|
||||
title,
|
||||
description,
|
||||
children,
|
||||
className = ''
|
||||
className = '',
|
||||
}) => {
|
||||
return (
|
||||
<div className={`space-y-6 ${className}`}>
|
||||
{title && (
|
||||
<div className="space-y-2">
|
||||
<h4 className="text-lg font-medium text-white">{title}</h4>
|
||||
{description && (
|
||||
<p className="text-sm text-gray-400">{description}</p>
|
||||
)}
|
||||
{description && <p className="text-sm text-gray-400">{description}</p>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="rounded-lg border border-white/10 bg-white/5 backdrop-blur-sm p-6">
|
||||
<div className="grid gap-6 md:grid-cols-2">
|
||||
{children}
|
||||
</div>
|
||||
<div className="grid gap-6 md:grid-cols-2">{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -46,17 +42,14 @@ const Field: React.FC<FieldProps> = ({
|
||||
children,
|
||||
helpText,
|
||||
className = '',
|
||||
fullWidth = false
|
||||
fullWidth = false,
|
||||
}) => {
|
||||
return (
|
||||
<label className={`space-y-3 text-sm text-gray-300 ${fullWidth ? 'md:col-span-2' : ''} ${className}`}>
|
||||
<label
|
||||
className={`space-y-3 text-sm text-gray-300 ${fullWidth ? 'md:col-span-2' : ''} ${className}`}>
|
||||
<div>
|
||||
<span className="font-medium">{label}</span>
|
||||
{helpText && (
|
||||
<p className="text-xs text-gray-400 leading-relaxed mt-1">
|
||||
{helpText}
|
||||
</p>
|
||||
)}
|
||||
{helpText && <p className="text-xs text-gray-400 leading-relaxed mt-1">{helpText}</p>}
|
||||
</div>
|
||||
{children}
|
||||
</label>
|
||||
@@ -76,7 +69,7 @@ const CheckboxField: React.FC<CheckboxFieldProps> = ({
|
||||
checked,
|
||||
onChange,
|
||||
helpText,
|
||||
className = ''
|
||||
className = '',
|
||||
}) => {
|
||||
return (
|
||||
<div className={`flex flex-col gap-3 ${className}`}>
|
||||
@@ -85,18 +78,14 @@ const CheckboxField: React.FC<CheckboxFieldProps> = ({
|
||||
type="checkbox"
|
||||
className="w-5 h-5 rounded border-2 border-stone-600 bg-stone-900/40 text-primary-500 focus:ring-2 focus:ring-primary-500/30 focus:border-primary-500/50 transition-all duration-200"
|
||||
checked={checked}
|
||||
onChange={(e) => onChange(e.target.checked)}
|
||||
onChange={e => onChange(e.target.checked)}
|
||||
/>
|
||||
<span className="font-medium">{label}</span>
|
||||
</label>
|
||||
{helpText && (
|
||||
<p className="text-xs text-gray-400 ml-7 leading-relaxed">
|
||||
{helpText}
|
||||
</p>
|
||||
)}
|
||||
{helpText && <p className="text-xs text-gray-400 ml-7 leading-relaxed">{helpText}</p>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InputGroup;
|
||||
export { Field, CheckboxField };
|
||||
export { Field, CheckboxField };
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { ChevronDownIcon, ChevronRightIcon } from '@heroicons/react/24/outline';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
interface SectionCardProps {
|
||||
title: string;
|
||||
@@ -16,14 +16,14 @@ const priorityStyles = {
|
||||
critical: 'bg-gradient-to-br from-primary-500/10 to-primary-600/5 border-primary-500/20',
|
||||
infrastructure: 'bg-gradient-to-br from-slate-500/8 to-slate-600/4 border-slate-500/15',
|
||||
development: 'bg-gradient-to-br from-amber-500/8 to-amber-600/4 border-amber-500/15',
|
||||
tools: 'bg-black/30 border-stone-600/30'
|
||||
tools: 'bg-black/30 border-stone-600/30',
|
||||
} as const;
|
||||
|
||||
const priorityIconColors = {
|
||||
critical: 'text-primary-400',
|
||||
infrastructure: 'text-slate-400',
|
||||
development: 'text-amber-400',
|
||||
tools: 'text-stone-400'
|
||||
tools: 'text-stone-400',
|
||||
} as const;
|
||||
|
||||
const SectionCard: React.FC<SectionCardProps> = ({
|
||||
@@ -34,7 +34,7 @@ const SectionCard: React.FC<SectionCardProps> = ({
|
||||
collapsible = false,
|
||||
defaultExpanded = true,
|
||||
hasChanges = false,
|
||||
loading = false
|
||||
loading = false,
|
||||
}) => {
|
||||
const [isExpanded, setIsExpanded] = useState(defaultExpanded);
|
||||
|
||||
@@ -45,20 +45,18 @@ const SectionCard: React.FC<SectionCardProps> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`rounded-xl border backdrop-blur-sm transition-all duration-200 ${priorityStyles[priority]}`}>
|
||||
<div
|
||||
className={`rounded-xl border backdrop-blur-sm transition-all duration-200 ${priorityStyles[priority]}`}>
|
||||
<div
|
||||
className={`flex items-center justify-between p-6 ${collapsible ? 'cursor-pointer hover:bg-white/5' : ''}`}
|
||||
onClick={handleToggle}
|
||||
>
|
||||
onClick={handleToggle}>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`flex-shrink-0 ${priorityIconColors[priority]}`}>
|
||||
{React.cloneElement(icon, { className: 'h-5 w-5' } as any)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="text-xl font-semibold text-white font-display">{title}</h3>
|
||||
{hasChanges && (
|
||||
<div className="h-2 w-2 rounded-full bg-amber-400 animate-pulse" />
|
||||
)}
|
||||
{hasChanges && <div className="h-2 w-2 rounded-full bg-amber-400 animate-pulse" />}
|
||||
{loading && (
|
||||
<div className="h-4 w-4 border-2 border-white/20 border-t-white rounded-full animate-spin" />
|
||||
)}
|
||||
@@ -77,13 +75,11 @@ const SectionCard: React.FC<SectionCardProps> = ({
|
||||
|
||||
{(!collapsible || isExpanded) && (
|
||||
<div className="px-6 pb-6">
|
||||
<div className="space-y-8">
|
||||
{children}
|
||||
</div>
|
||||
<div className="space-y-8">{children}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SectionCard;
|
||||
export default SectionCard;
|
||||
|
||||
@@ -52,11 +52,7 @@ export function syncGmailMetadataToBackend(gmailState: GmailStateForSync | undef
|
||||
// metadata.emails = gmailState.emails as GmailEmailSummaryLike[];
|
||||
// }
|
||||
|
||||
const payload = {
|
||||
requestId: crypto.randomUUID(),
|
||||
provider: PROVIDER_GOOGLE,
|
||||
metadata,
|
||||
};
|
||||
const payload = { requestId: crypto.randomUUID(), provider: PROVIDER_GOOGLE, metadata };
|
||||
|
||||
void emitViaRustSocket(INTEGRATION_METADATA_SYNC_EVENT, payload);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState, type KeyboardEvent } from 'react';
|
||||
import { type KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import LottieAnimation from '../components/LottieAnimation';
|
||||
@@ -92,7 +92,7 @@ const Mnemonic = () => {
|
||||
inputRefs.current[index + 1]?.focus();
|
||||
}
|
||||
},
|
||||
[importWords],
|
||||
[importWords]
|
||||
);
|
||||
|
||||
const handleImportKeyDown = useCallback(
|
||||
@@ -101,7 +101,7 @@ const Mnemonic = () => {
|
||||
inputRefs.current[index - 1]?.focus();
|
||||
}
|
||||
},
|
||||
[importWords],
|
||||
[importWords]
|
||||
);
|
||||
|
||||
const handleValidateImport = useCallback(() => {
|
||||
|
||||
@@ -8,21 +8,21 @@ import { invoke } from '@tauri-apps/api/core';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
import { type ReactNode, useEffect, useRef } from 'react';
|
||||
|
||||
import {
|
||||
type GmailStateForSync,
|
||||
syncGmailMetadataToBackend,
|
||||
} from '../lib/gmail/services/metadataSync';
|
||||
import { skillManager } from '../lib/skills/manager';
|
||||
import type { SkillManifest } from '../lib/skills/types';
|
||||
import { buildManualSentryEvent, enqueueError } from '../services/errorReportQueue';
|
||||
import { useAppDispatch, useAppSelector } from '../store/hooks';
|
||||
import {
|
||||
setGmailEmails,
|
||||
setGmailProfile,
|
||||
type GmailEmailSummary,
|
||||
type GmailProfile,
|
||||
setGmailEmails,
|
||||
setGmailProfile,
|
||||
} from '../store/gmailSlice';
|
||||
import { useAppDispatch, useAppSelector } from '../store/hooks';
|
||||
import { setSkillError, setSkillState, setSkillStatus } from '../store/skillsSlice';
|
||||
import {
|
||||
syncGmailMetadataToBackend,
|
||||
type GmailStateForSync,
|
||||
} from '../lib/gmail/services/metadataSync';
|
||||
import { DEV_AUTO_LOAD_SKILL, IS_DEV } from '../utils/config';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -7,9 +7,7 @@ interface ConsumeLoginTokenResponse {
|
||||
|
||||
interface IntegrationTokensResponse {
|
||||
success: boolean;
|
||||
data?: {
|
||||
encrypted: string;
|
||||
};
|
||||
data?: { encrypted: string };
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,10 +23,7 @@ interface GmailState {
|
||||
profile: GmailProfile | null;
|
||||
}
|
||||
|
||||
const initialState: GmailState = {
|
||||
emails: [],
|
||||
profile: null,
|
||||
};
|
||||
const initialState: GmailState = { emails: [], profile: null };
|
||||
|
||||
const gmailSlice = createSlice({
|
||||
name: 'gmail',
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { getPublicKey } from '@noble/secp256k1';
|
||||
import { keccak_256 } from '@noble/hashes/sha3.js';
|
||||
import { pbkdf2 } from '@noble/hashes/pbkdf2.js';
|
||||
import { sha256 } from '@noble/hashes/sha2.js';
|
||||
import { keccak_256 } from '@noble/hashes/sha3.js';
|
||||
import { bytesToHex } from '@noble/hashes/utils.js';
|
||||
import { getPublicKey } from '@noble/secp256k1';
|
||||
import { HDKey } from '@scure/bip32';
|
||||
import { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from '@scure/bip39';
|
||||
import { wordlist } from '@scure/bip39/wordlists/english.js';
|
||||
|
||||
@@ -4,13 +4,13 @@ import { getCurrent, onOpenUrl } from '@tauri-apps/plugin-deep-link';
|
||||
import { skillManager } from '../lib/skills/manager';
|
||||
import { consumeLoginToken, fetchIntegrationTokens } from '../services/api/authApi';
|
||||
import { store } from '../store';
|
||||
import { setToken } from '../store/authSlice';
|
||||
import { setSkillState } from '../store/skillsSlice';
|
||||
import {
|
||||
decryptIntegrationTokens,
|
||||
hexToBase64,
|
||||
type IntegrationTokensPayload,
|
||||
} from './integrationTokensCrypto';
|
||||
import { setToken } from '../store/authSlice';
|
||||
import { setSkillState } from '../store/skillsSlice';
|
||||
|
||||
function getCurrentUserId(): string | null {
|
||||
const state = store.getState();
|
||||
@@ -80,9 +80,7 @@ const handlePaymentDeepLink = async (parsed: URL) => {
|
||||
console.log('[DeepLink] Payment success, session_id:', sessionId);
|
||||
|
||||
// Broadcast to the app so billing components can react
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('payment:success', { detail: { sessionId } }),
|
||||
);
|
||||
window.dispatchEvent(new CustomEvent('payment:success', { detail: { sessionId } }));
|
||||
|
||||
// Navigate to billing settings to show confirmation
|
||||
window.location.hash = '/settings/billing';
|
||||
@@ -121,7 +119,6 @@ const handleOAuthDeepLink = async (parsed: URL) => {
|
||||
console.log(`[DeepLink] OAuth success for skill=${skillId} integration=${integrationId}`);
|
||||
|
||||
try {
|
||||
|
||||
const state = store.getState();
|
||||
const userId = getCurrentUserId();
|
||||
if (!userId) {
|
||||
@@ -283,9 +280,7 @@ export const setupDesktopDeepLinkListener = async () => {
|
||||
if (typeof window !== 'undefined') {
|
||||
// window.__simulateDeepLink('alphahuman://auth?token=1234567890')
|
||||
// window.__simulateDeepLink('alphahuman://oauth/success?integrationId=6989ef9c8e8bf1b6d991a08c&skillId=notion')
|
||||
const win = window as Window & {
|
||||
__simulateDeepLink?: (url: string) => Promise<void>;
|
||||
};
|
||||
const win = window as Window & { __simulateDeepLink?: (url: string) => Promise<void> };
|
||||
win.__simulateDeepLink = (url: string) => handleDeepLinkUrls([url]);
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
@@ -20,14 +20,10 @@ export function hexToBytes(hex: string): Uint8Array {
|
||||
const cleanHex = hex.trim().replace(/^0x/i, '');
|
||||
if (!cleanHex) return new Uint8Array();
|
||||
if (cleanHex.length % 2 !== 0) {
|
||||
throw new TypeError(
|
||||
`hexToBytes: hex string must have even length (got ${cleanHex.length})`
|
||||
);
|
||||
throw new TypeError(`hexToBytes: hex string must have even length (got ${cleanHex.length})`);
|
||||
}
|
||||
if (!HEX_REGEX.test(cleanHex)) {
|
||||
throw new TypeError(
|
||||
'hexToBytes: hex string must contain only [0-9a-fA-F] characters'
|
||||
);
|
||||
throw new TypeError('hexToBytes: hex string must contain only [0-9a-fA-F] characters');
|
||||
}
|
||||
const bytes = new Uint8Array(cleanHex.length / 2);
|
||||
for (let i = 0; i < cleanHex.length; i += 2) {
|
||||
|
||||
@@ -186,11 +186,7 @@ export interface SkillSnapshot {
|
||||
skill_id: string;
|
||||
name: string;
|
||||
status: unknown;
|
||||
tools: Array<{
|
||||
name: string;
|
||||
description: string;
|
||||
input_schema?: unknown;
|
||||
}>;
|
||||
tools: Array<{ name: string; description: string; input_schema?: unknown }>;
|
||||
error?: string | null;
|
||||
state?: Record<string, unknown>;
|
||||
}
|
||||
@@ -308,7 +304,11 @@ export interface TunnelConfig {
|
||||
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;
|
||||
custom?: {
|
||||
start_command: string;
|
||||
health_url?: string | null;
|
||||
url_pattern?: string | null;
|
||||
} | null;
|
||||
}
|
||||
|
||||
export async function alphahumanGetConfig(): Promise<CommandResponse<ConfigSnapshot>> {
|
||||
@@ -405,9 +405,7 @@ export async function alphahumanAgentChat(
|
||||
});
|
||||
}
|
||||
|
||||
export async function alphahumanEncryptSecret(
|
||||
plaintext: string
|
||||
): Promise<CommandResponse<string>> {
|
||||
export async function alphahumanEncryptSecret(plaintext: string): Promise<CommandResponse<string>> {
|
||||
if (!isTauri()) {
|
||||
throw new Error('Not running in Tauri');
|
||||
}
|
||||
@@ -437,10 +435,7 @@ export async function alphahumanDoctorModels(
|
||||
if (!isTauri()) {
|
||||
throw new Error('Not running in Tauri');
|
||||
}
|
||||
return await invoke('alphahuman_doctor_models', {
|
||||
providerOverride,
|
||||
useCache,
|
||||
});
|
||||
return await invoke('alphahuman_doctor_models', { providerOverride, useCache });
|
||||
}
|
||||
|
||||
export async function alphahumanListIntegrations(): Promise<CommandResponse<IntegrationInfo[]>> {
|
||||
@@ -476,10 +471,7 @@ export async function alphahumanMigrateOpenclaw(
|
||||
if (!isTauri()) {
|
||||
throw new Error('Not running in Tauri');
|
||||
}
|
||||
return await invoke('alphahuman_migrate_openclaw', {
|
||||
sourceWorkspace,
|
||||
dryRun,
|
||||
});
|
||||
return await invoke('alphahuman_migrate_openclaw', { sourceWorkspace, dryRun });
|
||||
}
|
||||
|
||||
export async function alphahumanHardwareDiscover(): Promise<CommandResponse<DiscoveredDevice[]>> {
|
||||
|
||||
Reference in New Issue
Block a user