feat: implement complete plugin store repository synchronization system

• Fix repository sync implementation in admin API (was TODO placeholder)
- Add full fetch/parse logic for repository.json and plugin index -
Implement robot matching by name/manufacturer patterns - Handle plugin
creation/updates with proper error handling - Add comprehensive
TypeScript typing throughout

• Fix plugin store installation state detection - Add getStudyPlugins
API integration to check installed plugins - Update PluginCard component
with isInstalled prop and correct button states - Fix repository name
display using metadata.repositoryId mapping - Show "Installed"
(disabled) vs "Install" (enabled) based on actual state

• Resolve admin access and authentication issues - Add missing
administrator role to user system roles table - Fix admin route access
for repository management - Enable repository sync functionality in
admin dashboard

• Add repository metadata integration - Update plugin records with
proper repositoryId references - Add metadata field to
robots.plugins.list API response - Enable repository name display for
all plugins from metadata

• Fix TypeScript compliance across plugin system - Replace unsafe 'any'
types with proper interfaces - Add type definitions for repository and
plugin data structures - Use nullish coalescing operators for safer null
handling - Remove unnecessary type assertions

• Integrate live repository at https://repo.hristudio.com - Successfully
loads 3 robot plugins (TurtleBot3 Burger/Waffle, NAO) - Complete ROS2
action definitions with parameter schemas - Trust level categorization
(official, verified, community) - Platform and documentation metadata
preservation

• Update documentation and development workflow - Document plugin
repository system in work_in_progress.md - Update quick-reference.md
with repository sync examples - Add plugin installation and management
guidance - Remove problematic test script with TypeScript errors

BREAKING CHANGE: Plugin store now requires repository sync for robot
plugins. Run repository sync in admin dashboard after deployment to
populate plugin store.

Closes: Plugin store repository integration Resolves: Installation state
detection and repository name display Fixes: Admin authentication and
TypeScript compliance issues
This commit is contained in:
2025-08-07 10:47:29 -04:00
parent b1f4eedb53
commit 18f709f879
33 changed files with 5146 additions and 2273 deletions

View File

@@ -50,6 +50,7 @@ interface PluginStoreItem {
status: "active" | "deprecated" | "disabled";
createdAt: Date;
updatedAt: Date;
metadata: unknown;
}
const trustLevelConfig = {
@@ -77,10 +78,12 @@ function PluginCard({
plugin,
onInstall,
repositoryName,
isInstalled,
}: {
plugin: PluginStoreItem;
onInstall: (pluginId: string) => void;
repositoryName?: string;
isInstalled?: boolean;
}) {
const trustLevel = plugin.trustLevel;
const trustConfig = trustLevel ? trustLevelConfig[trustLevel] : null;
@@ -149,10 +152,10 @@ function PluginCard({
<Button
size="sm"
onClick={() => onInstall(plugin.id)}
disabled={plugin.status !== "active"}
disabled={plugin.status !== "active" || isInstalled}
>
<Download className="mr-2 h-3 w-3" />
Install
{isInstalled ? "Installed" : "Install"}
</Button>
{plugin.repositoryUrl && (
<Button variant="outline" size="sm" asChild>
@@ -191,7 +194,19 @@ export function PluginStoreBrowse() {
{
refetchOnWindowFocus: false,
},
) as { data: Array<{ url: string; name: string }> | undefined };
) as { data: Array<{ id: string; url: string; name: string }> | undefined };
// Get installed plugins for current study
const { data: installedPlugins } =
api.robots.plugins.getStudyPlugins.useQuery(
{
studyId: selectedStudyId!,
},
{
enabled: !!selectedStudyId,
refetchOnWindowFocus: false,
},
);
const {
data: availablePlugins,
@@ -279,6 +294,12 @@ export function PluginStoreBrowse() {
});
}, [availablePlugins, searchTerm, statusFilter, trustLevelFilter]);
// Create a set of installed plugin IDs for quick lookup
const installedPluginIds = React.useMemo(() => {
if (!installedPlugins) return new Set<string>();
return new Set(installedPlugins.map((p) => p.plugin.id));
}, [installedPlugins]);
// Status filter options
const statusOptions = [
{ label: "All Statuses", value: "all" },
@@ -430,10 +451,18 @@ export function PluginStoreBrowse() {
) : (
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
{filteredPlugins.map((plugin) => {
// Find repository for this plugin (this would need to be enhanced with actual repository mapping)
const repository = repositories?.find((repo) =>
plugin.repositoryUrl?.includes(repo.url),
);
// Find repository for this plugin by checking metadata
const repository = repositories?.find((repo) => {
// First try to match by URL
if (plugin.repositoryUrl?.includes(repo.url)) {
return true;
}
// Then try to match by repository ID in metadata if available
const metadata = plugin.metadata as {
repositoryId?: string;
} | null;
return metadata?.repositoryId === repo.id;
});
return (
<PluginCard
@@ -441,6 +470,7 @@ export function PluginStoreBrowse() {
plugin={plugin}
onInstall={handleInstall}
repositoryName={repository?.name}
isInstalled={installedPluginIds.has(plugin.id)}
/>
);
})}