@knowsuchagency/fulcrum 3.2.1 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/server/index.js
CHANGED
|
@@ -1147055,8 +1147055,10 @@ You have access to Fulcrum's MCP tools. Use them proactively to help users.
|
|
|
1147055
1147055
|
- Use \`search\` with \`memoryTags: ["actionable"]\` to review tracked items
|
|
1147056
1147056
|
|
|
1147057
1147057
|
**Unified Search:**
|
|
1147058
|
-
- \`search\` - Cross-entity FTS5 full-text search across tasks, projects, messages, events, memories, and
|
|
1147059
|
-
- Filter by entity type: \`entities: ["tasks", "projects", "messages", "events", "memories", "conversations"]\`
|
|
1147058
|
+
- \`search\` - Cross-entity FTS5 full-text search across tasks, projects, messages, events, memories, conversations, and gmail
|
|
1147059
|
+
- Filter by entity type: \`entities: ["tasks", "projects", "messages", "events", "memories", "conversations", "gmail"]\`
|
|
1147060
|
+
- **Gmail is opt-in** \u2014 not included in default searches to avoid latency/rate-limit impact. Must be explicitly requested via \`entities: ["gmail"]\`
|
|
1147061
|
+
- Gmail-specific filters: \`gmailFrom\`, \`gmailTo\`, \`gmailAfter\`, \`gmailBefore\`
|
|
1147060
1147062
|
- Entity-specific filters: \`taskStatus\`, \`projectStatus\`, \`messageChannel\`, \`messageDirection\`, \`eventFrom\`, \`eventTo\`, \`memoryTags\`, \`conversationRole\`, \`conversationProvider\`, \`conversationProjectId\`
|
|
1147061
1147063
|
- Conversations search indexes AI assistant chat messages (excludes system prompts) with session context
|
|
1147062
1147064
|
- Results sorted by relevance score with BM25 ranking
|
|
@@ -1147398,7 +1147400,7 @@ Fulcrum is your digital concierge - a personal command center where you track ev
|
|
|
1147398
1147400
|
- list_projects, create_project
|
|
1147399
1147401
|
- execute_command (run any CLI command)
|
|
1147400
1147402
|
- send_notification
|
|
1147401
|
-
- search (unified FTS5 search across tasks, projects, messages, events, memories, conversations)
|
|
1147403
|
+
- search (unified FTS5 search across tasks, projects, messages, events, memories, conversations; gmail is opt-in via \`entities: ["gmail"]\`)
|
|
1147402
1147404
|
- memory_file_read, memory_file_update (master memory file - always in prompt)
|
|
1147403
1147405
|
- memory_store (ephemeral knowledge snippets with tags)
|
|
1147404
1147406
|
- message (send to WhatsApp/Discord/Telegram/Slack/Gmail - user-only, concierge mode)
|
|
@@ -1147597,7 +1147599,7 @@ function addMessage(sessionId, message) {
|
|
|
1147597
1147599
|
return db2.select().from(chatMessages).where(eq(chatMessages.id, id)).get();
|
|
1147598
1147600
|
}
|
|
1147599
1147601
|
function getMessages(sessionId) {
|
|
1147600
|
-
return db2.select().from(chatMessages).where(eq(chatMessages.sessionId, sessionId)).orderBy(chatMessages.createdAt).all();
|
|
1147602
|
+
return db2.select().from(chatMessages).where(eq(chatMessages.sessionId, sessionId)).orderBy(chatMessages.createdAt, sql`rowid`).all();
|
|
1147601
1147603
|
}
|
|
1147602
1147604
|
function buildBaselinePrompt(condensed = false) {
|
|
1147603
1147605
|
const settings = getSettings();
|
|
@@ -1188385,13 +1188387,13 @@ var require_core = __commonJS((exports) => {
|
|
|
1188385
1188387
|
return metaOpts;
|
|
1188386
1188388
|
}
|
|
1188387
1188389
|
var noLogs = { log() {}, warn() {}, error() {} };
|
|
1188388
|
-
function getLogger(
|
|
1188389
|
-
if (
|
|
1188390
|
+
function getLogger(logger7) {
|
|
1188391
|
+
if (logger7 === false)
|
|
1188390
1188392
|
return noLogs;
|
|
1188391
|
-
if (
|
|
1188393
|
+
if (logger7 === undefined)
|
|
1188392
1188394
|
return console;
|
|
1188393
|
-
if (
|
|
1188394
|
-
return
|
|
1188395
|
+
if (logger7.log && logger7.warn && logger7.error)
|
|
1188396
|
+
return logger7;
|
|
1188395
1188397
|
throw new Error("logger must implement log, warn and error methods");
|
|
1188396
1188398
|
}
|
|
1188397
1188399
|
var KEYWORD_NAME = /^[a-z_$][a-z0-9_$:-]*$/i;
|
|
@@ -1194018,13 +1194020,13 @@ var require_core3 = __commonJS((exports) => {
|
|
|
1194018
1194020
|
return metaOpts;
|
|
1194019
1194021
|
}
|
|
1194020
1194022
|
var noLogs = { log() {}, warn() {}, error() {} };
|
|
1194021
|
-
function getLogger(
|
|
1194022
|
-
if (
|
|
1194023
|
+
function getLogger(logger7) {
|
|
1194024
|
+
if (logger7 === false)
|
|
1194023
1194025
|
return noLogs;
|
|
1194024
|
-
if (
|
|
1194026
|
+
if (logger7 === undefined)
|
|
1194025
1194027
|
return console;
|
|
1194026
|
-
if (
|
|
1194027
|
-
return
|
|
1194028
|
+
if (logger7.log && logger7.warn && logger7.error)
|
|
1194029
|
+
return logger7;
|
|
1194028
1194030
|
throw new Error("logger must implement log, warn and error methods");
|
|
1194029
1194031
|
}
|
|
1194030
1194032
|
var KEYWORD_NAME = /^[a-z_$][a-z0-9_$:-]*$/i;
|
|
@@ -1200416,10 +1200418,10 @@ class CaldavAccountManager {
|
|
|
1200416
1200418
|
}
|
|
1200417
1200419
|
async startAll() {
|
|
1200418
1200420
|
const accounts = db2.select().from(caldavAccounts).where(eq(caldavAccounts.enabled, true)).all();
|
|
1200419
|
-
|
|
1200421
|
+
logger7.info("Starting all CalDAV accounts", { count: accounts.length });
|
|
1200420
1200422
|
for (const account2 of accounts) {
|
|
1200421
1200423
|
await this.startAccount(account2.id).catch((err) => {
|
|
1200422
|
-
|
|
1200424
|
+
logger7.error("Failed to start account", {
|
|
1200423
1200425
|
accountId: account2.id,
|
|
1200424
1200426
|
name: account2.name,
|
|
1200425
1200427
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -1200432,7 +1200434,7 @@ class CaldavAccountManager {
|
|
|
1200432
1200434
|
this.stopAccount(accountId);
|
|
1200433
1200435
|
}
|
|
1200434
1200436
|
this.connections.clear();
|
|
1200435
|
-
|
|
1200437
|
+
logger7.info("All CalDAV accounts stopped");
|
|
1200436
1200438
|
}
|
|
1200437
1200439
|
async startAccount(accountId) {
|
|
1200438
1200440
|
const account2 = db2.select().from(caldavAccounts).where(eq(caldavAccounts.id, accountId)).get();
|
|
@@ -1200440,7 +1200442,7 @@ class CaldavAccountManager {
|
|
|
1200440
1200442
|
throw new Error(`Account not found: ${accountId}`);
|
|
1200441
1200443
|
}
|
|
1200442
1200444
|
if (!account2.enabled) {
|
|
1200443
|
-
|
|
1200445
|
+
logger7.info("Account is disabled, skipping", { accountId, name: account2.name });
|
|
1200444
1200446
|
return;
|
|
1200445
1200447
|
}
|
|
1200446
1200448
|
this.stopAccount(accountId);
|
|
@@ -1200456,11 +1200458,11 @@ class CaldavAccountManager {
|
|
|
1200456
1200458
|
try {
|
|
1200457
1200459
|
conn.client = await this.connect(account2);
|
|
1200458
1200460
|
this.scheduleSync(accountId, account2.syncIntervalMinutes ?? 15);
|
|
1200459
|
-
|
|
1200461
|
+
logger7.info("Started CalDAV account", { accountId, name: account2.name });
|
|
1200460
1200462
|
} catch (err) {
|
|
1200461
1200463
|
conn.lastSyncError = err instanceof Error ? err.message : String(err);
|
|
1200462
1200464
|
this.updateAccountError(accountId, conn.lastSyncError);
|
|
1200463
|
-
|
|
1200465
|
+
logger7.error("Failed to connect account", { accountId, error: conn.lastSyncError });
|
|
1200464
1200466
|
this.scheduleRetry(accountId);
|
|
1200465
1200467
|
}
|
|
1200466
1200468
|
}
|
|
@@ -1200502,7 +1200504,7 @@ class CaldavAccountManager {
|
|
|
1200502
1200504
|
for (const [accountId, conn] of this.connections) {
|
|
1200503
1200505
|
if (conn.client && !conn.isSyncing) {
|
|
1200504
1200506
|
promises.push(this.syncAccount(accountId).catch((err) => {
|
|
1200505
|
-
|
|
1200507
|
+
logger7.error("Sync failed for account", {
|
|
1200506
1200508
|
accountId,
|
|
1200507
1200509
|
error: err instanceof Error ? err.message : String(err)
|
|
1200508
1200510
|
});
|
|
@@ -1200562,7 +1200564,7 @@ class CaldavAccountManager {
|
|
|
1200562
1200564
|
defaultAccountType: "caldav"
|
|
1200563
1200565
|
});
|
|
1200564
1200566
|
await client4.login();
|
|
1200565
|
-
|
|
1200567
|
+
logger7.info("Connected to CalDAV server (Basic)", { accountId: account2.id, serverUrl: account2.serverUrl });
|
|
1200566
1200568
|
return client4;
|
|
1200567
1200569
|
}
|
|
1200568
1200570
|
async connectOAuth(account2) {
|
|
@@ -1200597,9 +1200599,9 @@ class CaldavAccountManager {
|
|
|
1200597
1200599
|
credentials.expiration = newTokens.expiration;
|
|
1200598
1200600
|
try {
|
|
1200599
1200601
|
db2.update(caldavAccounts).set({ oauthTokens: newTokens, updatedAt: new Date().toISOString() }).where(eq(caldavAccounts.id, accountId)).run();
|
|
1200600
|
-
|
|
1200602
|
+
logger7.info("Persisted refreshed OAuth tokens", { accountId });
|
|
1200601
1200603
|
} catch (err) {
|
|
1200602
|
-
|
|
1200604
|
+
logger7.error("Failed to persist refreshed OAuth tokens", {
|
|
1200603
1200605
|
accountId,
|
|
1200604
1200606
|
error: err instanceof Error ? err.message : String(err)
|
|
1200605
1200607
|
});
|
|
@@ -1200610,7 +1200612,7 @@ class CaldavAccountManager {
|
|
|
1200610
1200612
|
defaultAccountType: "caldav"
|
|
1200611
1200613
|
});
|
|
1200612
1200614
|
await client4.login();
|
|
1200613
|
-
|
|
1200615
|
+
logger7.info("Connected to CalDAV server (Google OAuth)", { accountId: account2.id, serverUrl: account2.serverUrl });
|
|
1200614
1200616
|
return client4;
|
|
1200615
1200617
|
}
|
|
1200616
1200618
|
scheduleSync(accountId, intervalMinutes) {
|
|
@@ -1200622,7 +1200624,7 @@ class CaldavAccountManager {
|
|
|
1200622
1200624
|
}
|
|
1200623
1200625
|
conn.syncInterval = setInterval(() => {
|
|
1200624
1200626
|
this.syncAccount(accountId).catch((err) => {
|
|
1200625
|
-
|
|
1200627
|
+
logger7.error("Periodic sync failed", {
|
|
1200626
1200628
|
accountId,
|
|
1200627
1200629
|
error: err instanceof Error ? err.message : String(err)
|
|
1200628
1200630
|
});
|
|
@@ -1200635,7 +1200637,7 @@ class CaldavAccountManager {
|
|
|
1200635
1200637
|
return;
|
|
1200636
1200638
|
conn.retryCount++;
|
|
1200637
1200639
|
const delay2 = Math.min(1000 * Math.pow(2, conn.retryCount), MAX_RETRY_DELAY);
|
|
1200638
|
-
|
|
1200640
|
+
logger7.info("Scheduling account retry", { accountId, retryCount: conn.retryCount, delayMs: delay2 });
|
|
1200639
1200641
|
setTimeout(async () => {
|
|
1200640
1200642
|
const account2 = db2.select().from(caldavAccounts).where(eq(caldavAccounts.id, accountId)).get();
|
|
1200641
1200643
|
if (!account2 || !account2.enabled)
|
|
@@ -1200651,7 +1200653,7 @@ class CaldavAccountManager {
|
|
|
1200651
1200653
|
} catch (err) {
|
|
1200652
1200654
|
conn.lastSyncError = err instanceof Error ? err.message : String(err);
|
|
1200653
1200655
|
this.updateAccountError(accountId, conn.lastSyncError);
|
|
1200654
|
-
|
|
1200656
|
+
logger7.error("Account retry failed", { accountId, error: conn.lastSyncError });
|
|
1200655
1200657
|
this.scheduleRetry(accountId);
|
|
1200656
1200658
|
}
|
|
1200657
1200659
|
}, delay2);
|
|
@@ -1200709,10 +1200711,10 @@ class CaldavAccountManager {
|
|
|
1200709
1200711
|
if (!seenUrls.has(local.remoteUrl)) {
|
|
1200710
1200712
|
db2.delete(caldavEvents2).where(eq(caldavEvents2.calendarId, local.id)).run();
|
|
1200711
1200713
|
db2.delete(caldavCalendars).where(eq(caldavCalendars.id, local.id)).run();
|
|
1200712
|
-
|
|
1200714
|
+
logger7.info("Removed deleted calendar", { displayName: local.displayName, accountId });
|
|
1200713
1200715
|
}
|
|
1200714
1200716
|
}
|
|
1200715
|
-
|
|
1200717
|
+
logger7.info("Account sync complete", { accountId, calendars: remoteCalendars.length });
|
|
1200716
1200718
|
}
|
|
1200717
1200719
|
async syncCalendarEvents(calendarId, remoteCal, client4) {
|
|
1200718
1200720
|
const { caldavEvents: caldavEvents2 } = await Promise.resolve().then(() => (init_db2(), exports_db));
|
|
@@ -1200779,13 +1200781,13 @@ class CaldavAccountManager {
|
|
|
1200779
1200781
|
}
|
|
1200780
1200782
|
}
|
|
1200781
1200783
|
}
|
|
1200782
|
-
var
|
|
1200784
|
+
var logger7, GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token", MAX_RETRY_DELAY, accountManager;
|
|
1200783
1200785
|
var init_caldav_account_manager = __esm(() => {
|
|
1200784
1200786
|
init_tsdav_esm();
|
|
1200785
1200787
|
init_drizzle_orm();
|
|
1200786
1200788
|
init_db2();
|
|
1200787
1200789
|
init_logger3();
|
|
1200788
|
-
|
|
1200790
|
+
logger7 = createLogger("CalDAV:AccountManager");
|
|
1200789
1200791
|
MAX_RETRY_DELAY = 5 * 60 * 1000;
|
|
1200790
1200792
|
accountManager = new CaldavAccountManager;
|
|
1200791
1200793
|
});
|
|
@@ -1200808,7 +1200810,7 @@ class GoogleCalendarManager {
|
|
|
1200808
1200810
|
try {
|
|
1200809
1200811
|
await this.syncAccount(accountId);
|
|
1200810
1200812
|
} catch (err) {
|
|
1200811
|
-
|
|
1200813
|
+
logger8.error("Initial Google Calendar sync failed", {
|
|
1200812
1200814
|
accountId,
|
|
1200813
1200815
|
error: err instanceof Error ? err.message : String(err)
|
|
1200814
1200816
|
});
|
|
@@ -1200816,13 +1200818,13 @@ class GoogleCalendarManager {
|
|
|
1200816
1200818
|
const intervalMs = (account2.syncIntervalMinutes ?? 15) * 60 * 1000;
|
|
1200817
1200819
|
conn.syncInterval = setInterval(() => {
|
|
1200818
1200820
|
this.syncAccount(accountId).catch((err) => {
|
|
1200819
|
-
|
|
1200821
|
+
logger8.error("Periodic Google Calendar sync failed", {
|
|
1200820
1200822
|
accountId,
|
|
1200821
1200823
|
error: err instanceof Error ? err.message : String(err)
|
|
1200822
1200824
|
});
|
|
1200823
1200825
|
});
|
|
1200824
1200826
|
}, intervalMs);
|
|
1200825
|
-
|
|
1200827
|
+
logger8.info("Started Google Calendar account", { accountId, name: account2.name });
|
|
1200826
1200828
|
}
|
|
1200827
1200829
|
stopAccount(accountId) {
|
|
1200828
1200830
|
const conn = this.connections.get(accountId);
|
|
@@ -1200840,10 +1200842,10 @@ class GoogleCalendarManager {
|
|
|
1200840
1200842
|
}
|
|
1200841
1200843
|
async startAll() {
|
|
1200842
1200844
|
const accounts = db2.select().from(googleAccounts).where(eq(googleAccounts.calendarEnabled, true)).all();
|
|
1200843
|
-
|
|
1200845
|
+
logger8.info("Starting Google Calendar accounts", { count: accounts.length });
|
|
1200844
1200846
|
for (const account2 of accounts) {
|
|
1200845
1200847
|
await this.startAccount(account2.id).catch((err) => {
|
|
1200846
|
-
|
|
1200848
|
+
logger8.error("Failed to start Google Calendar account", {
|
|
1200847
1200849
|
accountId: account2.id,
|
|
1200848
1200850
|
error: err instanceof Error ? err.message : String(err)
|
|
1200849
1200851
|
});
|
|
@@ -1200905,7 +1200907,7 @@ class GoogleCalendarManager {
|
|
|
1200905
1200907
|
if (!seenRemoteUrls.has(local.remoteUrl)) {
|
|
1200906
1200908
|
db2.delete(caldavEvents).where(eq(caldavEvents.calendarId, local.id)).run();
|
|
1200907
1200909
|
db2.delete(caldavCalendars).where(eq(caldavCalendars.id, local.id)).run();
|
|
1200908
|
-
|
|
1200910
|
+
logger8.info("Removed deleted Google calendar", {
|
|
1200909
1200911
|
displayName: local.displayName,
|
|
1200910
1200912
|
accountId
|
|
1200911
1200913
|
});
|
|
@@ -1200919,7 +1200921,7 @@ class GoogleCalendarManager {
|
|
|
1200919
1200921
|
if (conn) {
|
|
1200920
1200922
|
conn.lastSyncError = null;
|
|
1200921
1200923
|
}
|
|
1200922
|
-
|
|
1200924
|
+
logger8.info("Google Calendar sync complete", {
|
|
1200923
1200925
|
accountId,
|
|
1200924
1200926
|
calendars: remoteCalendars.length
|
|
1200925
1200927
|
});
|
|
@@ -1201136,14 +1201138,14 @@ class GoogleCalendarManager {
|
|
|
1201136
1201138
|
return this.connections.get(accountId)?.lastSyncError ?? null;
|
|
1201137
1201139
|
}
|
|
1201138
1201140
|
}
|
|
1201139
|
-
var import_googleapis3,
|
|
1201141
|
+
var import_googleapis3, logger8, googleCalendarManager;
|
|
1201140
1201142
|
var init_google_calendar_manager = __esm(() => {
|
|
1201141
1201143
|
init_drizzle_orm();
|
|
1201142
1201144
|
init_db2();
|
|
1201143
1201145
|
init_google_oauth();
|
|
1201144
1201146
|
init_logger3();
|
|
1201145
1201147
|
import_googleapis3 = __toESM(require_src12(), 1);
|
|
1201146
|
-
|
|
1201148
|
+
logger8 = createLogger("Google:Calendar");
|
|
1201147
1201149
|
googleCalendarManager = new GoogleCalendarManager;
|
|
1201148
1201150
|
});
|
|
1201149
1201151
|
|
|
@@ -1201157,12 +1201159,12 @@ async function executeAllRules() {
|
|
|
1201157
1201159
|
const rules = db2.select().from(caldavCopyRules).where(eq(caldavCopyRules.enabled, true)).all();
|
|
1201158
1201160
|
if (rules.length === 0)
|
|
1201159
1201161
|
return;
|
|
1201160
|
-
|
|
1201162
|
+
logger9.info("Executing copy rules", { count: rules.length });
|
|
1201161
1201163
|
for (const rule of rules) {
|
|
1201162
1201164
|
try {
|
|
1201163
1201165
|
await executeSingleRule(rule.id);
|
|
1201164
1201166
|
} catch (err) {
|
|
1201165
|
-
|
|
1201167
|
+
logger9.error("Copy rule failed", {
|
|
1201166
1201168
|
ruleId: rule.id,
|
|
1201167
1201169
|
error: err instanceof Error ? err.message : String(err)
|
|
1201168
1201170
|
});
|
|
@@ -1201176,7 +1201178,7 @@ async function executeSingleRule(ruleId) {
|
|
|
1201176
1201178
|
const sourceCal = db2.select().from(caldavCalendars).where(eq(caldavCalendars.id, rule.sourceCalendarId)).get();
|
|
1201177
1201179
|
const destCal = db2.select().from(caldavCalendars).where(eq(caldavCalendars.id, rule.destCalendarId)).get();
|
|
1201178
1201180
|
if (!sourceCal || !destCal) {
|
|
1201179
|
-
|
|
1201181
|
+
logger9.warn("Copy rule references missing calendar", {
|
|
1201180
1201182
|
ruleId,
|
|
1201181
1201183
|
sourceExists: !!sourceCal,
|
|
1201182
1201184
|
destExists: !!destCal
|
|
@@ -1201186,7 +1201188,7 @@ async function executeSingleRule(ruleId) {
|
|
|
1201186
1201188
|
const isGoogleDest = !!destCal.googleAccountId;
|
|
1201187
1201189
|
const destClient = destCal.accountId ? accountManager.getClient(destCal.accountId) : null;
|
|
1201188
1201190
|
if (!isGoogleDest && !destClient) {
|
|
1201189
|
-
|
|
1201191
|
+
logger9.warn("Destination account not connected", {
|
|
1201190
1201192
|
ruleId,
|
|
1201191
1201193
|
destCalendarId: destCal.id,
|
|
1201192
1201194
|
destAccountId: destCal.accountId
|
|
@@ -1201272,7 +1201274,7 @@ async function executeSingleRule(ruleId) {
|
|
|
1201272
1201274
|
}).run();
|
|
1201273
1201275
|
created++;
|
|
1201274
1201276
|
} catch (err) {
|
|
1201275
|
-
|
|
1201277
|
+
logger9.error("Failed to copy event", {
|
|
1201276
1201278
|
ruleId,
|
|
1201277
1201279
|
sourceEventId: sourceEvent.id,
|
|
1201278
1201280
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -1201336,7 +1201338,7 @@ async function executeSingleRule(ruleId) {
|
|
|
1201336
1201338
|
db2.update(caldavCopiedEvents).set({ sourceEtag: sourceEvent.etag, updatedAt: now }).where(eq(caldavCopiedEvents.id, existingCopy.id)).run();
|
|
1201337
1201339
|
updated++;
|
|
1201338
1201340
|
} catch (err) {
|
|
1201339
|
-
|
|
1201341
|
+
logger9.error("Failed to update copied event", {
|
|
1201340
1201342
|
ruleId,
|
|
1201341
1201343
|
sourceEventId: sourceEvent.id,
|
|
1201342
1201344
|
destEventId: existingCopy.destEventId,
|
|
@@ -1201347,11 +1201349,11 @@ async function executeSingleRule(ruleId) {
|
|
|
1201347
1201349
|
}
|
|
1201348
1201350
|
db2.update(caldavCopyRules).set({ lastExecutedAt: now, updatedAt: now }).where(eq(caldavCopyRules.id, ruleId)).run();
|
|
1201349
1201351
|
if (created > 0 || updated > 0) {
|
|
1201350
|
-
|
|
1201352
|
+
logger9.info("Copy rule executed", { ruleId, created, updated });
|
|
1201351
1201353
|
}
|
|
1201352
1201354
|
return { created, updated };
|
|
1201353
1201355
|
}
|
|
1201354
|
-
var
|
|
1201356
|
+
var logger9;
|
|
1201355
1201357
|
var init_copy_engine = __esm(() => {
|
|
1201356
1201358
|
init_drizzle_orm();
|
|
1201357
1201359
|
init_db2();
|
|
@@ -1201359,7 +1201361,7 @@ var init_copy_engine = __esm(() => {
|
|
|
1201359
1201361
|
init_ical_helpers();
|
|
1201360
1201362
|
init_caldav_account_manager();
|
|
1201361
1201363
|
init_google_calendar_manager();
|
|
1201362
|
-
|
|
1201364
|
+
logger9 = createLogger("CalDAV:CopyEngine");
|
|
1201363
1201365
|
});
|
|
1201364
1201366
|
|
|
1201365
1201367
|
// node_modules/@hono/node-server/dist/index.mjs
|
|
@@ -1208351,6 +1208353,8 @@ async function updateTaskStatus(taskId, newStatus, newPosition) {
|
|
|
1208351
1208353
|
// server/services/search-service.ts
|
|
1208352
1208354
|
init_db2();
|
|
1208353
1208355
|
init_drizzle_orm();
|
|
1208356
|
+
init_schema();
|
|
1208357
|
+
init_logger3();
|
|
1208354
1208358
|
var ALL_ENTITIES = ["tasks", "projects", "messages", "events", "memories", "conversations"];
|
|
1208355
1208359
|
async function search(options) {
|
|
1208356
1208360
|
const entities = options.entities?.length ? options.entities : ALL_ENTITIES;
|
|
@@ -1208369,6 +1208373,8 @@ async function search(options) {
|
|
|
1208369
1208373
|
return searchMemories2(options.query, { tags: options.memoryTags }, limit);
|
|
1208370
1208374
|
case "conversations":
|
|
1208371
1208375
|
return searchConversations(options.query, { role: options.conversationRole, provider: options.conversationProvider, projectId: options.conversationProjectId }, limit);
|
|
1208376
|
+
case "gmail":
|
|
1208377
|
+
return searchGmail(options.query, { from: options.gmailFrom, to: options.gmailTo, after: options.gmailAfter, before: options.gmailBefore }, limit);
|
|
1208372
1208378
|
}
|
|
1208373
1208379
|
});
|
|
1208374
1208380
|
const resultSets = await Promise.all(searches);
|
|
@@ -1208572,6 +1208578,55 @@ async function searchConversations(query, filters, limit) {
|
|
|
1208572
1208578
|
}
|
|
1208573
1208579
|
}));
|
|
1208574
1208580
|
}
|
|
1208581
|
+
var logger6 = createLogger("SearchService");
|
|
1208582
|
+
async function searchGmail(query, filters, limit) {
|
|
1208583
|
+
const accounts = db2.select().from(googleAccounts).where(eq(googleAccounts.gmailEnabled, true)).all();
|
|
1208584
|
+
if (!accounts.length)
|
|
1208585
|
+
return [];
|
|
1208586
|
+
const parts = [query];
|
|
1208587
|
+
if (filters.from)
|
|
1208588
|
+
parts.push(`from:${filters.from}`);
|
|
1208589
|
+
if (filters.to)
|
|
1208590
|
+
parts.push(`to:${filters.to}`);
|
|
1208591
|
+
if (filters.after)
|
|
1208592
|
+
parts.push(`after:${filters.after}`);
|
|
1208593
|
+
if (filters.before)
|
|
1208594
|
+
parts.push(`before:${filters.before}`);
|
|
1208595
|
+
const gmailQuery = parts.join(" ");
|
|
1208596
|
+
const { listMessages: listMessages2 } = await Promise.resolve().then(() => (init_gmail_service(), exports_gmail_service));
|
|
1208597
|
+
const results = await Promise.allSettled(accounts.map(async (account) => {
|
|
1208598
|
+
const messages2 = await listMessages2(account.id, {
|
|
1208599
|
+
query: gmailQuery,
|
|
1208600
|
+
maxResults: limit
|
|
1208601
|
+
});
|
|
1208602
|
+
return messages2.map((msg) => ({
|
|
1208603
|
+
entityType: "gmail",
|
|
1208604
|
+
id: msg.id,
|
|
1208605
|
+
title: msg.subject || "(no subject)",
|
|
1208606
|
+
snippet: msg.snippet || "",
|
|
1208607
|
+
score: 0.9,
|
|
1208608
|
+
metadata: {
|
|
1208609
|
+
threadId: msg.threadId,
|
|
1208610
|
+
from: msg.from,
|
|
1208611
|
+
to: msg.to,
|
|
1208612
|
+
date: msg.date,
|
|
1208613
|
+
labels: msg.labels,
|
|
1208614
|
+
accountId: account.id,
|
|
1208615
|
+
accountEmail: account.email,
|
|
1208616
|
+
gmailLink: `https://mail.google.com/mail/u/0/#inbox/${msg.id}`
|
|
1208617
|
+
}
|
|
1208618
|
+
}));
|
|
1208619
|
+
}));
|
|
1208620
|
+
const allResults = [];
|
|
1208621
|
+
for (const result of results) {
|
|
1208622
|
+
if (result.status === "fulfilled") {
|
|
1208623
|
+
allResults.push(...result.value);
|
|
1208624
|
+
} else {
|
|
1208625
|
+
logger6.warn("Gmail search failed for an account", { error: String(result.reason) });
|
|
1208626
|
+
}
|
|
1208627
|
+
}
|
|
1208628
|
+
return allResults.slice(0, limit);
|
|
1208629
|
+
}
|
|
1208575
1208630
|
function reindexTaskFTS(taskId) {
|
|
1208576
1208631
|
db2.run(sql`
|
|
1208577
1208632
|
UPDATE tasks SET updated_at = updated_at WHERE id = ${taskId}
|
|
@@ -1212956,20 +1213011,20 @@ var VERSION4 = "7.0.6";
|
|
|
1212956
1213011
|
var noop2 = () => {};
|
|
1212957
1213012
|
var consoleWarn = console.warn.bind(console);
|
|
1212958
1213013
|
var consoleError = console.error.bind(console);
|
|
1212959
|
-
function createLogger2(
|
|
1212960
|
-
if (typeof
|
|
1212961
|
-
|
|
1213014
|
+
function createLogger2(logger7 = {}) {
|
|
1213015
|
+
if (typeof logger7.debug !== "function") {
|
|
1213016
|
+
logger7.debug = noop2;
|
|
1212962
1213017
|
}
|
|
1212963
|
-
if (typeof
|
|
1212964
|
-
|
|
1213018
|
+
if (typeof logger7.info !== "function") {
|
|
1213019
|
+
logger7.info = noop2;
|
|
1212965
1213020
|
}
|
|
1212966
|
-
if (typeof
|
|
1212967
|
-
|
|
1213021
|
+
if (typeof logger7.warn !== "function") {
|
|
1213022
|
+
logger7.warn = consoleWarn;
|
|
1212968
1213023
|
}
|
|
1212969
|
-
if (typeof
|
|
1212970
|
-
|
|
1213024
|
+
if (typeof logger7.error !== "function") {
|
|
1213025
|
+
logger7.error = consoleError;
|
|
1212971
1213026
|
}
|
|
1212972
|
-
return
|
|
1213027
|
+
return logger7;
|
|
1212973
1213028
|
}
|
|
1212974
1213029
|
var userAgentTrail = `octokit-core.js/${VERSION4} ${getUserAgent2()}`;
|
|
1212975
1213030
|
|
|
@@ -1251854,9 +1251909,9 @@ var toolRegistry = [
|
|
|
1251854
1251909
|
},
|
|
1251855
1251910
|
{
|
|
1251856
1251911
|
name: "search",
|
|
1251857
|
-
description: "Search across all Fulcrum entities (tasks, projects, messages, events, memories) using full-text search",
|
|
1251912
|
+
description: "Search across all Fulcrum entities (tasks, projects, messages, events, memories, gmail) using full-text search",
|
|
1251858
1251913
|
category: "core",
|
|
1251859
|
-
keywords: ["search", "find", "query", "fts", "full-text", "task", "project", "message", "event", "memory", "recall", "knowledge"],
|
|
1251914
|
+
keywords: ["search", "find", "query", "fts", "full-text", "task", "project", "message", "event", "memory", "recall", "knowledge", "gmail", "email", "google"],
|
|
1251860
1251915
|
defer_loading: false
|
|
1251861
1251916
|
},
|
|
1251862
1251917
|
{
|
|
@@ -1253645,9 +1253700,9 @@ var registerMessagingTools = (server2, client3) => {
|
|
|
1253645
1253700
|
};
|
|
1253646
1253701
|
|
|
1253647
1253702
|
// cli/src/mcp/tools/search.ts
|
|
1253648
|
-
var EntityTypeSchema = exports_external.enum(["tasks", "projects", "messages", "events", "memories", "conversations"]);
|
|
1253703
|
+
var EntityTypeSchema = exports_external.enum(["tasks", "projects", "messages", "events", "memories", "conversations", "gmail"]);
|
|
1253649
1253704
|
var registerSearchTools = (server2, client3) => {
|
|
1253650
|
-
server2.tool("search", 'Search across all Fulcrum entities (tasks, projects, messages, calendar events, memories, conversations) using full-text search. Supports boolean operators (AND, OR, NOT), phrase matching ("quoted phrases"), and prefix matching (term*). Returns results ranked by relevance.', {
|
|
1253705
|
+
server2.tool("search", 'Search across all Fulcrum entities (tasks, projects, messages, calendar events, memories, conversations) using full-text search. Supports boolean operators (AND, OR, NOT), phrase matching ("quoted phrases"), and prefix matching (term*). Returns results ranked by relevance. Gmail search is opt-in: include "gmail" in entities to search Gmail via API (not included in default searches).', {
|
|
1253651
1253706
|
query: exports_external.string().describe('FTS5 search query. Supports: AND, OR, NOT operators, "quoted phrases", prefix* matching. Example: "kubernetes deployment" OR k8s'),
|
|
1253652
1253707
|
entities: exports_external.optional(exports_external.array(EntityTypeSchema)).describe("Entity types to search. Defaults to all: tasks, projects, messages, events, memories, conversations"),
|
|
1253653
1253708
|
limit: exports_external.optional(exports_external.number()).describe("Maximum results per entity type (default: 10)"),
|
|
@@ -1253660,8 +1253715,12 @@ var registerSearchTools = (server2, client3) => {
|
|
|
1253660
1253715
|
memoryTags: exports_external.optional(exports_external.array(exports_external.string())).describe("Filter memories by tags"),
|
|
1253661
1253716
|
conversationRole: exports_external.optional(exports_external.string()).describe('Filter conversations by role (e.g., "user", "assistant")'),
|
|
1253662
1253717
|
conversationProvider: exports_external.optional(exports_external.string()).describe('Filter conversations by provider (e.g., "claude", "opencode")'),
|
|
1253663
|
-
conversationProjectId: exports_external.optional(exports_external.string()).describe("Filter conversations by project ID")
|
|
1253664
|
-
|
|
1253718
|
+
conversationProjectId: exports_external.optional(exports_external.string()).describe("Filter conversations by project ID"),
|
|
1253719
|
+
gmailFrom: exports_external.optional(exports_external.string()).describe('Filter Gmail results by sender (e.g., "user@example.com")'),
|
|
1253720
|
+
gmailTo: exports_external.optional(exports_external.string()).describe('Filter Gmail results by recipient (e.g., "user@example.com")'),
|
|
1253721
|
+
gmailAfter: exports_external.optional(exports_external.string()).describe("Filter Gmail results after this date (YYYY/MM/DD format)"),
|
|
1253722
|
+
gmailBefore: exports_external.optional(exports_external.string()).describe("Filter Gmail results before this date (YYYY/MM/DD format)")
|
|
1253723
|
+
}, async ({ query, entities, limit: limit2, taskStatus, projectStatus, messageChannel, messageDirection, eventFrom, eventTo, memoryTags, conversationRole, conversationProvider, conversationProjectId, gmailFrom, gmailTo, gmailAfter, gmailBefore }) => {
|
|
1253665
1253724
|
try {
|
|
1253666
1253725
|
const results = await client3.search({
|
|
1253667
1253726
|
query,
|
|
@@ -1253676,7 +1253735,11 @@ var registerSearchTools = (server2, client3) => {
|
|
|
1253676
1253735
|
memoryTags,
|
|
1253677
1253736
|
conversationRole,
|
|
1253678
1253737
|
conversationProvider,
|
|
1253679
|
-
conversationProjectId
|
|
1253738
|
+
conversationProjectId,
|
|
1253739
|
+
gmailFrom,
|
|
1253740
|
+
gmailTo,
|
|
1253741
|
+
gmailAfter,
|
|
1253742
|
+
gmailBefore
|
|
1253680
1253743
|
});
|
|
1253681
1253744
|
return formatSuccess(results);
|
|
1253682
1253745
|
} catch (err) {
|
|
@@ -1254516,6 +1254579,14 @@ class FulcrumClient {
|
|
|
1254516
1254579
|
params.set("conversationProvider", input.conversationProvider);
|
|
1254517
1254580
|
if (input.conversationProjectId)
|
|
1254518
1254581
|
params.set("conversationProjectId", input.conversationProjectId);
|
|
1254582
|
+
if (input.gmailFrom)
|
|
1254583
|
+
params.set("gmailFrom", input.gmailFrom);
|
|
1254584
|
+
if (input.gmailTo)
|
|
1254585
|
+
params.set("gmailTo", input.gmailTo);
|
|
1254586
|
+
if (input.gmailAfter)
|
|
1254587
|
+
params.set("gmailAfter", input.gmailAfter);
|
|
1254588
|
+
if (input.gmailBefore)
|
|
1254589
|
+
params.set("gmailBefore", input.gmailBefore);
|
|
1254519
1254590
|
return this.fetch(`/api/search?${params.toString()}`);
|
|
1254520
1254591
|
}
|
|
1254521
1254592
|
}
|
|
@@ -1254531,7 +1254602,7 @@ mcpRoutes.all("/", async (c) => {
|
|
|
1254531
1254602
|
});
|
|
1254532
1254603
|
const server2 = new McpServer({
|
|
1254533
1254604
|
name: "fulcrum",
|
|
1254534
|
-
version: "3.
|
|
1254605
|
+
version: "3.3.0"
|
|
1254535
1254606
|
});
|
|
1254536
1254607
|
const client3 = new FulcrumClient(`http://localhost:${port}`);
|
|
1254537
1254608
|
registerTools(server2, client3);
|
|
@@ -1255855,7 +1255926,7 @@ init_logger3();
|
|
|
1255855
1255926
|
init_ical_helpers();
|
|
1255856
1255927
|
init_caldav_account_manager();
|
|
1255857
1255928
|
init_google_calendar_manager();
|
|
1255858
|
-
var
|
|
1255929
|
+
var logger10 = createLogger("CalDAV");
|
|
1255859
1255930
|
function migrateFromSettings() {
|
|
1255860
1255931
|
const settings = getSettings();
|
|
1255861
1255932
|
const caldavSettings = settings.caldav;
|
|
@@ -1255871,7 +1255942,7 @@ function migrateFromSettings() {
|
|
|
1255871
1255942
|
return;
|
|
1255872
1255943
|
return;
|
|
1255873
1255944
|
}
|
|
1255874
|
-
|
|
1255945
|
+
logger10.info("Migrating CalDAV credentials from settings.json to database", {
|
|
1255875
1255946
|
authType: caldavSettings.authType,
|
|
1255876
1255947
|
orphanedCalendars: orphanedCalendars.length
|
|
1255877
1255948
|
});
|
|
@@ -1255896,28 +1255967,28 @@ function migrateFromSettings() {
|
|
|
1255896
1255967
|
for (const cal of orphanedCalendars) {
|
|
1255897
1255968
|
db2.update(caldavCalendars).set({ accountId, updatedAt: now }).where(eq(caldavCalendars.id, cal.id)).run();
|
|
1255898
1255969
|
}
|
|
1255899
|
-
|
|
1255970
|
+
logger10.info("Migration complete", { accountId, migratedCalendars: orphanedCalendars.length });
|
|
1255900
1255971
|
}
|
|
1255901
1255972
|
async function startCaldavSync() {
|
|
1255902
1255973
|
const settings = getSettings();
|
|
1255903
1255974
|
if (!settings.caldav?.enabled) {
|
|
1255904
|
-
|
|
1255975
|
+
logger10.info("CalDAV disabled, skipping sync start");
|
|
1255905
1255976
|
return;
|
|
1255906
1255977
|
}
|
|
1255907
1255978
|
migrateFromSettings();
|
|
1255908
1255979
|
await accountManager.startAll();
|
|
1255909
1255980
|
await accountManager.syncAll().catch((err) => {
|
|
1255910
|
-
|
|
1255981
|
+
logger10.error("Initial sync failed", { error: err instanceof Error ? err.message : String(err) });
|
|
1255911
1255982
|
});
|
|
1255912
1255983
|
await executeCopyRules().catch((err) => {
|
|
1255913
|
-
|
|
1255984
|
+
logger10.error("Copy rules execution failed after sync", {
|
|
1255914
1255985
|
error: err instanceof Error ? err.message : String(err)
|
|
1255915
1255986
|
});
|
|
1255916
1255987
|
});
|
|
1255917
1255988
|
}
|
|
1255918
1255989
|
function stopCaldavSync() {
|
|
1255919
1255990
|
accountManager.stopAll();
|
|
1255920
|
-
|
|
1255991
|
+
logger10.info("CalDAV sync stopped");
|
|
1255921
1255992
|
}
|
|
1255922
1255993
|
function getCaldavStatus() {
|
|
1255923
1255994
|
const accounts = accountManager.getStatus();
|
|
@@ -1255960,7 +1256031,7 @@ async function createAccount2(input) {
|
|
|
1255960
1256031
|
updatedAt: now
|
|
1255961
1256032
|
};
|
|
1255962
1256033
|
db2.insert(caldavAccounts).values(account2).run();
|
|
1255963
|
-
|
|
1256034
|
+
logger10.info("Created CalDAV account", { id, name: input.name });
|
|
1255964
1256035
|
return account2;
|
|
1255965
1256036
|
}
|
|
1255966
1256037
|
async function updateAccount(id, updates) {
|
|
@@ -1255988,7 +1256059,7 @@ async function deleteAccount(id) {
|
|
|
1255988
1256059
|
}
|
|
1255989
1256060
|
db2.delete(caldavCalendars).where(eq(caldavCalendars.accountId, id)).run();
|
|
1255990
1256061
|
db2.delete(caldavAccounts).where(eq(caldavAccounts.id, id)).run();
|
|
1255991
|
-
|
|
1256062
|
+
logger10.info("Deleted CalDAV account", { id });
|
|
1255992
1256063
|
}
|
|
1255993
1256064
|
async function enableAccount(id) {
|
|
1255994
1256065
|
db2.update(caldavAccounts).set({ enabled: true, updatedAt: new Date().toISOString() }).where(eq(caldavAccounts.id, id)).run();
|
|
@@ -1256024,7 +1256095,7 @@ async function testAccountConnection(config3) {
|
|
|
1256024
1256095
|
async function syncAccount(id) {
|
|
1256025
1256096
|
await accountManager.syncAccount(id);
|
|
1256026
1256097
|
await executeCopyRules().catch((err) => {
|
|
1256027
|
-
|
|
1256098
|
+
logger10.error("Copy rules failed after account sync", {
|
|
1256028
1256099
|
error: err instanceof Error ? err.message : String(err)
|
|
1256029
1256100
|
});
|
|
1256030
1256101
|
});
|
|
@@ -1256130,7 +1256201,7 @@ function listCalendars(accountId) {
|
|
|
1256130
1256201
|
async function syncCalendars2() {
|
|
1256131
1256202
|
await accountManager.syncAll();
|
|
1256132
1256203
|
await executeCopyRules().catch((err) => {
|
|
1256133
|
-
|
|
1256204
|
+
logger10.error("Copy rules failed after sync", {
|
|
1256134
1256205
|
error: err instanceof Error ? err.message : String(err)
|
|
1256135
1256206
|
});
|
|
1256136
1256207
|
});
|
|
@@ -1256221,7 +1256292,7 @@ async function createEvent(input) {
|
|
|
1256221
1256292
|
updatedAt: now
|
|
1256222
1256293
|
};
|
|
1256223
1256294
|
db2.insert(caldavEvents).values(event).run();
|
|
1256224
|
-
|
|
1256295
|
+
logger10.info("Created CalDAV event", { id, summary: input.summary });
|
|
1256225
1256296
|
return event;
|
|
1256226
1256297
|
}
|
|
1256227
1256298
|
async function updateEvent(id, updates) {
|
|
@@ -1256271,7 +1256342,7 @@ async function updateEvent(id, updates) {
|
|
|
1256271
1256342
|
rawIcal: updatedIcal,
|
|
1256272
1256343
|
updatedAt: now
|
|
1256273
1256344
|
}).where(eq(caldavEvents.id, id)).run();
|
|
1256274
|
-
|
|
1256345
|
+
logger10.info("Updated CalDAV event", { id, summary: updates.summary ?? event.summary });
|
|
1256275
1256346
|
return db2.select().from(caldavEvents).where(eq(caldavEvents.id, id)).get();
|
|
1256276
1256347
|
}
|
|
1256277
1256348
|
async function deleteEvent(id) {
|
|
@@ -1256295,7 +1256366,7 @@ async function deleteEvent(id) {
|
|
|
1256295
1256366
|
}
|
|
1256296
1256367
|
});
|
|
1256297
1256368
|
db2.delete(caldavEvents).where(eq(caldavEvents.id, id)).run();
|
|
1256298
|
-
|
|
1256369
|
+
logger10.info("Deleted CalDAV event", { id, summary: event.summary });
|
|
1256299
1256370
|
}
|
|
1256300
1256371
|
function listCopyRules() {
|
|
1256301
1256372
|
return db2.select().from(caldavCopyRules).all();
|
|
@@ -1256314,7 +1256385,7 @@ function createCopyRule(input) {
|
|
|
1256314
1256385
|
updatedAt: now
|
|
1256315
1256386
|
};
|
|
1256316
1256387
|
db2.insert(caldavCopyRules).values(rule).run();
|
|
1256317
|
-
|
|
1256388
|
+
logger10.info("Created copy rule", { id, source: input.sourceCalendarId, dest: input.destCalendarId });
|
|
1256318
1256389
|
return rule;
|
|
1256319
1256390
|
}
|
|
1256320
1256391
|
function updateCopyRule(id, updates) {
|
|
@@ -1256327,7 +1256398,7 @@ function updateCopyRule(id, updates) {
|
|
|
1256327
1256398
|
function deleteCopyRule(id) {
|
|
1256328
1256399
|
db2.delete(caldavCopiedEvents).where(eq(caldavCopiedEvents.ruleId, id)).run();
|
|
1256329
1256400
|
db2.delete(caldavCopyRules).where(eq(caldavCopyRules.id, id)).run();
|
|
1256330
|
-
|
|
1256401
|
+
logger10.info("Deleted copy rule", { id });
|
|
1256331
1256402
|
}
|
|
1256332
1256403
|
async function executeCopyRule(ruleId) {
|
|
1256333
1256404
|
const { executeSingleRule: executeSingleRule2 } = await Promise.resolve().then(() => (init_copy_engine(), exports_copy_engine));
|
|
@@ -1256770,13 +1256841,13 @@ init_db2();
|
|
|
1256770
1256841
|
init_settings();
|
|
1256771
1256842
|
init_google_oauth();
|
|
1256772
1256843
|
init_logger3();
|
|
1256773
|
-
var
|
|
1256844
|
+
var logger11 = createLogger("GoogleOAuth:Routes");
|
|
1256774
1256845
|
var app25 = new Hono2;
|
|
1256775
1256846
|
app25.get("/authorize", (c) => {
|
|
1256776
1256847
|
const accountName = c.req.query("accountName");
|
|
1256777
1256848
|
const accountId = c.req.query("accountId");
|
|
1256778
1256849
|
const clientOrigin = c.req.query("origin");
|
|
1256779
|
-
|
|
1256850
|
+
logger11.info("OAuth authorize request", {
|
|
1256780
1256851
|
accountName,
|
|
1256781
1256852
|
accountId,
|
|
1256782
1256853
|
clientOrigin,
|
|
@@ -1256800,7 +1256871,7 @@ app25.get("/authorize", (c) => {
|
|
|
1256800
1256871
|
accountName: accountName || "Google Account",
|
|
1256801
1256872
|
redirectUri
|
|
1256802
1256873
|
});
|
|
1256803
|
-
|
|
1256874
|
+
logger11.info("OAuth authorize: generating auth URL", {
|
|
1256804
1256875
|
baseUrl,
|
|
1256805
1256876
|
redirectUri,
|
|
1256806
1256877
|
stateJson: state,
|
|
@@ -1256809,18 +1256880,18 @@ app25.get("/authorize", (c) => {
|
|
|
1256809
1256880
|
const authUrl = generateAuthUrl(client4, redirectUri, state);
|
|
1256810
1256881
|
try {
|
|
1256811
1256882
|
const parsed = new URL(authUrl);
|
|
1256812
|
-
|
|
1256883
|
+
logger11.info("OAuth authorize: generated auth URL", {
|
|
1256813
1256884
|
authUrl,
|
|
1256814
1256885
|
redirect_uri_in_url: parsed.searchParams.get("redirect_uri"),
|
|
1256815
1256886
|
client_id_in_url: parsed.searchParams.get("client_id"),
|
|
1256816
1256887
|
scope_in_url: parsed.searchParams.get("scope")
|
|
1256817
1256888
|
});
|
|
1256818
1256889
|
} catch {
|
|
1256819
|
-
|
|
1256890
|
+
logger11.info("OAuth authorize: generated auth URL (unparseable)", { authUrl });
|
|
1256820
1256891
|
}
|
|
1256821
1256892
|
return c.json({ authUrl });
|
|
1256822
1256893
|
} catch (err) {
|
|
1256823
|
-
|
|
1256894
|
+
logger11.error("OAuth authorize failed", {
|
|
1256824
1256895
|
error: err instanceof Error ? err.message : String(err)
|
|
1256825
1256896
|
});
|
|
1256826
1256897
|
return c.json({ error: err instanceof Error ? err.message : "Failed to generate auth URL" }, 400);
|
|
@@ -1256831,7 +1256902,7 @@ app25.get("/callback", async (c) => {
|
|
|
1256831
1256902
|
const error46 = c.req.query("error");
|
|
1256832
1256903
|
const stateParam = c.req.query("state");
|
|
1256833
1256904
|
const fullUrl = c.req.url;
|
|
1256834
|
-
|
|
1256905
|
+
logger11.info("OAuth callback received", {
|
|
1256835
1256906
|
hasCode: !!code,
|
|
1256836
1256907
|
codePrefix: code?.slice(0, 20) + "...",
|
|
1256837
1256908
|
error: error46,
|
|
@@ -1256842,11 +1256913,11 @@ app25.get("/callback", async (c) => {
|
|
|
1256842
1256913
|
refererHeader: c.req.header("referer")
|
|
1256843
1256914
|
});
|
|
1256844
1256915
|
if (error46) {
|
|
1256845
|
-
|
|
1256916
|
+
logger11.warn("OAuth callback: Google returned error", { error: error46 });
|
|
1256846
1256917
|
return c.html(`<html><body><h2>Authorization Failed</h2><p>${error46}</p><script>setTimeout(()=>window.close(),3000)</script></body></html>`);
|
|
1256847
1256918
|
}
|
|
1256848
1256919
|
if (!code) {
|
|
1256849
|
-
|
|
1256920
|
+
logger11.warn("OAuth callback: missing authorization code");
|
|
1256850
1256921
|
return c.html("<html><body><h2>Missing authorization code</h2><script>setTimeout(()=>window.close(),3000)</script></body></html>", 400);
|
|
1256851
1256922
|
}
|
|
1256852
1256923
|
let state = {
|
|
@@ -1256858,9 +1256929,9 @@ app25.get("/callback", async (c) => {
|
|
|
1256858
1256929
|
state = JSON.parse(stateParam);
|
|
1256859
1256930
|
}
|
|
1256860
1256931
|
} catch {
|
|
1256861
|
-
|
|
1256932
|
+
logger11.warn("OAuth callback: failed to parse state param", { stateParam });
|
|
1256862
1256933
|
}
|
|
1256863
|
-
|
|
1256934
|
+
logger11.info("OAuth callback: parsed state", {
|
|
1256864
1256935
|
accountId: state.accountId,
|
|
1256865
1256936
|
accountName: state.accountName,
|
|
1256866
1256937
|
redirectUri: state.redirectUri
|
|
@@ -1256871,7 +1256942,7 @@ app25.get("/callback", async (c) => {
|
|
|
1256871
1256942
|
throw new Error("Missing redirect URI in state \u2014 cannot match token exchange");
|
|
1256872
1256943
|
}
|
|
1256873
1256944
|
const client4 = createOAuth2Client();
|
|
1256874
|
-
|
|
1256945
|
+
logger11.info("OAuth callback: exchanging code for tokens", {
|
|
1256875
1256946
|
redirectUri,
|
|
1256876
1256947
|
clientId: client4._clientId,
|
|
1256877
1256948
|
codePrefix: code.slice(0, 20) + "..."
|
|
@@ -1256893,7 +1256964,7 @@ app25.get("/callback", async (c) => {
|
|
|
1256893
1256964
|
email: email4 ?? undefined,
|
|
1256894
1256965
|
updatedAt: now
|
|
1256895
1256966
|
}).where(eq(googleAccounts.id, state.accountId)).run();
|
|
1256896
|
-
|
|
1256967
|
+
logger11.info("Re-authorized Google account", {
|
|
1256897
1256968
|
accountId: state.accountId,
|
|
1256898
1256969
|
email: email4
|
|
1256899
1256970
|
});
|
|
@@ -1256913,7 +1256984,7 @@ app25.get("/callback", async (c) => {
|
|
|
1256913
1256984
|
createdAt: now,
|
|
1256914
1256985
|
updatedAt: now
|
|
1256915
1256986
|
}).run();
|
|
1256916
|
-
|
|
1256987
|
+
logger11.info("Created Google account", {
|
|
1256917
1256988
|
accountId,
|
|
1256918
1256989
|
name: state.accountName,
|
|
1256919
1256990
|
email: email4
|
|
@@ -1256921,7 +1256992,7 @@ app25.get("/callback", async (c) => {
|
|
|
1256921
1256992
|
}
|
|
1256922
1256993
|
return c.html("<html><body><h2>Authorization Successful</h2><p>You can close this window.</p><script>setTimeout(()=>window.close(),2000)</script></body></html>");
|
|
1256923
1256994
|
} catch (err) {
|
|
1256924
|
-
|
|
1256995
|
+
logger11.error("OAuth callback failed", {
|
|
1256925
1256996
|
error: err instanceof Error ? err.message : String(err)
|
|
1256926
1256997
|
});
|
|
1256927
1256998
|
return c.html(`<html><body><h2>Authorization Failed</h2><p>${err instanceof Error ? err.message : String(err)}</p><script>setTimeout(()=>window.close(),5000)</script></body></html>`, 500);
|
|
@@ -1256936,7 +1257007,7 @@ init_google_calendar_manager();
|
|
|
1256936
1257007
|
init_logger3();
|
|
1256937
1257008
|
init_settings();
|
|
1256938
1257009
|
init_channel_manager();
|
|
1256939
|
-
var
|
|
1257010
|
+
var logger12 = createLogger("Google:CalendarService");
|
|
1256940
1257011
|
function listGoogleAccounts() {
|
|
1256941
1257012
|
return db2.select().from(googleAccounts).all();
|
|
1256942
1257013
|
}
|
|
@@ -1256956,19 +1257027,19 @@ async function deleteGoogleAccount(id) {
|
|
|
1256956
1257027
|
}
|
|
1256957
1257028
|
db2.delete(caldavCalendars).where(eq(caldavCalendars.googleAccountId, id)).run();
|
|
1256958
1257029
|
db2.delete(googleAccounts).where(eq(googleAccounts.id, id)).run();
|
|
1256959
|
-
|
|
1257030
|
+
logger12.info("Deleted Google account", { accountId: id });
|
|
1256960
1257031
|
}
|
|
1256961
1257032
|
async function enableGoogleCalendar(id) {
|
|
1256962
1257033
|
const now = new Date().toISOString();
|
|
1256963
1257034
|
db2.update(googleAccounts).set({ calendarEnabled: true, updatedAt: now }).where(eq(googleAccounts.id, id)).run();
|
|
1256964
1257035
|
await googleCalendarManager.startAccount(id);
|
|
1256965
|
-
|
|
1257036
|
+
logger12.info("Enabled Google Calendar for account", { accountId: id });
|
|
1256966
1257037
|
}
|
|
1256967
1257038
|
async function disableGoogleCalendar(id) {
|
|
1256968
1257039
|
const now = new Date().toISOString();
|
|
1256969
1257040
|
db2.update(googleAccounts).set({ calendarEnabled: false, updatedAt: now }).where(eq(googleAccounts.id, id)).run();
|
|
1256970
1257041
|
googleCalendarManager.stopAccount(id);
|
|
1256971
|
-
|
|
1257042
|
+
logger12.info("Disabled Google Calendar for account", { accountId: id });
|
|
1256972
1257043
|
}
|
|
1256973
1257044
|
async function enableGmail(id) {
|
|
1256974
1257045
|
const now = new Date().toISOString();
|
|
@@ -1256978,7 +1257049,7 @@ async function enableGmail(id) {
|
|
|
1256978
1257049
|
updateSettingByPath("channels.email.googleAccountId", id);
|
|
1256979
1257050
|
await stopEmailChannel();
|
|
1256980
1257051
|
await startEmailChannel();
|
|
1256981
|
-
|
|
1257052
|
+
logger12.info("Enabled Gmail for account", { accountId: id });
|
|
1256982
1257053
|
}
|
|
1256983
1257054
|
async function disableGmail(id) {
|
|
1256984
1257055
|
const now = new Date().toISOString();
|
|
@@ -1256986,7 +1257057,7 @@ async function disableGmail(id) {
|
|
|
1256986
1257057
|
await stopEmailChannel();
|
|
1256987
1257058
|
updateSettingByPath("channels.email.enabled", false);
|
|
1256988
1257059
|
updateSettingByPath("channels.email.googleAccountId", null);
|
|
1256989
|
-
|
|
1257060
|
+
logger12.info("Disabled Gmail for account", { accountId: id });
|
|
1256990
1257061
|
}
|
|
1256991
1257062
|
async function syncGoogleCalendar(id) {
|
|
1256992
1257063
|
await googleCalendarManager.syncAccount(id);
|
|
@@ -1257240,6 +1257311,10 @@ app28.get("/", async (c) => {
|
|
|
1257240
1257311
|
const conversationRole = c.req.query("conversationRole") || undefined;
|
|
1257241
1257312
|
const conversationProvider = c.req.query("conversationProvider") || undefined;
|
|
1257242
1257313
|
const conversationProjectId = c.req.query("conversationProjectId") || undefined;
|
|
1257314
|
+
const gmailFrom = c.req.query("gmailFrom") || undefined;
|
|
1257315
|
+
const gmailTo = c.req.query("gmailTo") || undefined;
|
|
1257316
|
+
const gmailAfter = c.req.query("gmailAfter") || undefined;
|
|
1257317
|
+
const gmailBefore = c.req.query("gmailBefore") || undefined;
|
|
1257243
1257318
|
try {
|
|
1257244
1257319
|
const results = await search({
|
|
1257245
1257320
|
query: query.trim(),
|
|
@@ -1257254,7 +1257329,11 @@ app28.get("/", async (c) => {
|
|
|
1257254
1257329
|
memoryTags,
|
|
1257255
1257330
|
conversationRole,
|
|
1257256
1257331
|
conversationProvider,
|
|
1257257
|
-
conversationProjectId
|
|
1257332
|
+
conversationProjectId,
|
|
1257333
|
+
gmailFrom,
|
|
1257334
|
+
gmailTo,
|
|
1257335
|
+
gmailAfter,
|
|
1257336
|
+
gmailBefore
|
|
1257258
1257337
|
});
|
|
1257259
1257338
|
return c.json(results);
|
|
1257260
1257339
|
} catch (err) {
|