mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-16 18:25:17 +02:00
Refactor Composio connection handling and improve tool display logic
- Removed the connection bridge for Composio toolkit OAuth, simplifying the connection process. - Updated ComposioConnectCard to display a more user-friendly connection message. - Introduced a new utility function, getToolDisplayName, to provide human-friendly names for builtin tools. - Refactored App and ChatSidebar components to utilize the new getToolDisplayName function for improved clarity in tool titles. - Cleaned up imports and removed unused code for better maintainability.
This commit is contained in:
parent
587778cb07
commit
fd1632ea5c
7 changed files with 65 additions and 79 deletions
|
|
@ -34,7 +34,6 @@ import { triggerSync as triggerGranolaSync } from '@x/core/dist/knowledge/granol
|
|||
import { ISlackConfigRepo } from '@x/core/dist/slack/repo.js';
|
||||
import { isOnboardingComplete, markOnboardingComplete } from '@x/core/dist/config/note_creation_config.js';
|
||||
import * as composioHandler from './composio-handler.js';
|
||||
import { setConnectionInitiator } from '@x/core/dist/composio/connection-bridge.js';
|
||||
import { IAgentScheduleRepo } from '@x/core/dist/agent-schedule/repo.js';
|
||||
import { IAgentScheduleStateRepo } from '@x/core/dist/agent-schedule/state-repo.js';
|
||||
import { triggerRun as triggerAgentScheduleRun } from '@x/core/dist/agent-schedule/runner.js';
|
||||
|
|
@ -377,9 +376,6 @@ export function setupIpcHandlers() {
|
|||
// Forward knowledge commit events to renderer for panel refresh
|
||||
versionHistory.onCommit(() => emitKnowledgeCommitEvent());
|
||||
|
||||
// Wire the connection bridge so builtin tools (in core) can trigger OAuth (in main)
|
||||
setConnectionInitiator(composioHandler.initiateConnection);
|
||||
|
||||
registerIpcHandlers({
|
||||
'app:getVersions': async () => {
|
||||
// args is null for this channel (no request payload)
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ import {
|
|||
getWebSearchCardData,
|
||||
getAppActionCardData,
|
||||
getComposioConnectCardData,
|
||||
getComposioActionCardData,
|
||||
getToolDisplayName,
|
||||
inferRunTitleFromMessage,
|
||||
isChatMessage,
|
||||
isErrorMessage,
|
||||
|
|
@ -3849,8 +3849,7 @@ function App() {
|
|||
/>
|
||||
)
|
||||
}
|
||||
const composioActionData = getComposioActionCardData(item)
|
||||
const toolTitle = composioActionData ? composioActionData.label : item.name
|
||||
const toolTitle = getToolDisplayName(item)
|
||||
const errorText = item.status === 'error' ? 'Tool error' : ''
|
||||
const output = normalizeToolOutput(item.result, item.status)
|
||||
const input = normalizeToolInput(item.input)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import {
|
|||
CheckCircleIcon,
|
||||
Link2Icon,
|
||||
LoaderIcon,
|
||||
WrenchIcon,
|
||||
XCircleIcon,
|
||||
} from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
|
@ -67,18 +66,21 @@ export function ComposioConnectCard({
|
|||
}, [toolkitSlug]);
|
||||
|
||||
const isToolRunning = status === "pending" || status === "running";
|
||||
const displayName = toolkitDisplayName || toolkitSlug;
|
||||
|
||||
return (
|
||||
<div className="not-prose mb-4 flex items-center gap-3 rounded-lg border px-3 py-2.5">
|
||||
{/* Toolkit initial */}
|
||||
<div className="size-7 rounded bg-muted flex items-center justify-center flex-shrink-0">
|
||||
<WrenchIcon className="size-3.5 text-muted-foreground" />
|
||||
<span className="text-xs font-bold text-muted-foreground">
|
||||
{displayName.charAt(0).toUpperCase()}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Name & status */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="text-sm font-medium truncate">
|
||||
{toolkitDisplayName || toolkitSlug}
|
||||
</span>
|
||||
<span className="text-sm font-medium truncate">{displayName}</span>
|
||||
{connectionState === "connected" && (
|
||||
<span className="rounded-full bg-green-500/10 px-1.5 py-0.5 text-[10px] font-medium leading-none text-green-600">
|
||||
Connected
|
||||
|
|
@ -93,6 +95,7 @@ export function ComposioConnectCard({
|
|||
)}
|
||||
</div>
|
||||
|
||||
{/* Action area */}
|
||||
{connectionState === "connected" ? (
|
||||
<CheckCircleIcon className="size-4 text-green-600 flex-shrink-0" />
|
||||
) : connectionState === "connecting" ? (
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import {
|
|||
createEmptyChatTabViewState,
|
||||
getWebSearchCardData,
|
||||
getComposioConnectCardData,
|
||||
getComposioActionCardData,
|
||||
getToolDisplayName,
|
||||
isChatMessage,
|
||||
isErrorMessage,
|
||||
isToolCall,
|
||||
|
|
@ -355,8 +355,7 @@ export function ChatSidebar({
|
|||
/>
|
||||
)
|
||||
}
|
||||
const composioActionData = getComposioActionCardData(item)
|
||||
const toolTitle = composioActionData ? composioActionData.label : item.name
|
||||
const toolTitle = getToolDisplayName(item)
|
||||
const errorText = item.status === 'error' ? 'Tool error' : ''
|
||||
const output = normalizeToolOutput(item.result, item.status)
|
||||
const input = normalizeToolInput(item.input)
|
||||
|
|
|
|||
|
|
@ -278,6 +278,51 @@ export const getComposioConnectCardData = (tool: ToolCall): ComposioConnectCardD
|
|||
}
|
||||
}
|
||||
|
||||
// Human-friendly display names for builtin tools
|
||||
const TOOL_DISPLAY_NAMES: Record<string, string> = {
|
||||
'workspace-readFile': 'Reading file',
|
||||
'workspace-writeFile': 'Writing file',
|
||||
'workspace-edit': 'Editing file',
|
||||
'workspace-readdir': 'Reading directory',
|
||||
'workspace-exists': 'Checking path',
|
||||
'workspace-stat': 'Getting file info',
|
||||
'workspace-glob': 'Finding files',
|
||||
'workspace-grep': 'Searching files',
|
||||
'workspace-mkdir': 'Creating directory',
|
||||
'workspace-rename': 'Renaming',
|
||||
'workspace-copy': 'Copying file',
|
||||
'workspace-remove': 'Removing',
|
||||
'workspace-getRoot': 'Getting workspace root',
|
||||
'loadSkill': 'Loading skill',
|
||||
'parseFile': 'Parsing file',
|
||||
'LLMParse': 'Extracting content',
|
||||
'analyzeAgent': 'Analyzing agent',
|
||||
'executeCommand': 'Running command',
|
||||
'addMcpServer': 'Adding MCP server',
|
||||
'listMcpServers': 'Listing MCP servers',
|
||||
'listMcpTools': 'Listing MCP tools',
|
||||
'executeMcpTool': 'Running MCP tool',
|
||||
'web-search': 'Searching the web',
|
||||
'save-to-memory': 'Saving to memory',
|
||||
'app-navigation': 'Navigating app',
|
||||
'composio-list-toolkits': 'Listing integrations',
|
||||
'composio-search-tools': 'Searching tools',
|
||||
'composio-execute-tool': 'Running tool',
|
||||
'composio-connect-toolkit': 'Connecting service',
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a human-friendly display name for a tool call.
|
||||
* For Composio tools, returns a contextual label (e.g., "Found 3 tools for 'send email' in Gmail").
|
||||
* For builtin tools, returns a static friendly name (e.g., "Reading file").
|
||||
* Falls back to the raw tool name if no mapping exists.
|
||||
*/
|
||||
export const getToolDisplayName = (tool: ToolCall): string => {
|
||||
const composioData = getComposioActionCardData(tool)
|
||||
if (composioData) return composioData.label
|
||||
return TOOL_DISPLAY_NAMES[tool.name] || tool.name
|
||||
}
|
||||
|
||||
// Composio action card data (for search, execute, list tools)
|
||||
export type ComposioActionCardData = {
|
||||
actionType: 'search' | 'execute' | 'list'
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import { WorkDir } from "../../config/config.js";
|
|||
import { composioAccountsRepo } from "../../composio/repo.js";
|
||||
import { executeAction as executeComposioAction, isConfigured as isComposioConfigured, searchTools as searchComposioTools } from "../../composio/client.js";
|
||||
import { CURATED_TOOLKITS, CURATED_TOOLKIT_SLUGS } from "@x/shared/dist/composio.js";
|
||||
import { getConnectionInitiator } from "../../composio/connection-bridge.js";
|
||||
import type { ToolContext } from "./exec-tool.js";
|
||||
import { generateText } from "ai";
|
||||
import { createProvider } from "../../models/models.js";
|
||||
|
|
@ -1292,7 +1291,7 @@ export const BuiltinTools: z.infer<typeof BuiltinToolsSchema> = {
|
|||
},
|
||||
|
||||
'composio-connect-toolkit': {
|
||||
description: 'Connect a Composio service (Gmail, Slack, GitHub, etc.) via OAuth. Opens the user\'s browser for authentication. After authenticating, the user can use tools from that service.',
|
||||
description: 'Connect a Composio service (Gmail, Slack, GitHub, etc.) via OAuth. Shows a connect card for the user to authenticate.',
|
||||
inputSchema: z.object({
|
||||
toolkitSlug: z.string().describe('The toolkit slug to connect (e.g., "gmail", "github", "slack", "notion")'),
|
||||
}),
|
||||
|
|
@ -1315,35 +1314,13 @@ export const BuiltinTools: z.infer<typeof BuiltinToolsSchema> = {
|
|||
};
|
||||
}
|
||||
|
||||
// Use the connection bridge to trigger OAuth
|
||||
const initiator = getConnectionInitiator();
|
||||
if (!initiator) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Connection system not available. Please try connecting via Settings > Tools Library instead.',
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await initiator(toolkitSlug);
|
||||
if (result.success) {
|
||||
const toolkit = CURATED_TOOLKITS.find(t => t.slug === toolkitSlug);
|
||||
return {
|
||||
success: true,
|
||||
message: `Opening browser to authenticate with ${toolkit?.displayName ?? toolkitSlug}. Please complete the authentication in your browser, then let me know when you're done.`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
error: result.error || 'Failed to initiate connection',
|
||||
};
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
return {
|
||||
success: false,
|
||||
error: `Connection failed: ${message}`,
|
||||
};
|
||||
}
|
||||
// Return signal — the UI renders a ComposioConnectCard with a Connect button.
|
||||
// OAuth only starts when the user clicks that button.
|
||||
const toolkit = CURATED_TOOLKITS.find(t => t.slug === toolkitSlug);
|
||||
return {
|
||||
success: true,
|
||||
message: `Please connect ${toolkit?.displayName ?? toolkitSlug} to continue.`,
|
||||
};
|
||||
},
|
||||
isAvailable: async () => isComposioConfigured(),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
/**
|
||||
* Connection bridge for Composio toolkit OAuth.
|
||||
*
|
||||
* Builtin tools run in the core package which cannot import Electron-specific
|
||||
* code from the main process. This module provides a callback registry so the
|
||||
* main process can register its `initiateConnection` function at startup, and
|
||||
* builtin tools can call it at runtime.
|
||||
*/
|
||||
|
||||
type ConnectionInitiator = (toolkitSlug: string) => Promise<{
|
||||
success: boolean;
|
||||
redirectUrl?: string;
|
||||
connectedAccountId?: string;
|
||||
error?: string;
|
||||
}>;
|
||||
|
||||
let connectionInitiator: ConnectionInitiator | null = null;
|
||||
|
||||
/**
|
||||
* Register the connection initiator callback.
|
||||
* Called once by the main process at startup.
|
||||
*/
|
||||
export function setConnectionInitiator(fn: ConnectionInitiator): void {
|
||||
connectionInitiator = fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the registered connection initiator.
|
||||
* Returns null if not yet registered (app not fully initialized).
|
||||
*/
|
||||
export function getConnectionInitiator(): ConnectionInitiator | null {
|
||||
return connectionInitiator;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue