This commit is contained in:
Sun Tao 2025-10-12 14:25:08 +08:00
parent a3b45a0c05
commit 77df69b585
5 changed files with 263 additions and 95 deletions

View file

@ -1,16 +1,29 @@
from typing import Any, Dict, List
import os
from app.component.environment import env
from app.service.task import Agents
from app.utils.listen.toolkit_listen import listen_toolkit
from app.utils.toolkit.abstract_toolkit import AbstractToolkit
from camel.toolkits import GoogleCalendarToolkit as BaseGoogleCalendarToolkit
SCOPES = ['https://www.googleapis.com/auth/calendar']
class GoogleCalendarToolkit(BaseGoogleCalendarToolkit, AbstractToolkit):
agent_name: str = Agents.social_medium_agent
def __init__(self, api_task_id: str, timeout: float | None = None):
self.api_task_id = api_task_id
self._token_path = (
os.environ.get("GOOGLE_CALENDAR_TOKEN_PATH")
or os.path.join(
os.path.expanduser("~"),
".eigent",
"tokens",
"google_calendar",
f"google_calendar_token_{api_task_id}.json",
)
)
super().__init__(timeout)
@listen_toolkit(BaseGoogleCalendarToolkit.create_event)
@ -24,10 +37,14 @@ class GoogleCalendarToolkit(BaseGoogleCalendarToolkit, AbstractToolkit):
attendees_email: List[str] | None = None,
timezone: str = "UTC",
) -> Dict[str, Any]:
return super().create_event(event_title, start_time, end_time, description, location, attendees_email, timezone)
return super().create_event(
event_title, start_time, end_time, description, location, attendees_email, timezone
)
@listen_toolkit(BaseGoogleCalendarToolkit.get_events)
def get_events(self, max_results: int = 10, time_min: str | None = None) -> List[Dict[str, Any]] | Dict[str, Any]:
def get_events(
self, max_results: int = 10, time_min: str | None = None
) -> List[Dict[str, Any]] | Dict[str, Any]:
return super().get_events(max_results, time_min)
@listen_toolkit(BaseGoogleCalendarToolkit.update_event)
@ -41,7 +58,9 @@ class GoogleCalendarToolkit(BaseGoogleCalendarToolkit, AbstractToolkit):
location: str | None = None,
attendees_email: List[str] | None = None,
) -> Dict[str, Any]:
return super().update_event(event_id, event_title, start_time, end_time, description, location, attendees_email)
return super().update_event(
event_id, event_title, start_time, end_time, description, location, attendees_email
)
@listen_toolkit(BaseGoogleCalendarToolkit.delete_event)
def delete_event(self, event_id: str) -> str:
@ -57,3 +76,75 @@ class GoogleCalendarToolkit(BaseGoogleCalendarToolkit, AbstractToolkit):
return cls(api_task_id).get_tools()
else:
return []
def _get_calendar_service(self):
from googleapiclient.discovery import build
from google.auth.transport.requests import Request
creds = self._authenticate()
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
try:
os.makedirs(os.path.dirname(self._token_path), exist_ok=True)
with open(self._token_path, "w") as f:
f.write(creds.to_json())
except Exception:
pass
return build("calendar", "v3", credentials=creds)
def _authenticate(self):
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
client_id = os.environ.get("GOOGLE_CLIENT_ID")
client_secret = os.environ.get("GOOGLE_CLIENT_SECRET")
refresh_token = os.environ.get("GOOGLE_REFRESH_TOKEN")
token_uri = os.environ.get("GOOGLE_TOKEN_URI", "https://oauth2.googleapis.com/token")
creds = None
try:
if os.path.exists(self._token_path):
creds = Credentials.from_authorized_user_file(self._token_path, SCOPES)
except Exception:
creds = None
if not creds and refresh_token:
creds = Credentials(
None,
refresh_token=refresh_token,
token_uri=token_uri,
client_id=client_id,
client_secret=client_secret,
scopes=SCOPES,
)
if not creds:
client_config = {
"installed": {
"client_id": client_id,
"client_secret": client_secret,
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": token_uri,
"redirect_uris": ["http://localhost"],
}
}
flow = InstalledAppFlow.from_client_config(client_config, SCOPES)
creds = flow.run_local_server(port=0)
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
try:
os.makedirs(os.path.dirname(self._token_path), exist_ok=True)
with open(self._token_path, "w") as f:
f.write(creds.to_json())
except Exception:
pass
return creds

View file

