mirror of https://github.com/kortix-ai/suna.git
Add Kortix Admin API Key to setup
This commit is contained in:
parent
1d01571500
commit
0e8c58106a
|
@ -165,6 +165,17 @@ export default function AgentsPage() {
|
|||
metadata: template.metadata,
|
||||
};
|
||||
|
||||
// Apply search filtering to each item
|
||||
const matchesSearch = !marketplaceSearchQuery.trim() || (() => {
|
||||
const searchLower = marketplaceSearchQuery.toLowerCase();
|
||||
return item.name.toLowerCase().includes(searchLower) ||
|
||||
item.description?.toLowerCase().includes(searchLower) ||
|
||||
item.tags.some(tag => tag.toLowerCase().includes(searchLower)) ||
|
||||
item.creator_name?.toLowerCase().includes(searchLower);
|
||||
})();
|
||||
|
||||
if (!matchesSearch) return; // Skip items that don't match search
|
||||
|
||||
// Always add user's own templates to mineItems for the "mine" filter
|
||||
if (user?.id === template.creator_id) {
|
||||
mineItems.push(item);
|
||||
|
@ -201,7 +212,7 @@ export default function AgentsPage() {
|
|||
communityItems: sortItems(communityItems),
|
||||
mineItems: sortItems(mineItems)
|
||||
};
|
||||
}, [marketplaceTemplates, marketplaceSortBy, user?.id]);
|
||||
}, [marketplaceTemplates, marketplaceSortBy, user?.id, marketplaceSearchQuery]);
|
||||
|
||||
const allMarketplaceItems = useMemo(() => {
|
||||
if (marketplaceFilter === 'kortix') {
|
||||
|
|
|
@ -1,163 +0,0 @@
|
|||
import React from 'react';
|
||||
import { Search, Filter, SortAsc, SortDesc, X, Settings, Wrench, Grid3X3, List } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, DropdownMenuCheckboxItem } from '@/components/ui/dropdown-menu';
|
||||
|
||||
type AgentSortOption = 'name' | 'created_at' | 'updated_at' | 'tools_count';
|
||||
type SortOrder = 'asc' | 'desc';
|
||||
type ViewMode = 'grid' | 'list';
|
||||
|
||||
interface FilterOptions {
|
||||
hasDefaultAgent: boolean;
|
||||
hasMcpTools: boolean;
|
||||
hasAgentpressTools: boolean;
|
||||
selectedTools: string[];
|
||||
}
|
||||
|
||||
interface SearchAndFiltersProps {
|
||||
searchQuery: string;
|
||||
setSearchQuery: (query: string) => void;
|
||||
sortBy: AgentSortOption;
|
||||
setSortBy: (sort: AgentSortOption) => void;
|
||||
sortOrder: SortOrder;
|
||||
setSortOrder: (order: SortOrder) => void;
|
||||
filters: FilterOptions;
|
||||
setFilters: React.Dispatch<React.SetStateAction<FilterOptions>>;
|
||||
activeFiltersCount: number;
|
||||
clearFilters: () => void;
|
||||
viewMode: ViewMode;
|
||||
setViewMode: (mode: ViewMode) => void;
|
||||
allTools: string[];
|
||||
}
|
||||
|
||||
export const SearchAndFilters = ({
|
||||
searchQuery,
|
||||
setSearchQuery,
|
||||
sortBy,
|
||||
setSortBy,
|
||||
sortOrder,
|
||||
setSortOrder,
|
||||
filters,
|
||||
setFilters,
|
||||
activeFiltersCount,
|
||||
clearFilters,
|
||||
viewMode,
|
||||
setViewMode,
|
||||
allTools
|
||||
}: SearchAndFiltersProps) => {
|
||||
return (
|
||||
<div className="flex flex-col w-full gap-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div className="flex flex-col w-full gap-3 sm:flex-row sm:items-center flex-1">
|
||||
<div className="relative flex-1 w-full border rounded-xl">
|
||||
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder="Search agents..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="pl-10"
|
||||
/>
|
||||
{searchQuery && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="absolute right-1 top-1/2 h-7 w-7 -translate-y-1/2 p-0"
|
||||
onClick={() => setSearchQuery('')}
|
||||
>
|
||||
<X className="h-3 w-3" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* <Select value={sortBy} onValueChange={(value: SortOption) => setSortBy(value)}>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue placeholder="Sort by" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="name">Name</SelectItem>
|
||||
<SelectItem value="created_at">Created Date</SelectItem>
|
||||
<SelectItem value="updated_at">Updated Date</SelectItem>
|
||||
<SelectItem value="tools_count">Tools Count</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc')}
|
||||
className="px-3"
|
||||
>
|
||||
{sortOrder === 'asc' ? <SortAsc className="h-4 w-4" /> : <SortDesc className="h-4 w-4" />}
|
||||
</Button> */}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
{/* <DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="sm" className="relative">
|
||||
<Filter className="h-4 w-4" />
|
||||
Filter
|
||||
{activeFiltersCount > 0 && (
|
||||
<Badge variant="secondary" className="ml-2 h-5 w-5 rounded-full p-0 text-xs">
|
||||
{activeFiltersCount}
|
||||
</Badge>
|
||||
)}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-56">
|
||||
<DropdownMenuLabel>Filter by</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuCheckboxItem
|
||||
checked={filters.hasMcpTools}
|
||||
onCheckedChange={(checked) =>
|
||||
setFilters(prev => ({ ...prev, hasMcpTools: checked }))
|
||||
}
|
||||
>
|
||||
<Wrench className="h-4 w-4" />
|
||||
Has MCP tools
|
||||
</DropdownMenuCheckboxItem>
|
||||
<DropdownMenuCheckboxItem
|
||||
checked={filters.hasAgentpressTools}
|
||||
onCheckedChange={(checked) =>
|
||||
setFilters(prev => ({ ...prev, hasAgentpressTools: checked }))
|
||||
}
|
||||
>
|
||||
<Settings className="h-4 w-4" />
|
||||
Has Default tools
|
||||
</DropdownMenuCheckboxItem>
|
||||
{activeFiltersCount > 0 && (
|
||||
<>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={clearFilters}>
|
||||
<X className="h-4 w-4" />
|
||||
Clear filters
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu> */}
|
||||
|
||||
{/* <div className="flex border rounded-md">
|
||||
<Button
|
||||
variant={viewMode === 'grid' ? 'default' : 'ghost'}
|
||||
size="sm"
|
||||
onClick={() => setViewMode('grid')}
|
||||
className="rounded-r-none border-r"
|
||||
>
|
||||
<Grid3X3 className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant={viewMode === 'list' ? 'default' : 'ghost'}
|
||||
size="sm"
|
||||
onClick={() => setViewMode('list')}
|
||||
className="rounded-l-none"
|
||||
>
|
||||
<List className="h-4 w-4" />
|
||||
</Button>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
40
setup.py
40
setup.py
|
@ -168,6 +168,9 @@ def load_existing_env_vars():
|
|||
"PIPEDREAM_CLIENT_SECRET": backend_env.get("PIPEDREAM_CLIENT_SECRET", ""),
|
||||
"PIPEDREAM_X_PD_ENVIRONMENT": backend_env.get("PIPEDREAM_X_PD_ENVIRONMENT", ""),
|
||||
},
|
||||
"kortix": {
|
||||
"KORTIX_ADMIN_API_KEY": backend_env.get("KORTIX_ADMIN_API_KEY", ""),
|
||||
},
|
||||
"frontend": {
|
||||
"NEXT_PUBLIC_SUPABASE_URL": frontend_env.get(
|
||||
"NEXT_PUBLIC_SUPABASE_URL", ""
|
||||
|
@ -241,6 +244,13 @@ def generate_encryption_key():
|
|||
return base64.b64encode(key_bytes).decode("utf-8")
|
||||
|
||||
|
||||
def generate_admin_api_key():
|
||||
"""Generates a secure admin API key for Kortix."""
|
||||
# Generate 32 random bytes and encode as hex for a readable API key
|
||||
key_bytes = secrets.token_bytes(32)
|
||||
return key_bytes.hex()
|
||||
|
||||
|
||||
# --- Main Setup Class ---
|
||||
class SetupWizard:
|
||||
def __init__(self):
|
||||
|
@ -263,6 +273,7 @@ class SetupWizard:
|
|||
"webhook": existing_env_vars["webhook"],
|
||||
"mcp": existing_env_vars["mcp"],
|
||||
"pipedream": existing_env_vars["pipedream"],
|
||||
"kortix": existing_env_vars["kortix"],
|
||||
}
|
||||
|
||||
# Override with any progress data (in case user is resuming)
|
||||
|
@ -273,7 +284,7 @@ class SetupWizard:
|
|||
else:
|
||||
self.env_vars[key] = value
|
||||
|
||||
self.total_steps = 18
|
||||
self.total_steps = 19
|
||||
|
||||
def show_current_config(self):
|
||||
"""Shows the current configuration status."""
|
||||
|
@ -359,6 +370,12 @@ class SetupWizard:
|
|||
else:
|
||||
config_items.append(f"{Colors.YELLOW}○{Colors.ENDC} Morph (recommended)")
|
||||
|
||||
# Check Kortix configuration
|
||||
if self.env_vars["kortix"]["KORTIX_ADMIN_API_KEY"]:
|
||||
config_items.append(f"{Colors.GREEN}✓{Colors.ENDC} Kortix Admin")
|
||||
else:
|
||||
config_items.append(f"{Colors.YELLOW}○{Colors.ENDC} Kortix Admin")
|
||||
|
||||
if any("✓" in item for item in config_items):
|
||||
print_info("Current configuration status:")
|
||||
for item in config_items:
|
||||
|
@ -384,6 +401,7 @@ class SetupWizard:
|
|||
self.run_step(6, self.collect_morph_api_key)
|
||||
self.run_step(7, self.collect_search_api_keys)
|
||||
self.run_step(8, self.collect_rapidapi_keys)
|
||||
self.run_step(9, self.collect_kortix_keys)
|
||||
self.run_step(10, self.collect_qstash_keys)
|
||||
self.run_step(11, self.collect_mcp_keys)
|
||||
self.run_step(12, self.collect_pipedream_keys)
|
||||
|
@ -893,6 +911,24 @@ class SetupWizard:
|
|||
else:
|
||||
print_info("Skipping RapidAPI key.")
|
||||
|
||||
def collect_kortix_keys(self):
|
||||
"""Generates or configures the Kortix admin API key."""
|
||||
print_step(9, self.total_steps, "Configuring Kortix Admin API Key")
|
||||
|
||||
# Check if we already have a value configured
|
||||
existing_key = self.env_vars["kortix"]["KORTIX_ADMIN_API_KEY"]
|
||||
if existing_key:
|
||||
print_info(
|
||||
f"Found existing Kortix admin API key: {mask_sensitive_value(existing_key)}"
|
||||
)
|
||||
print_info("Using existing admin API key.")
|
||||
else:
|
||||
print_info("Generating a secure admin API key for Kortix administrative functions...")
|
||||
self.env_vars["kortix"]["KORTIX_ADMIN_API_KEY"] = generate_admin_api_key()
|
||||
print_success("Kortix admin API key generated.")
|
||||
|
||||
print_success("Kortix admin configuration saved.")
|
||||
|
||||
def collect_qstash_keys(self):
|
||||
"""Collects the required QStash configuration."""
|
||||
print_step(
|
||||
|
@ -1129,6 +1165,7 @@ class SetupWizard:
|
|||
**self.env_vars["mcp"],
|
||||
**self.env_vars["pipedream"],
|
||||
**self.env_vars["daytona"],
|
||||
**self.env_vars["kortix"],
|
||||
"NEXT_PUBLIC_URL": "http://localhost:3000",
|
||||
}
|
||||
|
||||
|
@ -1149,6 +1186,7 @@ class SetupWizard:
|
|||
"NEXT_PUBLIC_BACKEND_URL": "http://localhost:8000/api",
|
||||
"NEXT_PUBLIC_URL": "http://localhost:3000",
|
||||
"NEXT_PUBLIC_ENV_MODE": "LOCAL",
|
||||
"KORTIX_ADMIN_API_KEY": self.env_vars["kortix"]["KORTIX_ADMIN_API_KEY"],
|
||||
}
|
||||
|
||||
frontend_env_content = "# Generated by Suna install script\n\n"
|
||||
|
|
Loading…
Reference in New Issue