@knowsuchagency/fulcrum 3.2.0 → 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();
|
|
@@ -1148030,6 +1148032,7 @@ ${attachment.data}`);
|
|
|
1148030
1148032
|
const resultMsg = message;
|
|
1148031
1148033
|
if (resultMsg.subtype?.startsWith("error_")) {
|
|
1148032
1148034
|
const errors2 = resultMsg.errors || ["Unknown error"];
|
|
1148035
|
+
state.claudeSessionId = undefined;
|
|
1148033
1148036
|
yield { type: "error", data: { message: errors2.join(", ") } };
|
|
1148034
1148037
|
}
|
|
1148035
1148038
|
if (resultMsg.structured_output) {
|
|
@@ -1148042,18 +1148045,21 @@ ${attachment.data}`);
|
|
|
1148042
1148045
|
});
|
|
1148043
1148046
|
}
|
|
1148044
1148047
|
}
|
|
1148045
|
-
|
|
1148046
|
-
|
|
1148047
|
-
|
|
1148048
|
-
|
|
1148049
|
-
|
|
1148050
|
-
|
|
1148051
|
-
|
|
1148052
|
-
|
|
1148048
|
+
if (currentText.trim()) {
|
|
1148049
|
+
addMessage(sessionId, {
|
|
1148050
|
+
role: "assistant",
|
|
1148051
|
+
content: currentText,
|
|
1148052
|
+
model: MODEL_MAP[effectiveModelId],
|
|
1148053
|
+
tokensIn,
|
|
1148054
|
+
tokensOut,
|
|
1148055
|
+
sessionId
|
|
1148056
|
+
});
|
|
1148057
|
+
}
|
|
1148053
1148058
|
yield { type: "done", data: {} };
|
|
1148054
1148059
|
} catch (err) {
|
|
1148055
1148060
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1148056
1148061
|
log2.assistant.error("Assistant stream error", { sessionId, error: errorMsg });
|
|
1148062
|
+
state.claudeSessionId = undefined;
|
|
1148057
1148063
|
yield { type: "error", data: { message: errorMsg } };
|
|
1148058
1148064
|
} finally {
|
|
1148059
1148065
|
for (const tempPath of tempFiles) {
|
|
@@ -1150024,6 +1150030,38 @@ var init_opencode_channel_service = __esm(() => {
|
|
|
1150024
1150030
|
});
|
|
1150025
1150031
|
|
|
1150026
1150032
|
// server/services/channels/message-handler.ts
|
|
1150033
|
+
function recordObserverFailure() {
|
|
1150034
|
+
OBSERVER_CIRCUIT_BREAKER.failureCount++;
|
|
1150035
|
+
if (OBSERVER_CIRCUIT_BREAKER.failureCount >= OBSERVER_CIRCUIT_BREAKER.failureThreshold) {
|
|
1150036
|
+
if (OBSERVER_CIRCUIT_BREAKER.state !== "open") {
|
|
1150037
|
+
log2.messaging.warn("Observer circuit breaker OPEN \u2014 pausing observe-only processing", {
|
|
1150038
|
+
failures: OBSERVER_CIRCUIT_BREAKER.failureCount,
|
|
1150039
|
+
cooldownMs: OBSERVER_CIRCUIT_BREAKER.cooldownMs
|
|
1150040
|
+
});
|
|
1150041
|
+
}
|
|
1150042
|
+
OBSERVER_CIRCUIT_BREAKER.state = "open";
|
|
1150043
|
+
OBSERVER_CIRCUIT_BREAKER.nextProbeAt = Date.now() + OBSERVER_CIRCUIT_BREAKER.cooldownMs;
|
|
1150044
|
+
OBSERVER_CIRCUIT_BREAKER.cooldownMs = Math.min(OBSERVER_CIRCUIT_BREAKER.cooldownMs * 2, OBSERVER_CIRCUIT_BREAKER.maxCooldownMs);
|
|
1150045
|
+
}
|
|
1150046
|
+
}
|
|
1150047
|
+
function recordObserverSuccess() {
|
|
1150048
|
+
if (OBSERVER_CIRCUIT_BREAKER.state === "open") {
|
|
1150049
|
+
log2.messaging.info("Observer circuit breaker CLOSED \u2014 resuming normal processing", {
|
|
1150050
|
+
previousFailures: OBSERVER_CIRCUIT_BREAKER.failureCount
|
|
1150051
|
+
});
|
|
1150052
|
+
}
|
|
1150053
|
+
OBSERVER_CIRCUIT_BREAKER.failureCount = 0;
|
|
1150054
|
+
OBSERVER_CIRCUIT_BREAKER.state = "closed";
|
|
1150055
|
+
OBSERVER_CIRCUIT_BREAKER.nextProbeAt = 0;
|
|
1150056
|
+
OBSERVER_CIRCUIT_BREAKER.cooldownMs = 60000;
|
|
1150057
|
+
}
|
|
1150058
|
+
function isObserverCircuitOpen() {
|
|
1150059
|
+
if (OBSERVER_CIRCUIT_BREAKER.state !== "open")
|
|
1150060
|
+
return false;
|
|
1150061
|
+
if (Date.now() >= OBSERVER_CIRCUIT_BREAKER.nextProbeAt)
|
|
1150062
|
+
return false;
|
|
1150063
|
+
return true;
|
|
1150064
|
+
}
|
|
1150027
1150065
|
async function handleIncomingMessage(msg) {
|
|
1150028
1150066
|
const content = msg.content.trim();
|
|
1150029
1150067
|
const isObserveOnly = msg.metadata?.observeOnly === true;
|
|
@@ -1150271,12 +1150309,16 @@ function splitMessage(content, maxLength) {
|
|
|
1150271
1150309
|
return parts;
|
|
1150272
1150310
|
}
|
|
1150273
1150311
|
async function processObserveOnlyMessage(msg) {
|
|
1150312
|
+
if (isObserverCircuitOpen()) {
|
|
1150313
|
+
return;
|
|
1150314
|
+
}
|
|
1150274
1150315
|
const observeSessionKey = `observe-${msg.connectionId}`;
|
|
1150275
1150316
|
const { session: session3 } = getOrCreateSession(msg.connectionId, observeSessionKey, "Observer", undefined, msg.channelType);
|
|
1150276
1150317
|
const settings = getSettings();
|
|
1150277
1150318
|
const observerProvider = settings.assistant.observerProvider ?? settings.assistant.provider;
|
|
1150278
1150319
|
if (observerProvider === "opencode") {
|
|
1150279
1150320
|
try {
|
|
1150321
|
+
let hadError = false;
|
|
1150280
1150322
|
const stream2 = _deps.streamOpencodeObserverMessage(session3.id, msg.content, {
|
|
1150281
1150323
|
channelType: msg.channelType,
|
|
1150282
1150324
|
senderId: msg.senderId,
|
|
@@ -1150284,15 +1150326,21 @@ async function processObserveOnlyMessage(msg) {
|
|
|
1150284
1150326
|
});
|
|
1150285
1150327
|
for await (const event of stream2) {
|
|
1150286
1150328
|
if (event.type === "error") {
|
|
1150329
|
+
hadError = true;
|
|
1150287
1150330
|
const errorMsg = event.data.message;
|
|
1150288
1150331
|
log2.messaging.error("Error in OpenCode observe-only processing", { error: errorMsg });
|
|
1150289
1150332
|
}
|
|
1150290
1150333
|
}
|
|
1150334
|
+
if (hadError)
|
|
1150335
|
+
recordObserverFailure();
|
|
1150336
|
+
else
|
|
1150337
|
+
recordObserverSuccess();
|
|
1150291
1150338
|
} catch (err) {
|
|
1150292
1150339
|
log2.messaging.error("Error processing observe-only message via OpenCode", {
|
|
1150293
1150340
|
connectionId: msg.connectionId,
|
|
1150294
1150341
|
error: String(err)
|
|
1150295
1150342
|
});
|
|
1150343
|
+
recordObserverFailure();
|
|
1150296
1150344
|
}
|
|
1150297
1150345
|
return;
|
|
1150298
1150346
|
}
|
|
@@ -1150308,6 +1150356,7 @@ async function processObserveOnlyMessage(msg) {
|
|
|
1150308
1150356
|
};
|
|
1150309
1150357
|
const systemPrompt = getObserveOnlySystemPrompt(msg.channelType, context);
|
|
1150310
1150358
|
try {
|
|
1150359
|
+
let hadError = false;
|
|
1150311
1150360
|
const observerModelId = settings.assistant.observerModel;
|
|
1150312
1150361
|
const stream2 = _deps.streamMessage(session3.id, msg.content, {
|
|
1150313
1150362
|
systemPromptAdditions: systemPrompt,
|
|
@@ -1150316,18 +1150365,24 @@ async function processObserveOnlyMessage(msg) {
|
|
|
1150316
1150365
|
});
|
|
1150317
1150366
|
for await (const event of stream2) {
|
|
1150318
1150367
|
if (event.type === "error") {
|
|
1150368
|
+
hadError = true;
|
|
1150319
1150369
|
const errorMsg = event.data.message;
|
|
1150320
1150370
|
log2.messaging.error("Error in observe-only message processing", { error: errorMsg });
|
|
1150321
1150371
|
}
|
|
1150322
1150372
|
}
|
|
1150373
|
+
if (hadError)
|
|
1150374
|
+
recordObserverFailure();
|
|
1150375
|
+
else
|
|
1150376
|
+
recordObserverSuccess();
|
|
1150323
1150377
|
} catch (err) {
|
|
1150324
1150378
|
log2.messaging.error("Error processing observe-only message", {
|
|
1150325
1150379
|
connectionId: msg.connectionId,
|
|
1150326
1150380
|
error: String(err)
|
|
1150327
1150381
|
});
|
|
1150382
|
+
recordObserverFailure();
|
|
1150328
1150383
|
}
|
|
1150329
1150384
|
}
|
|
1150330
|
-
var _deps, SLACK_RESPONSE_SCHEMA, COMMANDS;
|
|
1150385
|
+
var _deps, SLACK_RESPONSE_SCHEMA, OBSERVER_CIRCUIT_BREAKER, COMMANDS;
|
|
1150331
1150386
|
var init_message_handler = __esm(() => {
|
|
1150332
1150387
|
init_logger3();
|
|
1150333
1150388
|
init_channel_manager();
|
|
@@ -1150354,6 +1150409,14 @@ var init_message_handler = __esm(() => {
|
|
|
1150354
1150409
|
},
|
|
1150355
1150410
|
required: ["body"]
|
|
1150356
1150411
|
};
|
|
1150412
|
+
OBSERVER_CIRCUIT_BREAKER = {
|
|
1150413
|
+
failureCount: 0,
|
|
1150414
|
+
failureThreshold: 3,
|
|
1150415
|
+
state: "closed",
|
|
1150416
|
+
nextProbeAt: 0,
|
|
1150417
|
+
cooldownMs: 60000,
|
|
1150418
|
+
maxCooldownMs: 600000
|
|
1150419
|
+
};
|
|
1150357
1150420
|
COMMANDS = {
|
|
1150358
1150421
|
RESET: ["/reset", "/new", "/clear"],
|
|
1150359
1150422
|
HELP: ["/help", "/?"],
|
|
@@ -1188324,13 +1188387,13 @@ var require_core = __commonJS((exports) => {
|
|
|
1188324
1188387
|
return metaOpts;
|
|
1188325
1188388
|
}
|
|
1188326
1188389
|
var noLogs = { log() {}, warn() {}, error() {} };
|
|
1188327
|
-
function getLogger(
|
|
1188328
|
-
if (
|
|
1188390
|
+
function getLogger(logger7) {
|
|
1188391
|
+
if (logger7 === false)
|
|
1188329
1188392
|
return noLogs;
|
|
1188330
|
-
if (
|
|
1188393
|
+
if (logger7 === undefined)
|
|
1188331
1188394
|
return console;
|
|
1188332
|
-
if (
|
|
1188333
|
-
return
|
|
1188395
|
+
if (logger7.log && logger7.warn && logger7.error)
|
|
1188396
|
+
return logger7;
|
|
1188334
1188397
|
throw new Error("logger must implement log, warn and error methods");
|
|
1188335
1188398
|
}
|
|
1188336
1188399
|
var KEYWORD_NAME = /^[a-z_$][a-z0-9_$:-]*$/i;
|
|
@@ -1193957,13 +1194020,13 @@ var require_core3 = __commonJS((exports) => {
|
|
|
1193957
1194020
|
return metaOpts;
|
|
1193958
1194021
|
}
|
|
1193959
1194022
|
var noLogs = { log() {}, warn() {}, error() {} };
|
|
1193960
|
-
function getLogger(
|
|
1193961
|
-
if (
|
|
1194023
|
+
function getLogger(logger7) {
|
|
1194024
|
+
if (logger7 === false)
|
|
1193962
1194025
|
return noLogs;
|
|
1193963
|
-
if (
|
|
1194026
|
+
if (logger7 === undefined)
|
|
1193964
1194027
|
return console;
|
|
1193965
|
-
if (
|
|
1193966
|
-
return
|
|
1194028
|
+
if (logger7.log && logger7.warn && logger7.error)
|
|
1194029
|
+
return logger7;
|
|
1193967
1194030
|
throw new Error("logger must implement log, warn and error methods");
|
|
1193968
1194031
|
}
|
|
1193969
1194032
|
var KEYWORD_NAME = /^[a-z_$][a-z0-9_$:-]*$/i;
|
|
@@ -1200355,10 +1200418,10 @@ class CaldavAccountManager {
|
|
|
1200355
1200418
|
}
|
|
1200356
1200419
|
async startAll() {
|
|
1200357
1200420
|
const accounts = db2.select().from(caldavAccounts).where(eq(caldavAccounts.enabled, true)).all();
|
|
1200358
|
-
|
|
1200421
|
+
logger7.info("Starting all CalDAV accounts", { count: accounts.length });
|
|
1200359
1200422
|
for (const account2 of accounts) {
|
|
1200360
1200423
|
await this.startAccount(account2.id).catch((err) => {
|
|
1200361
|
-
|
|
1200424
|
+
logger7.error("Failed to start account", {
|
|
1200362
1200425
|
accountId: account2.id,
|
|
1200363
1200426
|
name: account2.name,
|
|
1200364
1200427
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -1200371,7 +1200434,7 @@ class CaldavAccountManager {
|
|
|
1200371
1200434
|
this.stopAccount(accountId);
|
|
1200372
1200435
|
}
|
|
1200373
1200436
|
this.connections.clear();
|
|
1200374
|
-
|
|
1200437
|
+
logger7.info("All CalDAV accounts stopped");
|
|
1200375
1200438
|
}
|
|
1200376
1200439
|
async startAccount(accountId) {
|
|
1200377
1200440
|
const account2 = db2.select().from(caldavAccounts).where(eq(caldavAccounts.id, accountId)).get();
|
|
@@ -1200379,7 +1200442,7 @@ class CaldavAccountManager {
|
|
|
1200379
1200442
|
throw new Error(`Account not found: ${accountId}`);
|
|
1200380
1200443
|
}
|
|
1200381
1200444
|
if (!account2.enabled) {
|
|
1200382
|
-
|
|
1200445
|
+
logger7.info("Account is disabled, skipping", { accountId, name: account2.name });
|
|
1200383
1200446
|
return;
|
|
1200384
1200447
|
}
|
|
1200385
1200448
|
this.stopAccount(accountId);
|
|
@@ -1200395,11 +1200458,11 @@ class CaldavAccountManager {
|
|
|
1200395
1200458
|
try {
|
|
1200396
1200459
|
conn.client = await this.connect(account2);
|
|
1200397
1200460
|
this.scheduleSync(accountId, account2.syncIntervalMinutes ?? 15);
|
|
1200398
|
-
|
|
1200461
|
+
logger7.info("Started CalDAV account", { accountId, name: account2.name });
|
|
1200399
1200462
|
} catch (err) {
|
|
1200400
1200463
|
conn.lastSyncError = err instanceof Error ? err.message : String(err);
|
|
1200401
1200464
|
this.updateAccountError(accountId, conn.lastSyncError);
|
|
1200402
|
-
|
|
1200465
|
+
logger7.error("Failed to connect account", { accountId, error: conn.lastSyncError });
|
|
1200403
1200466
|
this.scheduleRetry(accountId);
|
|
1200404
1200467
|
}
|
|
1200405
1200468
|
}
|
|
@@ -1200441,7 +1200504,7 @@ class CaldavAccountManager {
|
|
|
1200441
1200504
|
for (const [accountId, conn] of this.connections) {
|
|
1200442
1200505
|
if (conn.client && !conn.isSyncing) {
|
|
1200443
1200506
|
promises.push(this.syncAccount(accountId).catch((err) => {
|
|
1200444
|
-
|
|
1200507
|
+
logger7.error("Sync failed for account", {
|
|
1200445
1200508
|
accountId,
|
|
1200446
1200509
|
error: err instanceof Error ? err.message : String(err)
|
|
1200447
1200510
|
});
|
|
@@ -1200501,7 +1200564,7 @@ class CaldavAccountManager {
|
|
|
1200501
1200564
|
defaultAccountType: "caldav"
|
|
1200502
1200565
|
});
|
|
1200503
1200566
|
await client4.login();
|
|
1200504
|
-
|
|
1200567
|
+
logger7.info("Connected to CalDAV server (Basic)", { accountId: account2.id, serverUrl: account2.serverUrl });
|
|
1200505
1200568
|
return client4;
|
|
1200506
1200569
|
}
|
|
1200507
1200570
|
async connectOAuth(account2) {
|
|
@@ -1200536,9 +1200599,9 @@ class CaldavAccountManager {
|
|
|
1200536
1200599
|
credentials.expiration = newTokens.expiration;
|
|
1200537
1200600
|
try {
|
|
1200538
1200601
|
db2.update(caldavAccounts).set({ oauthTokens: newTokens, updatedAt: new Date().toISOString() }).where(eq(caldavAccounts.id, accountId)).run();
|
|
1200539
|
-
|
|
1200602
|
+
logger7.info("Persisted refreshed OAuth tokens", { accountId });
|
|
1200540
1200603
|
} catch (err) {
|
|
1200541
|
-
|
|
1200604
|
+
logger7.error("Failed to persist refreshed OAuth tokens", {
|
|
1200542
1200605
|
accountId,
|
|
1200543
1200606
|
error: err instanceof Error ? err.message : String(err)
|
|
1200544
1200607
|
});
|
|
@@ -1200549,7 +1200612,7 @@ class CaldavAccountManager {
|
|
|
1200549
1200612
|
defaultAccountType: "caldav"
|
|
1200550
1200613
|
});
|
|
1200551
1200614
|
await client4.login();
|
|
1200552
|
-
|
|
1200615
|
+
logger7.info("Connected to CalDAV server (Google OAuth)", { accountId: account2.id, serverUrl: account2.serverUrl });
|
|
1200553
1200616
|
return client4;
|
|
1200554
1200617
|
}
|
|
1200555
1200618
|
scheduleSync(accountId, intervalMinutes) {
|
|
@@ -1200561,7 +1200624,7 @@ class CaldavAccountManager {
|
|
|
1200561
1200624
|
}
|
|
1200562
1200625
|
conn.syncInterval = setInterval(() => {
|
|
1200563
1200626
|
this.syncAccount(accountId).catch((err) => {
|
|
1200564
|
-
|
|
1200627
|
+
logger7.error("Periodic sync failed", {
|
|
1200565
1200628
|
accountId,
|
|
1200566
1200629
|
error: err instanceof Error ? err.message : String(err)
|
|
1200567
1200630
|
});
|
|
@@ -1200574,7 +1200637,7 @@ class CaldavAccountManager {
|
|
|
1200574
1200637
|
return;
|
|
1200575
1200638
|
conn.retryCount++;
|
|
1200576
1200639
|
const delay2 = Math.min(1000 * Math.pow(2, conn.retryCount), MAX_RETRY_DELAY);
|
|
1200577
|
-
|
|
1200640
|
+
logger7.info("Scheduling account retry", { accountId, retryCount: conn.retryCount, delayMs: delay2 });
|
|
1200578
1200641
|
setTimeout(async () => {
|
|
1200579
1200642
|
const account2 = db2.select().from(caldavAccounts).where(eq(caldavAccounts.id, accountId)).get();
|
|
1200580
1200643
|
if (!account2 || !account2.enabled)
|
|
@@ -1200590,7 +1200653,7 @@ class CaldavAccountManager {
|
|
|
1200590
1200653
|
} catch (err) {
|
|
1200591
1200654
|
conn.lastSyncError = err instanceof Error ? err.message : String(err);
|
|
1200592
1200655
|
this.updateAccountError(accountId, conn.lastSyncError);
|
|
1200593
|
-
|
|
1200656
|
+
logger7.error("Account retry failed", { accountId, error: conn.lastSyncError });
|
|
1200594
1200657
|
this.scheduleRetry(accountId);
|
|
1200595
1200658
|
}
|
|
1200596
1200659
|
}, delay2);
|
|
@@ -1200648,10 +1200711,10 @@ class CaldavAccountManager {
|
|
|
1200648
1200711
|
if (!seenUrls.has(local.remoteUrl)) {
|
|
1200649
1200712
|
db2.delete(caldavEvents2).where(eq(caldavEvents2.calendarId, local.id)).run();
|
|
1200650
1200713
|
db2.delete(caldavCalendars).where(eq(caldavCalendars.id, local.id)).run();
|
|
1200651
|
-
|
|
1200714
|
+
logger7.info("Removed deleted calendar", { displayName: local.displayName, accountId });
|
|
1200652
1200715
|
}
|
|
1200653
1200716
|
}
|
|
1200654
|
-
|
|
1200717
|
+
logger7.info("Account sync complete", { accountId, calendars: remoteCalendars.length });
|
|
1200655
1200718
|
}
|
|
1200656
1200719
|
async syncCalendarEvents(calendarId, remoteCal, client4) {
|
|
1200657
1200720
|
const { caldavEvents: caldavEvents2 } = await Promise.resolve().then(() => (init_db2(), exports_db));
|
|
@@ -1200718,13 +1200781,13 @@ class CaldavAccountManager {
|
|
|
1200718
1200781
|
}
|
|
1200719
1200782
|
}
|
|
1200720
1200783
|
}
|
|
1200721
|
-
var
|
|
1200784
|
+
var logger7, GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token", MAX_RETRY_DELAY, accountManager;
|
|
1200722
1200785
|
var init_caldav_account_manager = __esm(() => {
|
|
1200723
1200786
|
init_tsdav_esm();
|
|
1200724
1200787
|
init_drizzle_orm();
|
|
1200725
1200788
|
init_db2();
|
|
1200726
1200789
|
init_logger3();
|
|
1200727
|
-
|
|
1200790
|
+
logger7 = createLogger("CalDAV:AccountManager");
|
|
1200728
1200791
|
MAX_RETRY_DELAY = 5 * 60 * 1000;
|
|
1200729
1200792
|
accountManager = new CaldavAccountManager;
|
|
1200730
1200793
|
});
|
|
@@ -1200747,7 +1200810,7 @@ class GoogleCalendarManager {
|
|
|
1200747
1200810
|
try {
|
|
1200748
1200811
|
await this.syncAccount(accountId);
|
|
1200749
1200812
|
} catch (err) {
|
|
1200750
|
-
|
|
1200813
|
+
logger8.error("Initial Google Calendar sync failed", {
|
|
1200751
1200814
|
accountId,
|
|
1200752
1200815
|
error: err instanceof Error ? err.message : String(err)
|
|
1200753
1200816
|
});
|
|
@@ -1200755,13 +1200818,13 @@ class GoogleCalendarManager {
|
|
|
1200755
1200818
|
const intervalMs = (account2.syncIntervalMinutes ?? 15) * 60 * 1000;
|
|
1200756
1200819
|
conn.syncInterval = setInterval(() => {
|
|
1200757
1200820
|
this.syncAccount(accountId).catch((err) => {
|
|
1200758
|
-
|
|
1200821
|
+
logger8.error("Periodic Google Calendar sync failed", {
|
|
1200759
1200822
|
accountId,
|
|
1200760
1200823
|
error: err instanceof Error ? err.message : String(err)
|
|
1200761
1200824
|
});
|
|
1200762
1200825
|
});
|
|
1200763
1200826
|
}, intervalMs);
|
|
1200764
|
-
|
|
1200827
|
+
logger8.info("Started Google Calendar account", { accountId, name: account2.name });
|
|
1200765
1200828
|
}
|
|
1200766
1200829
|
stopAccount(accountId) {
|
|
1200767
1200830
|
const conn = this.connections.get(accountId);
|
|
@@ -1200779,10 +1200842,10 @@ class GoogleCalendarManager {
|
|
|
1200779
1200842
|
}
|
|
1200780
1200843
|
async startAll() {
|
|
1200781
1200844
|
const accounts = db2.select().from(googleAccounts).where(eq(googleAccounts.calendarEnabled, true)).all();
|
|
1200782
|
-
|
|
1200845
|
+
logger8.info("Starting Google Calendar accounts", { count: accounts.length });
|
|
1200783
1200846
|
for (const account2 of accounts) {
|
|
1200784
1200847
|
await this.startAccount(account2.id).catch((err) => {
|
|
1200785
|
-
|
|
1200848
|
+
logger8.error("Failed to start Google Calendar account", {
|
|
1200786
1200849
|
accountId: account2.id,
|
|
1200787
1200850
|
error: err instanceof Error ? err.message : String(err)
|
|
1200788
1200851
|
});
|
|
@@ -1200844,7 +1200907,7 @@ class GoogleCalendarManager {
|
|
|
1200844
1200907
|
if (!seenRemoteUrls.has(local.remoteUrl)) {
|
|
1200845
1200908
|
db2.delete(caldavEvents).where(eq(caldavEvents.calendarId, local.id)).run();
|
|
1200846
1200909
|
db2.delete(caldavCalendars).where(eq(caldavCalendars.id, local.id)).run();
|
|
1200847
|
-
|
|
1200910
|
+
logger8.info("Removed deleted Google calendar", {
|
|
1200848
1200911
|
displayName: local.displayName,
|
|
1200849
1200912
|
accountId
|
|
1200850
1200913
|
});
|
|
@@ -1200858,7 +1200921,7 @@ class GoogleCalendarManager {
|
|
|
1200858
1200921
|
if (conn) {
|
|
1200859
1200922
|
conn.lastSyncError = null;
|
|
1200860
1200923
|
}
|
|
1200861
|
-
|
|
1200924
|
+
logger8.info("Google Calendar sync complete", {
|
|
1200862
1200925
|
accountId,
|
|
1200863
1200926
|
calendars: remoteCalendars.length
|
|
1200864
1200927
|
});
|
|
@@ -1201075,14 +1201138,14 @@ class GoogleCalendarManager {
|
|
|
1201075
1201138
|
return this.connections.get(accountId)?.lastSyncError ?? null;
|
|
1201076
1201139
|
}
|
|
1201077
1201140
|
}
|
|
1201078
|
-
var import_googleapis3,
|
|
1201141
|
+
var import_googleapis3, logger8, googleCalendarManager;
|
|
1201079
1201142
|
var init_google_calendar_manager = __esm(() => {
|
|
1201080
1201143
|
init_drizzle_orm();
|
|
1201081
1201144
|
init_db2();
|
|
1201082
1201145
|
init_google_oauth();
|
|
1201083
1201146
|
init_logger3();
|
|
1201084
1201147
|
import_googleapis3 = __toESM(require_src12(), 1);
|
|
1201085
|
-
|
|
1201148
|
+
logger8 = createLogger("Google:Calendar");
|
|
1201086
1201149
|
googleCalendarManager = new GoogleCalendarManager;
|
|
1201087
1201150
|
});
|
|
1201088
1201151
|
|
|
@@ -1201096,12 +1201159,12 @@ async function executeAllRules() {
|
|
|
1201096
1201159
|
const rules = db2.select().from(caldavCopyRules).where(eq(caldavCopyRules.enabled, true)).all();
|
|
1201097
1201160
|
if (rules.length === 0)
|
|
1201098
1201161
|
return;
|
|
1201099
|
-
|
|
1201162
|
+
logger9.info("Executing copy rules", { count: rules.length });
|
|
1201100
1201163
|
for (const rule of rules) {
|
|
1201101
1201164
|
try {
|
|
1201102
1201165
|
await executeSingleRule(rule.id);
|
|
1201103
1201166
|
} catch (err) {
|
|
1201104
|
-
|
|
1201167
|
+
logger9.error("Copy rule failed", {
|
|
1201105
1201168
|
ruleId: rule.id,
|
|
1201106
1201169
|
error: err instanceof Error ? err.message : String(err)
|
|
1201107
1201170
|
});
|
|
@@ -1201115,7 +1201178,7 @@ async function executeSingleRule(ruleId) {
|
|
|
1201115
1201178
|
const sourceCal = db2.select().from(caldavCalendars).where(eq(caldavCalendars.id, rule.sourceCalendarId)).get();
|
|
1201116
1201179
|
const destCal = db2.select().from(caldavCalendars).where(eq(caldavCalendars.id, rule.destCalendarId)).get();
|
|
1201117
1201180
|
if (!sourceCal || !destCal) {
|
|
1201118
|
-
|
|
1201181
|
+
logger9.warn("Copy rule references missing calendar", {
|
|
1201119
1201182
|
ruleId,
|
|
1201120
1201183
|
sourceExists: !!sourceCal,
|
|
1201121
1201184
|
destExists: !!destCal
|
|
@@ -1201125,7 +1201188,7 @@ async function executeSingleRule(ruleId) {
|
|
|
1201125
1201188
|
const isGoogleDest = !!destCal.googleAccountId;
|
|
1201126
1201189
|
const destClient = destCal.accountId ? accountManager.getClient(destCal.accountId) : null;
|
|
1201127
1201190
|
if (!isGoogleDest && !destClient) {
|
|
1201128
|
-
|
|
1201191
|
+
logger9.warn("Destination account not connected", {
|
|
1201129
1201192
|
ruleId,
|
|
1201130
1201193
|
destCalendarId: destCal.id,
|
|
1201131
1201194
|
destAccountId: destCal.accountId
|
|
@@ -1201211,7 +1201274,7 @@ async function executeSingleRule(ruleId) {
|
|
|
1201211
1201274
|
}).run();
|
|
1201212
1201275
|
created++;
|
|
1201213
1201276
|
} catch (err) {
|
|
1201214
|
-
|
|
1201277
|
+
logger9.error("Failed to copy event", {
|
|
1201215
1201278
|
ruleId,
|
|
1201216
1201279
|
sourceEventId: sourceEvent.id,
|
|
1201217
1201280
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -1201275,7 +1201338,7 @@ async function executeSingleRule(ruleId) {
|
|
|
1201275
1201338
|
db2.update(caldavCopiedEvents).set({ sourceEtag: sourceEvent.etag, updatedAt: now }).where(eq(caldavCopiedEvents.id, existingCopy.id)).run();
|
|
1201276
1201339
|
updated++;
|
|
1201277
1201340
|
} catch (err) {
|
|
1201278
|
-
|
|
1201341
|
+
logger9.error("Failed to update copied event", {
|
|
1201279
1201342
|
ruleId,
|
|
1201280
1201343
|
sourceEventId: sourceEvent.id,
|
|
1201281
1201344
|
destEventId: existingCopy.destEventId,
|
|
@@ -1201286,11 +1201349,11 @@ async function executeSingleRule(ruleId) {
|
|
|
1201286
1201349
|
}
|
|
1201287
1201350
|
db2.update(caldavCopyRules).set({ lastExecutedAt: now, updatedAt: now }).where(eq(caldavCopyRules.id, ruleId)).run();
|
|
1201288
1201351
|
if (created > 0 || updated > 0) {
|
|
1201289
|
-
|
|
1201352
|
+
logger9.info("Copy rule executed", { ruleId, created, updated });
|
|
1201290
1201353
|
}
|
|
1201291
1201354
|
return { created, updated };
|
|
1201292
1201355
|
}
|
|
1201293
|
-
var
|
|
1201356
|
+
var logger9;
|
|
1201294
1201357
|
var init_copy_engine = __esm(() => {
|
|
1201295
1201358
|
init_drizzle_orm();
|
|
1201296
1201359
|
init_db2();
|
|
@@ -1201298,7 +1201361,7 @@ var init_copy_engine = __esm(() => {
|
|
|
1201298
1201361
|
init_ical_helpers();
|
|
1201299
1201362
|
init_caldav_account_manager();
|
|
1201300
1201363
|
init_google_calendar_manager();
|
|
1201301
|
-
|
|
1201364
|
+
logger9 = createLogger("CalDAV:CopyEngine");
|
|
1201302
1201365
|
});
|
|
1201303
1201366
|
|
|
1201304
1201367
|
// node_modules/@hono/node-server/dist/index.mjs
|
|
@@ -1208290,6 +1208353,8 @@ async function updateTaskStatus(taskId, newStatus, newPosition) {
|
|
|
1208290
1208353
|
// server/services/search-service.ts
|
|
1208291
1208354
|
init_db2();
|
|
1208292
1208355
|
init_drizzle_orm();
|
|
1208356
|
+
init_schema();
|
|
1208357
|
+
init_logger3();
|
|
1208293
1208358
|
var ALL_ENTITIES = ["tasks", "projects", "messages", "events", "memories", "conversations"];
|
|
1208294
1208359
|
async function search(options) {
|
|
1208295
1208360
|
const entities = options.entities?.length ? options.entities : ALL_ENTITIES;
|
|
@@ -1208308,6 +1208373,8 @@ async function search(options) {
|
|
|
1208308
1208373
|
return searchMemories2(options.query, { tags: options.memoryTags }, limit);
|
|
1208309
1208374
|
case "conversations":
|
|
1208310
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);
|
|
1208311
1208378
|
}
|
|
1208312
1208379
|
});
|
|
1208313
1208380
|
const resultSets = await Promise.all(searches);
|
|
@@ -1208511,6 +1208578,55 @@ async function searchConversations(query, filters, limit) {
|
|
|
1208511
1208578
|
}
|
|
1208512
1208579
|
}));
|
|
1208513
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
|
+
}
|
|
1208514
1208630
|
function reindexTaskFTS(taskId) {
|
|
1208515
1208631
|
db2.run(sql`
|
|
1208516
1208632
|
UPDATE tasks SET updated_at = updated_at WHERE id = ${taskId}
|
|
@@ -1212895,20 +1213011,20 @@ var VERSION4 = "7.0.6";
|
|
|
1212895
1213011
|
var noop2 = () => {};
|
|
1212896
1213012
|
var consoleWarn = console.warn.bind(console);
|
|
1212897
1213013
|
var consoleError = console.error.bind(console);
|
|
1212898
|
-
function createLogger2(
|
|
1212899
|
-
if (typeof
|
|
1212900
|
-
|
|
1213014
|
+
function createLogger2(logger7 = {}) {
|
|
1213015
|
+
if (typeof logger7.debug !== "function") {
|
|
1213016
|
+
logger7.debug = noop2;
|
|
1212901
1213017
|
}
|
|
1212902
|
-
if (typeof
|
|
1212903
|
-
|
|
1213018
|
+
if (typeof logger7.info !== "function") {
|
|
1213019
|
+
logger7.info = noop2;
|
|
1212904
1213020
|
}
|
|
1212905
|
-
if (typeof
|
|
1212906
|
-
|
|
1213021
|
+
if (typeof logger7.warn !== "function") {
|
|
1213022
|
+
logger7.warn = consoleWarn;
|
|
1212907
1213023
|
}
|
|
1212908
|
-
if (typeof
|
|
1212909
|
-
|
|
1213024
|
+
if (typeof logger7.error !== "function") {
|
|
1213025
|
+
logger7.error = consoleError;
|
|
1212910
1213026
|
}
|
|
1212911
|
-
return
|
|
1213027
|
+
return logger7;
|
|
1212912
1213028
|
}
|
|
1212913
1213029
|
var userAgentTrail = `octokit-core.js/${VERSION4} ${getUserAgent2()}`;
|
|
1212914
1213030
|
|
|
@@ -1251751,7 +1251867,7 @@ var toolRegistry = [
|
|
|
1251751
1251867
|
},
|
|
1251752
1251868
|
{
|
|
1251753
1251869
|
name: "create_calendar_event",
|
|
1251754
|
-
description: "Create a new calendar event on a CalDAV calendar",
|
|
1251870
|
+
description: "Create a new calendar event on a CalDAV or Google calendar.",
|
|
1251755
1251871
|
category: "caldav",
|
|
1251756
1251872
|
keywords: ["calendar", "event", "create", "new", "schedule", "appointment", "meeting"],
|
|
1251757
1251873
|
defer_loading: true
|
|
@@ -1251793,9 +1251909,9 @@ var toolRegistry = [
|
|
|
1251793
1251909
|
},
|
|
1251794
1251910
|
{
|
|
1251795
1251911
|
name: "search",
|
|
1251796
|
-
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",
|
|
1251797
1251913
|
category: "core",
|
|
1251798
|
-
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"],
|
|
1251799
1251915
|
defer_loading: false
|
|
1251800
1251916
|
},
|
|
1251801
1251917
|
{
|
|
@@ -1253584,9 +1253700,9 @@ var registerMessagingTools = (server2, client3) => {
|
|
|
1253584
1253700
|
};
|
|
1253585
1253701
|
|
|
1253586
1253702
|
// cli/src/mcp/tools/search.ts
|
|
1253587
|
-
var EntityTypeSchema = exports_external.enum(["tasks", "projects", "messages", "events", "memories", "conversations"]);
|
|
1253703
|
+
var EntityTypeSchema = exports_external.enum(["tasks", "projects", "messages", "events", "memories", "conversations", "gmail"]);
|
|
1253588
1253704
|
var registerSearchTools = (server2, client3) => {
|
|
1253589
|
-
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).', {
|
|
1253590
1253706
|
query: exports_external.string().describe('FTS5 search query. Supports: AND, OR, NOT operators, "quoted phrases", prefix* matching. Example: "kubernetes deployment" OR k8s'),
|
|
1253591
1253707
|
entities: exports_external.optional(exports_external.array(EntityTypeSchema)).describe("Entity types to search. Defaults to all: tasks, projects, messages, events, memories, conversations"),
|
|
1253592
1253708
|
limit: exports_external.optional(exports_external.number()).describe("Maximum results per entity type (default: 10)"),
|
|
@@ -1253599,8 +1253715,12 @@ var registerSearchTools = (server2, client3) => {
|
|
|
1253599
1253715
|
memoryTags: exports_external.optional(exports_external.array(exports_external.string())).describe("Filter memories by tags"),
|
|
1253600
1253716
|
conversationRole: exports_external.optional(exports_external.string()).describe('Filter conversations by role (e.g., "user", "assistant")'),
|
|
1253601
1253717
|
conversationProvider: exports_external.optional(exports_external.string()).describe('Filter conversations by provider (e.g., "claude", "opencode")'),
|
|
1253602
|
-
conversationProjectId: exports_external.optional(exports_external.string()).describe("Filter conversations by project ID")
|
|
1253603
|
-
|
|
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 }) => {
|
|
1253604
1253724
|
try {
|
|
1253605
1253725
|
const results = await client3.search({
|
|
1253606
1253726
|
query,
|
|
@@ -1253615,7 +1253735,11 @@ var registerSearchTools = (server2, client3) => {
|
|
|
1253615
1253735
|
memoryTags,
|
|
1253616
1253736
|
conversationRole,
|
|
1253617
1253737
|
conversationProvider,
|
|
1253618
|
-
conversationProjectId
|
|
1253738
|
+
conversationProjectId,
|
|
1253739
|
+
gmailFrom,
|
|
1253740
|
+
gmailTo,
|
|
1253741
|
+
gmailAfter,
|
|
1253742
|
+
gmailBefore
|
|
1253619
1253743
|
});
|
|
1253620
1253744
|
return formatSuccess(results);
|
|
1253621
1253745
|
} catch (err) {
|
|
@@ -1254455,6 +1254579,14 @@ class FulcrumClient {
|
|
|
1254455
1254579
|
params.set("conversationProvider", input.conversationProvider);
|
|
1254456
1254580
|
if (input.conversationProjectId)
|
|
1254457
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);
|
|
1254458
1254590
|
return this.fetch(`/api/search?${params.toString()}`);
|
|
1254459
1254591
|
}
|
|
1254460
1254592
|
}
|
|
@@ -1254470,7 +1254602,7 @@ mcpRoutes.all("/", async (c) => {
|
|
|
1254470
1254602
|
});
|
|
1254471
1254603
|
const server2 = new McpServer({
|
|
1254472
1254604
|
name: "fulcrum",
|
|
1254473
|
-
version: "3.
|
|
1254605
|
+
version: "3.3.0"
|
|
1254474
1254606
|
});
|
|
1254475
1254607
|
const client3 = new FulcrumClient(`http://localhost:${port}`);
|
|
1254476
1254608
|
registerTools(server2, client3);
|
|
@@ -1255793,7 +1255925,8 @@ init_settings();
|
|
|
1255793
1255925
|
init_logger3();
|
|
1255794
1255926
|
init_ical_helpers();
|
|
1255795
1255927
|
init_caldav_account_manager();
|
|
1255796
|
-
|
|
1255928
|
+
init_google_calendar_manager();
|
|
1255929
|
+
var logger10 = createLogger("CalDAV");
|
|
1255797
1255930
|
function migrateFromSettings() {
|
|
1255798
1255931
|
const settings = getSettings();
|
|
1255799
1255932
|
const caldavSettings = settings.caldav;
|
|
@@ -1255809,7 +1255942,7 @@ function migrateFromSettings() {
|
|
|
1255809
1255942
|
return;
|
|
1255810
1255943
|
return;
|
|
1255811
1255944
|
}
|
|
1255812
|
-
|
|
1255945
|
+
logger10.info("Migrating CalDAV credentials from settings.json to database", {
|
|
1255813
1255946
|
authType: caldavSettings.authType,
|
|
1255814
1255947
|
orphanedCalendars: orphanedCalendars.length
|
|
1255815
1255948
|
});
|
|
@@ -1255834,28 +1255967,28 @@ function migrateFromSettings() {
|
|
|
1255834
1255967
|
for (const cal of orphanedCalendars) {
|
|
1255835
1255968
|
db2.update(caldavCalendars).set({ accountId, updatedAt: now }).where(eq(caldavCalendars.id, cal.id)).run();
|
|
1255836
1255969
|
}
|
|
1255837
|
-
|
|
1255970
|
+
logger10.info("Migration complete", { accountId, migratedCalendars: orphanedCalendars.length });
|
|
1255838
1255971
|
}
|
|
1255839
1255972
|
async function startCaldavSync() {
|
|
1255840
1255973
|
const settings = getSettings();
|
|
1255841
1255974
|
if (!settings.caldav?.enabled) {
|
|
1255842
|
-
|
|
1255975
|
+
logger10.info("CalDAV disabled, skipping sync start");
|
|
1255843
1255976
|
return;
|
|
1255844
1255977
|
}
|
|
1255845
1255978
|
migrateFromSettings();
|
|
1255846
1255979
|
await accountManager.startAll();
|
|
1255847
1255980
|
await accountManager.syncAll().catch((err) => {
|
|
1255848
|
-
|
|
1255981
|
+
logger10.error("Initial sync failed", { error: err instanceof Error ? err.message : String(err) });
|
|
1255849
1255982
|
});
|
|
1255850
1255983
|
await executeCopyRules().catch((err) => {
|
|
1255851
|
-
|
|
1255984
|
+
logger10.error("Copy rules execution failed after sync", {
|
|
1255852
1255985
|
error: err instanceof Error ? err.message : String(err)
|
|
1255853
1255986
|
});
|
|
1255854
1255987
|
});
|
|
1255855
1255988
|
}
|
|
1255856
1255989
|
function stopCaldavSync() {
|
|
1255857
1255990
|
accountManager.stopAll();
|
|
1255858
|
-
|
|
1255991
|
+
logger10.info("CalDAV sync stopped");
|
|
1255859
1255992
|
}
|
|
1255860
1255993
|
function getCaldavStatus() {
|
|
1255861
1255994
|
const accounts = accountManager.getStatus();
|
|
@@ -1255898,7 +1256031,7 @@ async function createAccount2(input) {
|
|
|
1255898
1256031
|
updatedAt: now
|
|
1255899
1256032
|
};
|
|
1255900
1256033
|
db2.insert(caldavAccounts).values(account2).run();
|
|
1255901
|
-
|
|
1256034
|
+
logger10.info("Created CalDAV account", { id, name: input.name });
|
|
1255902
1256035
|
return account2;
|
|
1255903
1256036
|
}
|
|
1255904
1256037
|
async function updateAccount(id, updates) {
|
|
@@ -1255926,7 +1256059,7 @@ async function deleteAccount(id) {
|
|
|
1255926
1256059
|
}
|
|
1255927
1256060
|
db2.delete(caldavCalendars).where(eq(caldavCalendars.accountId, id)).run();
|
|
1255928
1256061
|
db2.delete(caldavAccounts).where(eq(caldavAccounts.id, id)).run();
|
|
1255929
|
-
|
|
1256062
|
+
logger10.info("Deleted CalDAV account", { id });
|
|
1255930
1256063
|
}
|
|
1255931
1256064
|
async function enableAccount(id) {
|
|
1255932
1256065
|
db2.update(caldavAccounts).set({ enabled: true, updatedAt: new Date().toISOString() }).where(eq(caldavAccounts.id, id)).run();
|
|
@@ -1255962,7 +1256095,7 @@ async function testAccountConnection(config3) {
|
|
|
1255962
1256095
|
async function syncAccount(id) {
|
|
1255963
1256096
|
await accountManager.syncAccount(id);
|
|
1255964
1256097
|
await executeCopyRules().catch((err) => {
|
|
1255965
|
-
|
|
1256098
|
+
logger10.error("Copy rules failed after account sync", {
|
|
1255966
1256099
|
error: err instanceof Error ? err.message : String(err)
|
|
1255967
1256100
|
});
|
|
1255968
1256101
|
});
|
|
@@ -1256068,7 +1256201,7 @@ function listCalendars(accountId) {
|
|
|
1256068
1256201
|
async function syncCalendars2() {
|
|
1256069
1256202
|
await accountManager.syncAll();
|
|
1256070
1256203
|
await executeCopyRules().catch((err) => {
|
|
1256071
|
-
|
|
1256204
|
+
logger10.error("Copy rules failed after sync", {
|
|
1256072
1256205
|
error: err instanceof Error ? err.message : String(err)
|
|
1256073
1256206
|
});
|
|
1256074
1256207
|
});
|
|
@@ -1256098,6 +1256231,20 @@ async function createEvent(input) {
|
|
|
1256098
1256231
|
if (!calendar2) {
|
|
1256099
1256232
|
throw new Error(`Calendar not found: ${input.calendarId}`);
|
|
1256100
1256233
|
}
|
|
1256234
|
+
if (calendar2.googleAccountId) {
|
|
1256235
|
+
await googleCalendarManager.createEvent(calendar2.id, {
|
|
1256236
|
+
summary: input.summary,
|
|
1256237
|
+
dtstart: input.dtstart,
|
|
1256238
|
+
dtend: input.dtend,
|
|
1256239
|
+
description: input.description,
|
|
1256240
|
+
location: input.location,
|
|
1256241
|
+
allDay: input.allDay
|
|
1256242
|
+
});
|
|
1256243
|
+
const created = db2.select().from(caldavEvents).where(eq(caldavEvents.calendarId, input.calendarId)).orderBy(desc(caldavEvents.createdAt)).limit(1).get();
|
|
1256244
|
+
if (!created)
|
|
1256245
|
+
throw new Error("Failed to retrieve created Google Calendar event");
|
|
1256246
|
+
return created;
|
|
1256247
|
+
}
|
|
1256101
1256248
|
const client4 = calendar2.accountId ? accountManager.getClient(calendar2.accountId) : null;
|
|
1256102
1256249
|
if (!client4) {
|
|
1256103
1256250
|
throw new Error("CalDAV account not connected for this calendar");
|
|
@@ -1256145,7 +1256292,7 @@ async function createEvent(input) {
|
|
|
1256145
1256292
|
updatedAt: now
|
|
1256146
1256293
|
};
|
|
1256147
1256294
|
db2.insert(caldavEvents).values(event).run();
|
|
1256148
|
-
|
|
1256295
|
+
logger10.info("Created CalDAV event", { id, summary: input.summary });
|
|
1256149
1256296
|
return event;
|
|
1256150
1256297
|
}
|
|
1256151
1256298
|
async function updateEvent(id, updates) {
|
|
@@ -1256154,6 +1256301,10 @@ async function updateEvent(id, updates) {
|
|
|
1256154
1256301
|
throw new Error(`Event not found: ${id}`);
|
|
1256155
1256302
|
}
|
|
1256156
1256303
|
const calendar2 = db2.select().from(caldavCalendars).where(eq(caldavCalendars.id, event.calendarId)).get();
|
|
1256304
|
+
if (calendar2?.googleAccountId) {
|
|
1256305
|
+
await googleCalendarManager.updateEvent(id, updates);
|
|
1256306
|
+
return db2.select().from(caldavEvents).where(eq(caldavEvents.id, id)).get();
|
|
1256307
|
+
}
|
|
1256157
1256308
|
const client4 = calendar2?.accountId ? accountManager.getClient(calendar2.accountId) : null;
|
|
1256158
1256309
|
if (!client4) {
|
|
1256159
1256310
|
throw new Error("CalDAV account not connected for this calendar");
|
|
@@ -1256191,7 +1256342,7 @@ async function updateEvent(id, updates) {
|
|
|
1256191
1256342
|
rawIcal: updatedIcal,
|
|
1256192
1256343
|
updatedAt: now
|
|
1256193
1256344
|
}).where(eq(caldavEvents.id, id)).run();
|
|
1256194
|
-
|
|
1256345
|
+
logger10.info("Updated CalDAV event", { id, summary: updates.summary ?? event.summary });
|
|
1256195
1256346
|
return db2.select().from(caldavEvents).where(eq(caldavEvents.id, id)).get();
|
|
1256196
1256347
|
}
|
|
1256197
1256348
|
async function deleteEvent(id) {
|
|
@@ -1256200,6 +1256351,10 @@ async function deleteEvent(id) {
|
|
|
1256200
1256351
|
throw new Error(`Event not found: ${id}`);
|
|
1256201
1256352
|
}
|
|
1256202
1256353
|
const calendar2 = db2.select().from(caldavCalendars).where(eq(caldavCalendars.id, event.calendarId)).get();
|
|
1256354
|
+
if (calendar2?.googleAccountId) {
|
|
1256355
|
+
await googleCalendarManager.deleteEvent(id);
|
|
1256356
|
+
return;
|
|
1256357
|
+
}
|
|
1256203
1256358
|
const client4 = calendar2?.accountId ? accountManager.getClient(calendar2.accountId) : null;
|
|
1256204
1256359
|
if (!client4) {
|
|
1256205
1256360
|
throw new Error("CalDAV account not connected for this calendar");
|
|
@@ -1256211,7 +1256366,7 @@ async function deleteEvent(id) {
|
|
|
1256211
1256366
|
}
|
|
1256212
1256367
|
});
|
|
1256213
1256368
|
db2.delete(caldavEvents).where(eq(caldavEvents.id, id)).run();
|
|
1256214
|
-
|
|
1256369
|
+
logger10.info("Deleted CalDAV event", { id, summary: event.summary });
|
|
1256215
1256370
|
}
|
|
1256216
1256371
|
function listCopyRules() {
|
|
1256217
1256372
|
return db2.select().from(caldavCopyRules).all();
|
|
@@ -1256230,7 +1256385,7 @@ function createCopyRule(input) {
|
|
|
1256230
1256385
|
updatedAt: now
|
|
1256231
1256386
|
};
|
|
1256232
1256387
|
db2.insert(caldavCopyRules).values(rule).run();
|
|
1256233
|
-
|
|
1256388
|
+
logger10.info("Created copy rule", { id, source: input.sourceCalendarId, dest: input.destCalendarId });
|
|
1256234
1256389
|
return rule;
|
|
1256235
1256390
|
}
|
|
1256236
1256391
|
function updateCopyRule(id, updates) {
|
|
@@ -1256243,7 +1256398,7 @@ function updateCopyRule(id, updates) {
|
|
|
1256243
1256398
|
function deleteCopyRule(id) {
|
|
1256244
1256399
|
db2.delete(caldavCopiedEvents).where(eq(caldavCopiedEvents.ruleId, id)).run();
|
|
1256245
1256400
|
db2.delete(caldavCopyRules).where(eq(caldavCopyRules.id, id)).run();
|
|
1256246
|
-
|
|
1256401
|
+
logger10.info("Deleted copy rule", { id });
|
|
1256247
1256402
|
}
|
|
1256248
1256403
|
async function executeCopyRule(ruleId) {
|
|
1256249
1256404
|
const { executeSingleRule: executeSingleRule2 } = await Promise.resolve().then(() => (init_copy_engine(), exports_copy_engine));
|
|
@@ -1256686,13 +1256841,13 @@ init_db2();
|
|
|
1256686
1256841
|
init_settings();
|
|
1256687
1256842
|
init_google_oauth();
|
|
1256688
1256843
|
init_logger3();
|
|
1256689
|
-
var
|
|
1256844
|
+
var logger11 = createLogger("GoogleOAuth:Routes");
|
|
1256690
1256845
|
var app25 = new Hono2;
|
|
1256691
1256846
|
app25.get("/authorize", (c) => {
|
|
1256692
1256847
|
const accountName = c.req.query("accountName");
|
|
1256693
1256848
|
const accountId = c.req.query("accountId");
|
|
1256694
1256849
|
const clientOrigin = c.req.query("origin");
|
|
1256695
|
-
|
|
1256850
|
+
logger11.info("OAuth authorize request", {
|
|
1256696
1256851
|
accountName,
|
|
1256697
1256852
|
accountId,
|
|
1256698
1256853
|
clientOrigin,
|
|
@@ -1256716,7 +1256871,7 @@ app25.get("/authorize", (c) => {
|
|
|
1256716
1256871
|
accountName: accountName || "Google Account",
|
|
1256717
1256872
|
redirectUri
|
|
1256718
1256873
|
});
|
|
1256719
|
-
|
|
1256874
|
+
logger11.info("OAuth authorize: generating auth URL", {
|
|
1256720
1256875
|
baseUrl,
|
|
1256721
1256876
|
redirectUri,
|
|
1256722
1256877
|
stateJson: state,
|
|
@@ -1256725,18 +1256880,18 @@ app25.get("/authorize", (c) => {
|
|
|
1256725
1256880
|
const authUrl = generateAuthUrl(client4, redirectUri, state);
|
|
1256726
1256881
|
try {
|
|
1256727
1256882
|
const parsed = new URL(authUrl);
|
|
1256728
|
-
|
|
1256883
|
+
logger11.info("OAuth authorize: generated auth URL", {
|
|
1256729
1256884
|
authUrl,
|
|
1256730
1256885
|
redirect_uri_in_url: parsed.searchParams.get("redirect_uri"),
|
|
1256731
1256886
|
client_id_in_url: parsed.searchParams.get("client_id"),
|
|
1256732
1256887
|
scope_in_url: parsed.searchParams.get("scope")
|
|
1256733
1256888
|
});
|
|
1256734
1256889
|
} catch {
|
|
1256735
|
-
|
|
1256890
|
+
logger11.info("OAuth authorize: generated auth URL (unparseable)", { authUrl });
|
|
1256736
1256891
|
}
|
|
1256737
1256892
|
return c.json({ authUrl });
|
|
1256738
1256893
|
} catch (err) {
|
|
1256739
|
-
|
|
1256894
|
+
logger11.error("OAuth authorize failed", {
|
|
1256740
1256895
|
error: err instanceof Error ? err.message : String(err)
|
|
1256741
1256896
|
});
|
|
1256742
1256897
|
return c.json({ error: err instanceof Error ? err.message : "Failed to generate auth URL" }, 400);
|
|
@@ -1256747,7 +1256902,7 @@ app25.get("/callback", async (c) => {
|
|
|
1256747
1256902
|
const error46 = c.req.query("error");
|
|
1256748
1256903
|
const stateParam = c.req.query("state");
|
|
1256749
1256904
|
const fullUrl = c.req.url;
|
|
1256750
|
-
|
|
1256905
|
+
logger11.info("OAuth callback received", {
|
|
1256751
1256906
|
hasCode: !!code,
|
|
1256752
1256907
|
codePrefix: code?.slice(0, 20) + "...",
|
|
1256753
1256908
|
error: error46,
|
|
@@ -1256758,11 +1256913,11 @@ app25.get("/callback", async (c) => {
|
|
|
1256758
1256913
|
refererHeader: c.req.header("referer")
|
|
1256759
1256914
|
});
|
|
1256760
1256915
|
if (error46) {
|
|
1256761
|
-
|
|
1256916
|
+
logger11.warn("OAuth callback: Google returned error", { error: error46 });
|
|
1256762
1256917
|
return c.html(`<html><body><h2>Authorization Failed</h2><p>${error46}</p><script>setTimeout(()=>window.close(),3000)</script></body></html>`);
|
|
1256763
1256918
|
}
|
|
1256764
1256919
|
if (!code) {
|
|
1256765
|
-
|
|
1256920
|
+
logger11.warn("OAuth callback: missing authorization code");
|
|
1256766
1256921
|
return c.html("<html><body><h2>Missing authorization code</h2><script>setTimeout(()=>window.close(),3000)</script></body></html>", 400);
|
|
1256767
1256922
|
}
|
|
1256768
1256923
|
let state = {
|
|
@@ -1256774,9 +1256929,9 @@ app25.get("/callback", async (c) => {
|
|
|
1256774
1256929
|
state = JSON.parse(stateParam);
|
|
1256775
1256930
|
}
|
|
1256776
1256931
|
} catch {
|
|
1256777
|
-
|
|
1256932
|
+
logger11.warn("OAuth callback: failed to parse state param", { stateParam });
|
|
1256778
1256933
|
}
|
|
1256779
|
-
|
|
1256934
|
+
logger11.info("OAuth callback: parsed state", {
|
|
1256780
1256935
|
accountId: state.accountId,
|
|
1256781
1256936
|
accountName: state.accountName,
|
|
1256782
1256937
|
redirectUri: state.redirectUri
|
|
@@ -1256787,7 +1256942,7 @@ app25.get("/callback", async (c) => {
|
|
|
1256787
1256942
|
throw new Error("Missing redirect URI in state \u2014 cannot match token exchange");
|
|
1256788
1256943
|
}
|
|
1256789
1256944
|
const client4 = createOAuth2Client();
|
|
1256790
|
-
|
|
1256945
|
+
logger11.info("OAuth callback: exchanging code for tokens", {
|
|
1256791
1256946
|
redirectUri,
|
|
1256792
1256947
|
clientId: client4._clientId,
|
|
1256793
1256948
|
codePrefix: code.slice(0, 20) + "..."
|
|
@@ -1256809,7 +1256964,7 @@ app25.get("/callback", async (c) => {
|
|
|
1256809
1256964
|
email: email4 ?? undefined,
|
|
1256810
1256965
|
updatedAt: now
|
|
1256811
1256966
|
}).where(eq(googleAccounts.id, state.accountId)).run();
|
|
1256812
|
-
|
|
1256967
|
+
logger11.info("Re-authorized Google account", {
|
|
1256813
1256968
|
accountId: state.accountId,
|
|
1256814
1256969
|
email: email4
|
|
1256815
1256970
|
});
|
|
@@ -1256829,7 +1256984,7 @@ app25.get("/callback", async (c) => {
|
|
|
1256829
1256984
|
createdAt: now,
|
|
1256830
1256985
|
updatedAt: now
|
|
1256831
1256986
|
}).run();
|
|
1256832
|
-
|
|
1256987
|
+
logger11.info("Created Google account", {
|
|
1256833
1256988
|
accountId,
|
|
1256834
1256989
|
name: state.accountName,
|
|
1256835
1256990
|
email: email4
|
|
@@ -1256837,7 +1256992,7 @@ app25.get("/callback", async (c) => {
|
|
|
1256837
1256992
|
}
|
|
1256838
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>");
|
|
1256839
1256994
|
} catch (err) {
|
|
1256840
|
-
|
|
1256995
|
+
logger11.error("OAuth callback failed", {
|
|
1256841
1256996
|
error: err instanceof Error ? err.message : String(err)
|
|
1256842
1256997
|
});
|
|
1256843
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);
|
|
@@ -1256852,7 +1257007,7 @@ init_google_calendar_manager();
|
|
|
1256852
1257007
|
init_logger3();
|
|
1256853
1257008
|
init_settings();
|
|
1256854
1257009
|
init_channel_manager();
|
|
1256855
|
-
var
|
|
1257010
|
+
var logger12 = createLogger("Google:CalendarService");
|
|
1256856
1257011
|
function listGoogleAccounts() {
|
|
1256857
1257012
|
return db2.select().from(googleAccounts).all();
|
|
1256858
1257013
|
}
|
|
@@ -1256872,19 +1257027,19 @@ async function deleteGoogleAccount(id) {
|
|
|
1256872
1257027
|
}
|
|
1256873
1257028
|
db2.delete(caldavCalendars).where(eq(caldavCalendars.googleAccountId, id)).run();
|
|
1256874
1257029
|
db2.delete(googleAccounts).where(eq(googleAccounts.id, id)).run();
|
|
1256875
|
-
|
|
1257030
|
+
logger12.info("Deleted Google account", { accountId: id });
|
|
1256876
1257031
|
}
|
|
1256877
1257032
|
async function enableGoogleCalendar(id) {
|
|
1256878
1257033
|
const now = new Date().toISOString();
|
|
1256879
1257034
|
db2.update(googleAccounts).set({ calendarEnabled: true, updatedAt: now }).where(eq(googleAccounts.id, id)).run();
|
|
1256880
1257035
|
await googleCalendarManager.startAccount(id);
|
|
1256881
|
-
|
|
1257036
|
+
logger12.info("Enabled Google Calendar for account", { accountId: id });
|
|
1256882
1257037
|
}
|
|
1256883
1257038
|
async function disableGoogleCalendar(id) {
|
|
1256884
1257039
|
const now = new Date().toISOString();
|
|
1256885
1257040
|
db2.update(googleAccounts).set({ calendarEnabled: false, updatedAt: now }).where(eq(googleAccounts.id, id)).run();
|
|
1256886
1257041
|
googleCalendarManager.stopAccount(id);
|
|
1256887
|
-
|
|
1257042
|
+
logger12.info("Disabled Google Calendar for account", { accountId: id });
|
|
1256888
1257043
|
}
|
|
1256889
1257044
|
async function enableGmail(id) {
|
|
1256890
1257045
|
const now = new Date().toISOString();
|
|
@@ -1256894,7 +1257049,7 @@ async function enableGmail(id) {
|
|
|
1256894
1257049
|
updateSettingByPath("channels.email.googleAccountId", id);
|
|
1256895
1257050
|
await stopEmailChannel();
|
|
1256896
1257051
|
await startEmailChannel();
|
|
1256897
|
-
|
|
1257052
|
+
logger12.info("Enabled Gmail for account", { accountId: id });
|
|
1256898
1257053
|
}
|
|
1256899
1257054
|
async function disableGmail(id) {
|
|
1256900
1257055
|
const now = new Date().toISOString();
|
|
@@ -1256902,7 +1257057,7 @@ async function disableGmail(id) {
|
|
|
1256902
1257057
|
await stopEmailChannel();
|
|
1256903
1257058
|
updateSettingByPath("channels.email.enabled", false);
|
|
1256904
1257059
|
updateSettingByPath("channels.email.googleAccountId", null);
|
|
1256905
|
-
|
|
1257060
|
+
logger12.info("Disabled Gmail for account", { accountId: id });
|
|
1256906
1257061
|
}
|
|
1256907
1257062
|
async function syncGoogleCalendar(id) {
|
|
1256908
1257063
|
await googleCalendarManager.syncAccount(id);
|
|
@@ -1257156,6 +1257311,10 @@ app28.get("/", async (c) => {
|
|
|
1257156
1257311
|
const conversationRole = c.req.query("conversationRole") || undefined;
|
|
1257157
1257312
|
const conversationProvider = c.req.query("conversationProvider") || undefined;
|
|
1257158
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;
|
|
1257159
1257318
|
try {
|
|
1257160
1257319
|
const results = await search({
|
|
1257161
1257320
|
query: query.trim(),
|
|
@@ -1257170,7 +1257329,11 @@ app28.get("/", async (c) => {
|
|
|
1257170
1257329
|
memoryTags,
|
|
1257171
1257330
|
conversationRole,
|
|
1257172
1257331
|
conversationProvider,
|
|
1257173
|
-
conversationProjectId
|
|
1257332
|
+
conversationProjectId,
|
|
1257333
|
+
gmailFrom,
|
|
1257334
|
+
gmailTo,
|
|
1257335
|
+
gmailAfter,
|
|
1257336
|
+
gmailBefore
|
|
1257174
1257337
|
});
|
|
1257175
1257338
|
return c.json(results);
|
|
1257176
1257339
|
} catch (err) {
|