@ -1,7 +1,7 @@
import { Button } from "@/components/ui/button";
import { TooltipSimple } from "@/components/ui/tooltip";
import { CircleAlert } from "lucide-react";
import { proxyFetchGet, proxyFetchPost, proxyFetchDelete } from "@/api/http";
import { proxyFetchGet, proxyFetchPost, proxyFetchPut, proxyFetchDelete } from "@/api/http";
import React, { useState, useCallback, useEffect, useRef } from "react";
import ellipseIcon from "@/assets/mcp/Ellipse-25.svg";
@ -104,7 +104,21 @@ export default function IntegrationList({
config_name: envVarKey,
config_value: value,
};
await proxyFetchPost("/api/configs", configPayload);
// Check if config already exists
const existingConfig = configs.find(
(c: any) => c.config_name === envVarKey &&
c.config_group?.toLowerCase() === provider.toLowerCase()
);
if (existingConfig) {
// Update existing config
await proxyFetchPut(`/api/configs/${existingConfig.id}`, configPayload);
} else {
// Create new config
await proxyFetchPost("/api/configs", configPayload);
}
if (window.electronAPI?.envWrite) {
await window.electronAPI.envWrite(email, { key: envVarKey, value });
}
@ -223,39 +237,55 @@ export default function IntegrationList({
return;
}
if (item.key === "Google Calendar") {
let mcp = {
name: "Google Calendar",
key: "Google Calendar",
install_command: {
env: {} as any,
},
id: 14,
};
item.env_vars.map((key) => {
mcp.install_command.env[key] = "";
});
onShowEnvConfig?.(mcp);
return;
}
if (installed[item.key]) return;
await item.onInstall();
// refresh configs after install to update installed state indicator
await fetchInstalled();
if (item.key === "Google Calendar") {
let mcp = {
name: "Google Calendar",
key: "Google Calendar",
install_command: {
env: {} as any,
},
id: 14,
};
item.env_vars.map((key) => {
mcp.install_command.env[key] = "";
});
onShowEnvConfig?.(mcp);
return;
}
if (installed[item.key]) return;
await item.onInstall();
// refresh configs after install to update installed state indicator
await fetchInstalled();
},
[installed]
);
const onConnect = (mcp: any) => {
console.log(mcp);
Object.keys(mcp.install_command.env).map(async (key) => {
await saveEnvAndConfig(mcp.key, key, mcp.install_command.env[key]);
});
const onConnect = async (mcp: any) => {
// Refresh configs first to get latest state
await fetchInstalled();
// Save all environment variables
await Promise.all(
Object.keys(mcp.install_command.env).map(async (key) => {
return saveEnvAndConfig(mcp.key, key, mcp.install_command.env[key]);
})
);
fetchInstalled();
addOption(mcp, true);
onClose();
};
// After saving env vars, trigger installation/instantiation for Google Calendar
if (mcp.key === "Google Calendar") {
const calendarItem = items.find(item => item.key === "Google Calendar");
if (calendarItem && calendarItem.onInstall) {
await calendarItem.onInstall();
}
}
await fetchInstalled();
// Don't call addOption here for Google Calendar, as it's already called in onInstall
if (mcp.key !== "Google Calendar") {
addOption(mcp, true);
}
onClose();
};
const onClose = () => {
setShowEnvConfig(false);
setActiveMcp(null);

View file

@ -7,7 +7,7 @@ import React, {
} from "react";
import { Badge } from "@/components/ui/badge";
import { CircleAlert, Store, X } from "lucide-react";
import { proxyFetchGet, proxyFetchPost, fetchPost } from "@/api/http";
import { proxyFetchGet, proxyFetchPost, proxyFetchPut, fetchPost } from "@/api/http";
import { Input } from "../ui/input";
import { Button } from "../ui/button";
import githubIcon from "@/assets/github.svg";
@ -210,12 +210,30 @@ const ToolSelect = forwardRef<
envVarKey: string,
value: string
) => {
// First fetch current configs to check for existing ones
const configsRes = await proxyFetchGet("/api/configs");
const configs = Array.isArray(configsRes) ? configsRes : [];
const configPayload = {
config_group: capitalizeFirstLetter(provider),
config_name: envVarKey,
config_value: value,
};
await proxyFetchPost("/api/configs", configPayload);
// Check if config already exists
const existingConfig = configs.find(
(c: any) => c.config_name === envVarKey &&
c.config_group?.toLowerCase() === provider.toLowerCase()
);
if (existingConfig) {
// Update existing config
await proxyFetchPut(`/api/configs/${existingConfig.id}`, configPayload);
} else {
// Create new config
await proxyFetchPost("/api/configs", configPayload);
}
if (window.electronAPI?.envWrite) {
await window.electronAPI.envWrite(email, { key: envVarKey, value });
}
@ -233,36 +251,49 @@ const ToolSelect = forwardRef<
env[key] = envValue[key]?.value;
});
activeMcp.install_command.env = env;
Object.keys(activeMcp.install_command.env).map(async (key) => {
await saveEnvAndConfig(
activeMcp.key,
key,
activeMcp.install_command.env[key]
);
});
// Add to selected tools after saving config
// Save all env vars and wait for completion
console.log("[installMcp] Saving env vars for", activeMcp.key);
try {
await Promise.all(
Object.keys(activeMcp.install_command.env).map(async (key) => {
console.log("[installMcp] Saving", key, "=", activeMcp.install_command.env[key]);
return saveEnvAndConfig(
activeMcp.key,
key,
activeMcp.install_command.env[key]
);
})
);
console.log("[installMcp] All env vars saved successfully");
} catch (error) {
console.error("[installMcp] Failed to save env vars:", error);
// Continue anyway to trigger installation
}
// Trigger instantiation for Google Calendar
if (activeMcp.key === "Google Calendar") {
const calendarItem = {
id: activeMcp.id,
key: activeMcp.key,
name: activeMcp.name,
description: "Google Calendar integration for managing events and schedules",
toolkit: "google_calendar_toolkit",
isLocal: true
};
addOption(calendarItem, true);
try {
const response = await fetchPost("/install/tool/google_calendar");
if (response.success) {
const selectedItem = {
id: activeMcp.id,
key: activeMcp.key,
name: activeMcp.name,
description: "Google Calendar integration for managing events and schedules",
toolkit: "google_calendar_toolkit",
isLocal: true
};
addOption(selectedItem, true);
} else {
console.error("Failed to install Google Calendar:", response.error || "Unknown error");
}
} catch (error: any) {
console.error("Failed to install Google Calendar:", error.message);
}
}
return;
// async function fetchInstalled() {
// try {
// const configsRes = await proxyFetchGet("/api/configs");
// setConfigs(Array.isArray(configsRes) ? configsRes : []);
// } catch (e) {
// setConfigs([]);
// }
// }
// fetchInstalled();
}
setInstalling((prev) => ({ ...prev, [id]: true }));
try {

View file

@ -109,7 +109,7 @@ export default function SettingMCP() {
proxyFetchGet("/api/config/info").then((res) => {
if (res && typeof res === "object") {
const baseURL = getProxyBaseURL();
const list = Object.entries(res).map(([key, value]: [string, any]) => {
const list = Object.entries(res).map(([key, value]: [string, any]) => {
let onInstall = null;
// Special handling for Notion MCP
@ -136,18 +136,12 @@ export default function SettingMCP() {
toast.error(error.message || "Failed to install Notion MCP");
}
};
} else if (key.toLowerCase() === 'google calendar') {
} else if (key.toLowerCase() === 'google calendar') {
onInstall = async () => {
try {
const response = await fetchPost("/install/tool/google_calendar");
if (response.success) {
toast.success("Google Calendar installed successfully");
// Save to config to mark as installed
await proxyFetchPost("/api/configs", {
config_group: "Google Calendar",
config_name: "GOOGLE_CLIENT_ID",
config_value: response.toolkit_name || "GoogleCalendarToolkit",
});
toast.success("Google Calendar installed successfully");
// Refresh the integrations list to show the installed state
fetchList();
// Force refresh IntegrationList component
@ -181,10 +175,6 @@ export default function SettingMCP() {
onInstall,
};
});
console.log("API response:", res);
console.log("Generated list:", list);
console.log("Essential integrations:", essentialIntegrations);
setIntegrations(
list.filter(
(item) => !essentialIntegrations.find((i) => i.key === item.key)

View file

@ -8,6 +8,7 @@ import { CircleAlert } from "lucide-react";
import {
proxyFetchGet,
proxyFetchPost,
proxyFetchPut,
proxyFetchDelete,
} from "@/api/http";
@ -102,7 +103,21 @@ export default function IntegrationList({
config_name: envVarKey,
config_value: value,
};
await proxyFetchPost("/api/configs", configPayload);
// Check if config already exists
const existingConfig = configs.find(
(c: any) => c.config_name === envVarKey &&
c.config_group?.toLowerCase() === provider.toLowerCase()
);
if (existingConfig) {
// Update existing config
await proxyFetchPut(`/api/configs/${existingConfig.id}`, configPayload);
} else {
// Create new config
await proxyFetchPost("/api/configs", configPayload);
}
if (window.electronAPI?.envWrite) {
await window.electronAPI.envWrite(email, { key: envVarKey, value });
}
@ -228,38 +243,49 @@ export default function IntegrationList({
return;
}
if (item.key === "Google Calendar") {
let mcp = {
name: "Google Calendar",
key: "Google Calendar",
install_command: {
env: {} as any,
},
id: 14,
};
item.env_vars.map((key) => {
mcp.install_command.env[key] = "";
});
setActiveMcp(mcp);
setShowEnvConfig(true);
return;
}
if (item.key === "Google Calendar") {
let mcp = {
name: "Google Calendar",
key: "Google Calendar",
install_command: {
env: {} as any,
},
id: 14,
};
item.env_vars.map((key) => {
mcp.install_command.env[key] = "";
});
setActiveMcp(mcp);
setShowEnvConfig(true);
return;
}
if (installed[item.key]) return;
await item.onInstall();
if (installed[item.key]) return;
await item.onInstall();
},
[installed]
);
const onConnect = async (mcp: any) => {
console.log(mcp);
// Refresh configs first to get latest state
await fetchInstalled();
// Save all environment variables
await Promise.all(
Object.keys(mcp.install_command.env).map((key) => {
return saveEnvAndConfig(mcp.key, key, mcp.install_command.env[key]);
})
);
fetchInstalled();
// After saving env vars, trigger installation/instantiation for Google Calendar
if (mcp.key === "Google Calendar") {
const calendarItem = items.find(item => item.key === "Google Calendar");
if (calendarItem && calendarItem.onInstall) {
await calendarItem.onInstall();
}
}
await fetchInstalled();
onClose();
};
const onClose = () => {