UNPKG

@elizaos/plugin-google-meet-cute

Version:

Google Meet integration plugin for ElizaOS - manage meetings, get participant info, and access meeting artifacts via Google Meet REST API

938 lines (918 loc) 32.1 kB
import { Service, logger } from '@elizaos/core'; import { google } from 'googleapis'; import { createServer } from 'http'; import { URL } from 'url'; import { z } from 'zod'; import * as fs from 'fs/promises'; import * as path from 'path'; // src/index.ts var GoogleAuthService = class _GoogleAuthService extends Service { static serviceType = "google-auth"; oauth2Client; authenticated = false; get capabilityDescription() { return "Google OAuth2 authentication service for Google Meet API"; } constructor(runtime) { super(runtime); const clientId = runtime.getSetting("GOOGLE_CLIENT_ID"); const clientSecret = runtime.getSetting("GOOGLE_CLIENT_SECRET"); const redirectUri = runtime.getSetting("GOOGLE_REDIRECT_URI") || "http://localhost:3000/oauth2callback"; if (!clientId || !clientSecret) { throw new Error("Google OAuth2 credentials not configured. Please set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET"); } this.oauth2Client = new google.auth.OAuth2( clientId, clientSecret, redirectUri ); const refreshToken = runtime.getSetting("GOOGLE_REFRESH_TOKEN"); if (refreshToken) { this.oauth2Client.setCredentials({ refresh_token: refreshToken }); this.authenticated = true; logger.info("Authenticated with Google using refresh token"); } } static async start(runtime) { logger.info("Starting Google Auth Service"); const service = new _GoogleAuthService(runtime); await service.initialize(); return service; } async initialize() { if (!this.authenticated) { logger.info("Google OAuth2 client initialized. Use authenticateInteractive() to authenticate."); } } getAuthUrl() { const scopes = [ "https://www.googleapis.com/auth/meetings.space.created", "https://www.googleapis.com/auth/meetings.space.readonly" ]; const authUrl = this.oauth2Client.generateAuthUrl({ access_type: "offline", scope: scopes, prompt: "consent" }); return authUrl; } async authenticateInteractive() { if (this.authenticated) { logger.info("Already authenticated with Google"); return; } const authUrl = this.getAuthUrl(); logger.info(`Please visit this URL to authenticate: ${authUrl}`); const server = createServer(async (req, res) => { try { const url = new URL(req.url, `http://${req.headers.host}`); if (url.pathname === "/oauth2callback") { const code = url.searchParams.get("code"); if (code) { const { tokens } = await this.oauth2Client.getToken(code); this.oauth2Client.setCredentials(tokens); this.authenticated = true; logger.info("Successfully authenticated with Google"); logger.info(`Refresh token: ${tokens.refresh_token}`); logger.info("Save this refresh token in your .env file as GOOGLE_REFRESH_TOKEN to avoid re-authentication"); res.writeHead(200, { "Content-Type": "text/html" }); res.end("<h1>Authentication successful!</h1><p>You can close this window.</p>"); server.close(); } else { throw new Error("No authorization code received"); } } } catch (error) { logger.error("OAuth callback error:", error); res.writeHead(500, { "Content-Type": "text/html" }); res.end("<h1>Authentication failed!</h1><p>Check the logs for details.</p>"); server.close(); } }); const redirectUri = this.runtime.getSetting("GOOGLE_REDIRECT_URI") || "http://localhost:3000/oauth2callback"; const port = new URL(redirectUri).port || "3000"; server.listen(parseInt(port)); return new Promise((resolve) => { server.on("close", resolve); }); } async getAccessToken() { if (!this.authenticated) { throw new Error("Not authenticated. Please authenticate first."); } const tokens = await this.oauth2Client.getAccessToken(); if (!tokens.token) { throw new Error("Failed to get access token"); } return tokens.token; } getOAuth2Client() { return this.oauth2Client; } isAuthenticated() { return this.authenticated; } async stop() { logger.info("Stopping Google Auth Service"); } }; var googleMeetConfigSchema = z.object({ GOOGLE_CLIENT_ID: z.string().optional(), GOOGLE_CLIENT_SECRET: z.string().optional(), GOOGLE_REDIRECT_URI: z.string().default("http://localhost:3000/oauth2callback"), GOOGLE_REFRESH_TOKEN: z.string().optional(), GOOGLE_MEET_DEFAULT_DURATION_MINUTES: z.union([z.number(), z.string()]).transform( (val) => typeof val === "string" ? parseInt(val, 10) : val ).default(60), GOOGLE_MEET_DEFAULT_ACCESS_TYPE: z.enum(["OPEN", "TRUSTED", "RESTRICTED"]).default("OPEN") }); var MeetingStatus = /* @__PURE__ */ ((MeetingStatus2) => { MeetingStatus2["WAITING"] = "waiting"; MeetingStatus2["ACTIVE"] = "active"; MeetingStatus2["ENDED"] = "ended"; MeetingStatus2["ERROR"] = "error"; return MeetingStatus2; })(MeetingStatus || {}); // src/services/googleMeetAPIService.ts var GoogleMeetAPIService = class _GoogleMeetAPIService extends Service { static serviceType = "google-meet-api"; authService; meetClient; meetings = /* @__PURE__ */ new Map(); currentMeetingSpace = null; get capabilityDescription() { return "Google Meet API service for managing meetings, participants, and artifacts"; } constructor(runtime) { super(runtime); this.authService = runtime.getService("google-auth"); if (!this.authService) { throw new Error("GoogleAuthService not found. Make sure it's registered before GoogleMeetAPIService"); } this.meetClient = google.meet({ version: "v2", auth: this.authService.getOAuth2Client() }); } static async start(runtime) { logger.info("Starting Google Meet API Service"); const service = new _GoogleMeetAPIService(runtime); await service.initialize(); return service; } async initialize() { if (!this.authService.isAuthenticated()) { logger.warn("Google Auth Service not authenticated. Please authenticate before using Meet API."); } } async createMeeting(config) { try { const response = await this.meetClient.spaces.create({ requestBody: { config: { accessType: config?.accessType || "OPEN", entryPointAccess: "ALL" } } }); const space = response.data; if (!space.name || !space.meetingUri || !space.meetingCode) { throw new Error("Invalid meeting space response"); } const meeting = { id: space.name, meetingCode: space.meetingCode, meetingUri: space.meetingUri, status: "active" /* ACTIVE */, startTime: /* @__PURE__ */ new Date(), participants: [], transcripts: [] }; this.meetings.set(meeting.id, meeting); this.currentMeetingSpace = space; logger.info(`Created meeting: ${meeting.meetingUri}`); return meeting; } catch (error) { logger.error("Failed to create meeting:", error); throw error; } } async getMeetingSpace(spaceName) { try { const response = await this.meetClient.spaces.get({ name: spaceName }); return response.data; } catch (error) { logger.error(`Failed to get meeting space ${spaceName}:`, error); throw error; } } async getConference(conferenceName) { try { const response = await this.meetClient.conferenceRecords.get({ name: conferenceName }); return response.data; } catch (error) { logger.error(`Failed to get conference ${conferenceName}:`, error); throw error; } } async listParticipants(conferenceRecordName) { try { const response = await this.meetClient.conferenceRecords.participants.list({ parent: conferenceRecordName, pageSize: 100 }); const participants = []; if (response.data.participants) { for (const p of response.data.participants) { if (p.name) { let displayName = "Unknown"; if (p.signedinUser) { displayName = p.signedinUser.displayName || p.signedinUser.user || "Signed-in User"; } else if (p.anonymousUser) { displayName = p.anonymousUser.displayName || "Anonymous User"; } else if (p.phoneUser) { displayName = p.phoneUser.displayName || "Phone User"; } participants.push({ id: p.name, name: displayName, joinTime: p.earliestStartTime ? new Date(p.earliestStartTime) : /* @__PURE__ */ new Date(), leaveTime: p.latestEndTime ? new Date(p.latestEndTime) : void 0, isActive: !p.latestEndTime }); } } } return participants; } catch (error) { logger.error(`Failed to list participants for ${conferenceRecordName}:`, error); throw error; } } async getTranscript(transcriptName) { try { const response = await this.meetClient.conferenceRecords.transcripts.get({ name: transcriptName }); if (!response.data.name) { throw new Error("Invalid transcript response"); } const entriesResponse = await this.meetClient.conferenceRecords.transcripts.entries.list({ parent: response.data.name, pageSize: 1e3 }); let fullTranscript = ""; if (entriesResponse.data.transcriptEntries) { for (const entry of entriesResponse.data.transcriptEntries) { const speaker = entry.participant || "Unknown"; const text = entry.text || ""; fullTranscript += `${speaker}: ${text} `; } } return fullTranscript; } catch (error) { logger.error(`Failed to get transcript ${transcriptName}:`, error); throw error; } } async listRecordings(conferenceRecordName) { try { const response = await this.meetClient.conferenceRecords.recordings.list({ parent: conferenceRecordName }); return response.data.recordings || []; } catch (error) { logger.error(`Failed to list recordings for ${conferenceRecordName}:`, error); throw error; } } async getRecordingUrl(recordingName) { try { const response = await this.meetClient.conferenceRecords.recordings.get({ name: recordingName }); return response.data.driveDestination?.file || null; } catch (error) { logger.error(`Failed to get recording ${recordingName}:`, error); throw error; } } async endMeeting(spaceName) { try { await this.meetClient.spaces.endActiveConference({ name: spaceName }); const meeting = Array.from(this.meetings.values()).find((m) => m.id === spaceName); if (meeting) { meeting.status = "ended" /* ENDED */; meeting.endTime = /* @__PURE__ */ new Date(); } logger.info(`Ended meeting: ${spaceName}`); } catch (error) { logger.error(`Failed to end meeting ${spaceName}:`, error); throw error; } } getCurrentMeeting() { return this.currentMeetingSpace ? this.meetings.get(this.currentMeetingSpace.name) || null : null; } getMeeting(meetingId) { return this.meetings.get(meetingId) || null; } async stop() { logger.info("Stopping Google Meet API Service"); this.meetings.clear(); this.currentMeetingSpace = null; } }; var createMeetingAction = { name: "CREATE_MEETING", description: "Create a new Google Meet meeting space", similes: ["start a meeting", "create a meet", "new meeting", "setup a call"], examples: [ [ { name: "user", content: { text: "Create a new meeting" } }, { name: "assistant", content: { text: "I'll create a new Google Meet meeting for you.", action: "CREATE_MEETING" } } ], [ { name: "user", content: { text: "Start a team meeting with restricted access" } }, { name: "assistant", content: { text: "I'll create a restricted access meeting for your team.", action: "CREATE_MEETING" } } ] ], validate: async (runtime, message, state) => { const googleMeetService = runtime.getService("google-meet-api"); if (!googleMeetService) { logger.error("Google Meet API service not found"); return false; } return true; }, handler: async (runtime, message, state, params, callback) => { try { const googleMeetService = runtime.getService("google-meet-api"); if (!googleMeetService) { throw new Error("Google Meet API service not found"); } const meetingParams = params; let accessType = meetingParams?.accessType; if (!accessType && message.content.text) { const text = message.content.text.toLowerCase(); if (text.includes("restricted") || text.includes("private")) { accessType = "RESTRICTED"; } else if (text.includes("trusted") || text.includes("organization")) { accessType = "TRUSTED"; } else { accessType = "OPEN"; } } const meeting = await googleMeetService.createMeeting({ accessType }); const response = `\u2705 Meeting created successfully! \u{1F4C5} **Meeting Details:** - Meeting Link: ${meeting.meetingUri} - Meeting Code: ${meeting.meetingCode} - Access Type: ${accessType} - Status: Active You can share this link with participants to join the meeting.`; if (callback) { callback({ text: response, metadata: { meetingId: meeting.id, meetingUri: meeting.meetingUri, meetingCode: meeting.meetingCode } }); } } catch (error) { logger.error("Failed to create meeting:", error); if (callback) { callback({ text: `\u274C Failed to create meeting: ${error instanceof Error ? error.message : "Unknown error"}`, error: true }); } } } }; var getMeetingInfoAction = { name: "GET_MEETING_INFO", description: "Get information about a Google Meet meeting", similes: ["meeting info", "check meeting", "meeting status", "meeting details"], examples: [ [ { name: "user", content: { text: "What's the status of the current meeting?" } }, { name: "assistant", content: { text: "I'll check the current meeting status for you.", action: "GET_MEETING_INFO" } } ], [ { name: "user", content: { text: "Get information about meeting abc-defg-hij" } }, { name: "assistant", content: { text: "I'll retrieve the information for that meeting.", action: "GET_MEETING_INFO" } } ] ], validate: async (runtime, message, state) => { const googleMeetService = runtime.getService("google-meet-api"); if (!googleMeetService) { logger.error("Google Meet API service not found"); return false; } return true; }, handler: async (runtime, message, state, params, callback) => { try { const googleMeetService = runtime.getService("google-meet-api"); if (!googleMeetService) { throw new Error("Google Meet API service not found"); } const meetingParams = params; let meeting = googleMeetService.getCurrentMeeting(); if (!meeting && message.content.text) { const meetingCodeMatch = message.content.text.match(/[a-z]{3}-[a-z]{4}-[a-z]{3}/i); if (meetingCodeMatch) { throw new Error("Please provide the full meeting space ID (not just the meeting code) to retrieve meeting information"); } if (meetingParams?.meetingId) { meeting = googleMeetService.getMeeting(meetingParams.meetingId); } } if (!meeting) { throw new Error("No active meeting found. Please create a meeting first or provide a meeting ID."); } const response = `\u{1F4C5} **Meeting Information:** - Meeting Link: ${meeting.meetingUri} - Meeting Code: ${meeting.meetingCode} - Status: ${meeting.status} - Started: ${meeting.startTime.toLocaleString()} ${meeting.endTime ? `- Ended: ${meeting.endTime.toLocaleString()}` : "- Duration: Ongoing"} - Participants: ${meeting.participants.length} ${meeting.participants.length > 0 ? "\n**Active Participants:**\n" + meeting.participants.filter((p) => p.isActive).map((p) => ` \u2022 ${p.name} (joined ${p.joinTime.toLocaleTimeString()})`).join("\n") : ""}`; if (callback) { callback({ text: response, metadata: { meetingId: meeting.id, meetingUri: meeting.meetingUri, meetingCode: meeting.meetingCode, participantCount: meeting.participants.length } }); } } catch (error) { logger.error("Failed to get meeting info:", error); if (callback) { callback({ text: `\u274C Failed to get meeting info: ${error instanceof Error ? error.message : "Unknown error"}`, error: true }); } } } }; var getParticipantsAction = { name: "GET_PARTICIPANTS", description: "Get the list of participants in a Google Meet conference", similes: ["who's in the meeting", "list participants", "attendees", "who joined"], examples: [ [ { name: "user", content: { text: "Who's in the meeting?" } }, { name: "assistant", content: { text: "I'll check who's currently in the meeting.", action: "GET_PARTICIPANTS" } } ], [ { name: "user", content: { text: "List all participants" } }, { name: "assistant", content: { text: "Let me get the participant list for you.", action: "GET_PARTICIPANTS" } } ] ], validate: async (runtime, message, state) => { const googleMeetService = runtime.getService("google-meet-api"); if (!googleMeetService) { logger.error("Google Meet API service not found"); return false; } return true; }, handler: async (runtime, message, state, params, callback) => { try { const googleMeetService = runtime.getService("google-meet-api"); if (!googleMeetService) { throw new Error("Google Meet API service not found"); } const currentMeeting = googleMeetService.getCurrentMeeting(); if (!currentMeeting) { throw new Error("No active meeting found. Please create or join a meeting first."); } const response = `\u{1F465} **Meeting Participants:** ${currentMeeting.participants.length === 0 ? "No participants have joined yet." : currentMeeting.participants.map((p, index) => { const status = p.isActive ? "\u{1F7E2}" : "\u26AB"; const duration = p.leaveTime ? `(${Math.round((p.leaveTime.getTime() - p.joinTime.getTime()) / 1e3 / 60)} min)` : "(active)"; return `${index + 1}. ${status} ${p.name} - Joined at ${p.joinTime.toLocaleTimeString()} ${duration}`; }).join("\n")} **Total participants:** ${currentMeeting.participants.length} **Currently active:** ${currentMeeting.participants.filter((p) => p.isActive).length}`; if (callback) { callback({ text: response, metadata: { totalParticipants: currentMeeting.participants.length, activeParticipants: currentMeeting.participants.filter((p) => p.isActive).length, participants: currentMeeting.participants.map((p) => ({ name: p.name, isActive: p.isActive, joinTime: p.joinTime.toISOString() })) } }); } } catch (error) { logger.error("Failed to get participants:", error); if (callback) { callback({ text: `\u274C Failed to get participants: ${error instanceof Error ? error.message : "Unknown error"} Note: To get real-time participant data, ensure you have an active conference record name from a running meeting.`, error: true }); } } } }; var generateReportAction = { name: "GENERATE_REPORT", description: "Generate a comprehensive report from Google Meet artifacts (transcripts, recordings)", similes: ["create report", "meeting summary", "get transcript", "meeting notes"], examples: [ [ { name: "user", content: { text: "Generate a report for the meeting" } }, { name: "assistant", content: { text: "I'll generate a comprehensive report from the meeting artifacts.", action: "GENERATE_REPORT" } } ], [ { name: "user", content: { text: "Get the meeting transcript and summary" } }, { name: "assistant", content: { text: "I'll retrieve the transcript and create a summary for you.", action: "GENERATE_REPORT" } } ] ], validate: async (runtime, message, state) => { const googleMeetService = runtime.getService("google-meet-api"); if (!googleMeetService) { logger.error("Google Meet API service not found"); return false; } return true; }, handler: async (runtime, message, state, params, callback) => { try { const googleMeetService = runtime.getService("google-meet-api"); if (!googleMeetService) { throw new Error("Google Meet API service not found"); } const reportParams = params; let meetingId = reportParams?.meetingId; const currentMeeting = googleMeetService.getCurrentMeeting(); if (!meetingId && currentMeeting) { meetingId = currentMeeting.id; } if (!meetingId) { throw new Error("No meeting specified. Please provide a meeting ID or ensure there's an active meeting."); } const report = { meetingId, title: `Meeting Report - ${(/* @__PURE__ */ new Date()).toLocaleDateString()}`, date: /* @__PURE__ */ new Date(), duration: 0, // Would calculate from conference record participants: [], // Would get from participants API summary: "Meeting summary would be generated from transcript data", keyPoints: [ "Key points would be extracted from transcript", "Using natural language processing" ], actionItems: reportParams?.includeActionItems ? [ { description: "Action items would be extracted from transcript", priority: "medium" } ] : [], fullTranscript: reportParams?.includeTranscript ? [] : [] }; let reportContent = `# Meeting Report **Meeting ID:** ${report.meetingId} **Date:** ${report.date.toLocaleDateString()} **Duration:** ${report.duration} minutes ## Summary ${report.summary} ## Key Points ${report.keyPoints.map((point) => `- ${point}`).join("\n")} `; if (report.actionItems.length > 0) { reportContent += ` ## Action Items ${report.actionItems.map((item) => `- ${item.description} (Priority: ${item.priority})`).join("\n")} `; } const outputDir = runtime.getSetting("REPORT_OUTPUT_DIR") || "./meeting-reports"; try { await fs.mkdir(outputDir, { recursive: true }); const filename = `meeting-report-${Date.now()}.md`; const filepath = path.join(outputDir, filename); await fs.writeFile(filepath, reportContent); reportContent += ` \u{1F4C4} Report saved to: ${filepath}`; } catch (error) { logger.warn("Failed to save report to file:", error); } const response = `\u2705 Meeting report generated successfully! ${reportContent} Note: To get actual transcript and recording data, ensure the meeting has ended and artifacts are available through the Google Meet API.`; if (callback) { callback({ text: response, metadata: { report, savedToFile: true } }); } } catch (error) { logger.error("Failed to generate report:", error); if (callback) { callback({ text: `\u274C Failed to generate report: ${error instanceof Error ? error.message : "Unknown error"}`, error: true }); } } } }; var authenticateAction = { name: "AUTHENTICATE_GOOGLE", description: "Authenticate with Google to access Meet API", similes: ["login to google", "google auth", "sign in", "authenticate"], examples: [ [ { name: "user", content: { text: "Authenticate with Google" } }, { name: "assistant", content: { text: "I'll help you authenticate with Google Meet API.", action: "AUTHENTICATE_GOOGLE" } } ] ], validate: async (runtime, message, state) => { const authService = runtime.getService("google-auth"); if (!authService) { logger.error("Google Auth service not found"); return false; } return true; }, handler: async (runtime, message, state, params, callback) => { try { const authService = runtime.getService("google-auth"); if (!authService) { throw new Error("Google Auth service not found"); } const authParams = params; if (authService.isAuthenticated()) { if (callback) { callback({ text: "\u2705 Already authenticated with Google Meet API. You can now create meetings, get participant info, and access meeting artifacts." }); } return; } const interactive = authParams?.interactive !== false; if (interactive) { const authUrl = authService.getAuthUrl(); if (callback) { callback({ text: `\u{1F510} To authenticate with Google Meet API: 1. Visit this URL: ${authUrl} 2. Sign in with your Google account 3. Grant the requested permissions 4. You'll be redirected back to complete authentication Starting authentication server...`, metadata: { authUrl, interactive: true } }); } await authService.authenticateInteractive(); if (callback) { callback({ text: `\u2705 Successfully authenticated with Google Meet API! You can now: - Create new meetings - Get meeting information - List participants - Generate reports from meeting artifacts \u{1F4A1} Tip: Save the refresh token shown in the logs to your .env file to avoid re-authentication.` }); } } else { if (callback) { callback({ text: `\u274C Authentication required. Please set up Google OAuth credentials: 1. Set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET in your .env file 2. Optionally set GOOGLE_REFRESH_TOKEN if you have one 3. Run this command again to authenticate interactively` }); } } } catch (error) { logger.error("Failed to authenticate:", error); if (callback) { callback({ text: `\u274C Failed to authenticate: ${error instanceof Error ? error.message : "Unknown error"}`, error: true }); } } } }; var meetingProvider = { name: "GOOGLE_MEET_PROVIDER", description: "Provides current Google Meet meeting information and status", get: async (runtime, message, state) => { try { const googleMeetService = runtime.getService("google-meet-api"); if (!googleMeetService) { return { text: "Google Meet API service not available" }; } const currentMeeting = googleMeetService.getCurrentMeeting(); if (!currentMeeting) { return { text: "No active Google Meet meeting" }; } const activeParticipants = currentMeeting.participants.filter((p) => p.isActive).length; const duration = currentMeeting.endTime ? Math.round((currentMeeting.endTime.getTime() - currentMeeting.startTime.getTime()) / 1e3 / 60) : Math.round((Date.now() - currentMeeting.startTime.getTime()) / 1e3 / 60); const text = `Current Google Meet: - Meeting Link: ${currentMeeting.meetingUri} - Meeting Code: ${currentMeeting.meetingCode} - Status: ${currentMeeting.status} - Duration: ${duration} minutes - Active Participants: ${activeParticipants} - Total Participants: ${currentMeeting.participants.length}`; return { text }; } catch (error) { logger.error("Error in meeting provider:", error); return { text: "Error retrieving meeting information" }; } } }; // src/index.ts var googleMeetPlugin = { name: "plugin-google-meet-cute", description: "Google Meet integration plugin for ElizaOS - manage meetings, get participant info, and access meeting artifacts via Google Meet REST API", services: [GoogleAuthService, GoogleMeetAPIService], actions: [ authenticateAction, createMeetingAction, getMeetingInfoAction, getParticipantsAction, generateReportAction ], providers: [meetingProvider], config: { GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET, GOOGLE_REDIRECT_URI: process.env.GOOGLE_REDIRECT_URI, GOOGLE_REFRESH_TOKEN: process.env.GOOGLE_REFRESH_TOKEN, GOOGLE_MEET_DEFAULT_ACCESS_TYPE: process.env.GOOGLE_MEET_DEFAULT_ACCESS_TYPE, REPORT_OUTPUT_DIR: process.env.REPORT_OUTPUT_DIR }, async init(config, runtime) { logger.info("Initializing Google Meet plugin..."); try { const validatedConfig = { GOOGLE_CLIENT_ID: runtime?.getSetting("GOOGLE_CLIENT_ID") || config.GOOGLE_CLIENT_ID || process.env.GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET: runtime?.getSetting("GOOGLE_CLIENT_SECRET") || config.GOOGLE_CLIENT_SECRET || process.env.GOOGLE_CLIENT_SECRET, GOOGLE_REDIRECT_URI: runtime?.getSetting("GOOGLE_REDIRECT_URI") || config.GOOGLE_REDIRECT_URI || process.env.GOOGLE_REDIRECT_URI || "http://localhost:3000/oauth2callback", GOOGLE_REFRESH_TOKEN: runtime?.getSetting("GOOGLE_REFRESH_TOKEN") || config.GOOGLE_REFRESH_TOKEN || process.env.GOOGLE_REFRESH_TOKEN, GOOGLE_MEET_DEFAULT_ACCESS_TYPE: runtime?.getSetting("GOOGLE_MEET_DEFAULT_ACCESS_TYPE") || config.GOOGLE_MEET_DEFAULT_ACCESS_TYPE || process.env.GOOGLE_MEET_DEFAULT_ACCESS_TYPE || "OPEN", REPORT_OUTPUT_DIR: runtime?.getSetting("REPORT_OUTPUT_DIR") || config.REPORT_OUTPUT_DIR || process.env.REPORT_OUTPUT_DIR || "./meeting-reports" }; await googleMeetConfigSchema.parseAsync(validatedConfig); logger.info("Google Meet plugin configuration validated successfully"); if (runtime) { runtime.character.settings = runtime.character.settings || {}; runtime.character.settings.googleMeetConfig = validatedConfig; } if (!validatedConfig.GOOGLE_CLIENT_ID || !validatedConfig.GOOGLE_CLIENT_SECRET) { logger.warn("Google OAuth credentials not configured. Please set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET to use the Google Meet API."); } } catch (error) { logger.error( "Google Meet plugin configuration validation failed:", error ); throw new Error( `Invalid Google Meet plugin configuration: ${error instanceof Error ? error.message : String(error)}` ); } }, dependencies: [] }; var index_default = googleMeetPlugin; export { GoogleAuthService, GoogleMeetAPIService, MeetingStatus, index_default as default, googleMeetConfigSchema, googleMeetPlugin }; //# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map