mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-07-06 20:42:15 +02:00
sync immediately on account connect
This commit is contained in:
parent
752c537892
commit
dc21596e8e
6 changed files with 126 additions and 12 deletions
|
|
@ -18,6 +18,7 @@ import z from 'zod';
|
||||||
import { RunEvent } from 'packages/shared/dist/runs.js';
|
import { RunEvent } from 'packages/shared/dist/runs.js';
|
||||||
import container from '@x/core/dist/di/container.js';
|
import container from '@x/core/dist/di/container.js';
|
||||||
import { IGranolaConfigRepo } from '@x/core/dist/knowledge/granola/repo.js';
|
import { IGranolaConfigRepo } from '@x/core/dist/knowledge/granola/repo.js';
|
||||||
|
import { triggerSync as triggerGranolaSync } from '@x/core/dist/knowledge/granola/sync.js';
|
||||||
|
|
||||||
type InvokeChannels = ipc.InvokeChannels;
|
type InvokeChannels = ipc.InvokeChannels;
|
||||||
type IPCChannels = ipc.IPCChannels;
|
type IPCChannels = ipc.IPCChannels;
|
||||||
|
|
@ -316,6 +317,12 @@ export function setupIpcHandlers() {
|
||||||
'granola:setConfig': async (_event, args) => {
|
'granola:setConfig': async (_event, args) => {
|
||||||
const repo = container.resolve<IGranolaConfigRepo>('granolaConfigRepo');
|
const repo = container.resolve<IGranolaConfigRepo>('granolaConfigRepo');
|
||||||
await repo.setConfig({ enabled: args.enabled });
|
await repo.setConfig({ enabled: args.enabled });
|
||||||
|
|
||||||
|
// Trigger sync immediately when enabled
|
||||||
|
if (args.enabled) {
|
||||||
|
triggerGranolaSync();
|
||||||
|
}
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,9 @@ import { getProviderConfig, getAvailableProviders } from '@x/core/dist/auth/prov
|
||||||
import container from '@x/core/dist/di/container.js';
|
import container from '@x/core/dist/di/container.js';
|
||||||
import { IOAuthRepo } from '@x/core/dist/auth/repo.js';
|
import { IOAuthRepo } from '@x/core/dist/auth/repo.js';
|
||||||
import { IClientRegistrationRepo } from '@x/core/dist/auth/client-repo.js';
|
import { IClientRegistrationRepo } from '@x/core/dist/auth/client-repo.js';
|
||||||
|
import { triggerSync as triggerGmailSync } from '@x/core/dist/knowledge/sync_gmail.js';
|
||||||
|
import { triggerSync as triggerCalendarSync } from '@x/core/dist/knowledge/sync_calendar.js';
|
||||||
|
import { triggerSync as triggerFirefliesSync } from '@x/core/dist/knowledge/sync_fireflies.js';
|
||||||
|
|
||||||
const REDIRECT_URI = 'http://localhost:8080/oauth/callback';
|
const REDIRECT_URI = 'http://localhost:8080/oauth/callback';
|
||||||
|
|
||||||
|
|
@ -138,6 +141,14 @@ export async function connectProvider(provider: string): Promise<{ success: bool
|
||||||
// Save tokens
|
// Save tokens
|
||||||
console.log(`[OAuth] Token exchange successful for ${provider}`);
|
console.log(`[OAuth] Token exchange successful for ${provider}`);
|
||||||
await oauthRepo.saveTokens(provider, tokens);
|
await oauthRepo.saveTokens(provider, tokens);
|
||||||
|
|
||||||
|
// Trigger immediate sync for relevant providers
|
||||||
|
if (provider === 'google') {
|
||||||
|
triggerGmailSync();
|
||||||
|
triggerCalendarSync();
|
||||||
|
} else if (provider === 'fireflies-ai') {
|
||||||
|
triggerFirefliesSync();
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('OAuth token exchange failed:', error);
|
console.error('OAuth token exchange failed:', error);
|
||||||
throw error;
|
throw error;
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,30 @@ const RATE_LIMIT_RETRY_DELAY_MS = 60 * 1000; // Wait 1 minute on rate limit
|
||||||
const MAX_RETRIES = 3; // Maximum retries for rate-limited requests
|
const MAX_RETRIES = 3; // Maximum retries for rate-limited requests
|
||||||
const MAX_BATCH_SIZE = 10; // Process max 10 documents per folder per sync
|
const MAX_BATCH_SIZE = 10; // Process max 10 documents per folder per sync
|
||||||
|
|
||||||
|
// --- Wake Signal for Immediate Sync Trigger ---
|
||||||
|
let wakeResolve: (() => void) | null = null;
|
||||||
|
|
||||||
|
export function triggerSync(): void {
|
||||||
|
if (wakeResolve) {
|
||||||
|
console.log('[Granola] Triggered - waking up immediately');
|
||||||
|
wakeResolve();
|
||||||
|
wakeResolve = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function interruptibleSleep(ms: number): Promise<void> {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
wakeResolve = null;
|
||||||
|
resolve();
|
||||||
|
}, ms);
|
||||||
|
wakeResolve = () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// --- Token Extraction ---
|
// --- Token Extraction ---
|
||||||
|
|
||||||
interface WorkosTokens {
|
interface WorkosTokens {
|
||||||
|
|
@ -404,7 +428,7 @@ async function syncNotes(): Promise<void> {
|
||||||
|
|
||||||
export async function init(): Promise<void> {
|
export async function init(): Promise<void> {
|
||||||
console.log('[Granola] Starting Granola Sync...');
|
console.log('[Granola] Starting Granola Sync...');
|
||||||
console.log(`[Granola] Will check every ${SYNC_INTERVAL_MS / 60000} minutes.`);
|
console.log(`[Granola] Will sync every ${SYNC_INTERVAL_MS / 60000} minutes.`);
|
||||||
console.log(`[Granola] Notes will be saved to: ${SYNC_DIR}`);
|
console.log(`[Granola] Notes will be saved to: ${SYNC_DIR}`);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
@ -414,9 +438,9 @@ export async function init(): Promise<void> {
|
||||||
console.error('[Granola] Error in sync loop:', error);
|
console.error('[Granola] Error in sync loop:', error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sleep before next check
|
// Sleep before next check (can be interrupted by triggerSync)
|
||||||
console.log(`[Granola] Sleeping for ${SYNC_INTERVAL_MS / 60000} minutes...`);
|
console.log(`[Granola] Sleeping for ${SYNC_INTERVAL_MS / 60000} minutes...`);
|
||||||
await new Promise(resolve => setTimeout(resolve, SYNC_INTERVAL_MS));
|
await interruptibleSleep(SYNC_INTERVAL_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,30 @@ const REQUIRED_SCOPES = [
|
||||||
|
|
||||||
const nhm = new NodeHtmlMarkdown();
|
const nhm = new NodeHtmlMarkdown();
|
||||||
|
|
||||||
|
// --- Wake Signal for Immediate Sync Trigger ---
|
||||||
|
let wakeResolve: (() => void) | null = null;
|
||||||
|
|
||||||
|
export function triggerSync(): void {
|
||||||
|
if (wakeResolve) {
|
||||||
|
console.log('[Calendar] Triggered - waking up immediately');
|
||||||
|
wakeResolve();
|
||||||
|
wakeResolve = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function interruptibleSleep(ms: number): Promise<void> {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
wakeResolve = null;
|
||||||
|
resolve();
|
||||||
|
}, ms);
|
||||||
|
wakeResolve = () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// --- Helper Functions ---
|
// --- Helper Functions ---
|
||||||
|
|
||||||
function cleanFilename(name: string): string {
|
function cleanFilename(name: string): string {
|
||||||
|
|
@ -211,7 +235,7 @@ async function performSync(syncDir: string, lookbackDays: number) {
|
||||||
|
|
||||||
export async function init() {
|
export async function init() {
|
||||||
console.log("Starting Google Calendar & Notes Sync (TS)...");
|
console.log("Starting Google Calendar & Notes Sync (TS)...");
|
||||||
console.log(`Will check for credentials every ${SYNC_INTERVAL_MS / 1000} seconds.`);
|
console.log(`Will sync every ${SYNC_INTERVAL_MS / 1000} seconds.`);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -228,8 +252,8 @@ export async function init() {
|
||||||
console.error("Error in main loop:", error);
|
console.error("Error in main loop:", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sleep for N minutes before next check
|
// Sleep for N minutes before next check (can be interrupted by triggerSync)
|
||||||
console.log(`Sleeping for ${SYNC_INTERVAL_MS / 1000} seconds...`);
|
console.log(`Sleeping for ${SYNC_INTERVAL_MS / 1000} seconds...`);
|
||||||
await new Promise(resolve => setTimeout(resolve, SYNC_INTERVAL_MS));
|
await interruptibleSleep(SYNC_INTERVAL_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -12,6 +12,30 @@ const API_DELAY_MS = 2000; // 2 second delay between API calls
|
||||||
const RATE_LIMIT_RETRY_DELAY_MS = 60 * 1000; // Wait 1 minute on rate limit
|
const RATE_LIMIT_RETRY_DELAY_MS = 60 * 1000; // Wait 1 minute on rate limit
|
||||||
const MAX_RETRIES = 3; // Maximum retries for rate-limited requests
|
const MAX_RETRIES = 3; // Maximum retries for rate-limited requests
|
||||||
|
|
||||||
|
// --- Wake Signal for Immediate Sync Trigger ---
|
||||||
|
let wakeResolve: (() => void) | null = null;
|
||||||
|
|
||||||
|
export function triggerSync(): void {
|
||||||
|
if (wakeResolve) {
|
||||||
|
console.log('[Fireflies] Triggered - waking up immediately');
|
||||||
|
wakeResolve();
|
||||||
|
wakeResolve = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function interruptibleSleep(ms: number): Promise<void> {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
wakeResolve = null;
|
||||||
|
resolve();
|
||||||
|
}, ms);
|
||||||
|
wakeResolve = () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// --- Types for Fireflies API responses ---
|
// --- Types for Fireflies API responses ---
|
||||||
|
|
||||||
interface FirefliesMeeting {
|
interface FirefliesMeeting {
|
||||||
|
|
@ -553,7 +577,7 @@ async function syncMeetings() {
|
||||||
*/
|
*/
|
||||||
export async function init() {
|
export async function init() {
|
||||||
console.log('[Fireflies] Starting Fireflies Sync...');
|
console.log('[Fireflies] Starting Fireflies Sync...');
|
||||||
console.log(`[Fireflies] Will check for credentials every ${SYNC_INTERVAL_MS / 1000} seconds.`);
|
console.log(`[Fireflies] Will sync every ${SYNC_INTERVAL_MS / 1000} seconds.`);
|
||||||
console.log(`[Fireflies] Syncing transcripts from the last ${LOOKBACK_DAYS} days.`);
|
console.log(`[Fireflies] Syncing transcripts from the last ${LOOKBACK_DAYS} days.`);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
@ -571,9 +595,9 @@ export async function init() {
|
||||||
console.error('[Fireflies] Error in main loop:', error);
|
console.error('[Fireflies] Error in main loop:', error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sleep before next check
|
// Sleep before next check (can be interrupted by triggerSync)
|
||||||
console.log(`[Fireflies] Sleeping for ${SYNC_INTERVAL_MS / 1000} seconds...`);
|
console.log(`[Fireflies] Sleeping for ${SYNC_INTERVAL_MS / 1000} seconds...`);
|
||||||
await new Promise(resolve => setTimeout(resolve, SYNC_INTERVAL_MS));
|
await interruptibleSleep(SYNC_INTERVAL_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,30 @@ const REQUIRED_SCOPE = 'https://www.googleapis.com/auth/gmail.readonly';
|
||||||
|
|
||||||
const nhm = new NodeHtmlMarkdown();
|
const nhm = new NodeHtmlMarkdown();
|
||||||
|
|
||||||
|
// --- Wake Signal for Immediate Sync Trigger ---
|
||||||
|
let wakeResolve: (() => void) | null = null;
|
||||||
|
|
||||||
|
export function triggerSync(): void {
|
||||||
|
if (wakeResolve) {
|
||||||
|
console.log('[Gmail] Triggered - waking up immediately');
|
||||||
|
wakeResolve();
|
||||||
|
wakeResolve = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function interruptibleSleep(ms: number): Promise<void> {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
wakeResolve = null;
|
||||||
|
resolve();
|
||||||
|
}, ms);
|
||||||
|
wakeResolve = () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// --- Helper Functions ---
|
// --- Helper Functions ---
|
||||||
|
|
||||||
function cleanFilename(name: string): string {
|
function cleanFilename(name: string): string {
|
||||||
|
|
@ -287,7 +311,7 @@ async function performSync() {
|
||||||
|
|
||||||
export async function init() {
|
export async function init() {
|
||||||
console.log("Starting Gmail Sync (TS)...");
|
console.log("Starting Gmail Sync (TS)...");
|
||||||
console.log(`Will check for credentials every ${SYNC_INTERVAL_MS / 1000} seconds.`);
|
console.log(`Will sync every ${SYNC_INTERVAL_MS / 1000} seconds.`);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -304,8 +328,8 @@ export async function init() {
|
||||||
console.error("Error in main loop:", error);
|
console.error("Error in main loop:", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sleep for N minutes before next check
|
// Sleep for N minutes before next check (can be interrupted by triggerSync)
|
||||||
console.log(`Sleeping for ${SYNC_INTERVAL_MS / 1000} seconds...`);
|
console.log(`Sleeping for ${SYNC_INTERVAL_MS / 1000} seconds...`);
|
||||||
await new Promise(resolve => setTimeout(resolve, SYNC_INTERVAL_MS));
|
await interruptibleSleep(SYNC_INTERVAL_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue