mcp-scraper 0.8.0 → 0.9.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/dist/bin/api-server.cjs +589 -7
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +1 -1
- package/dist/bin/mcp-scraper-cli.cjs +1 -1
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +1 -1
- package/dist/bin/mcp-scraper-install.cjs +1 -1
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +1 -1
- package/dist/bin/mcp-stdio-server.cjs +87 -1
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +2 -2
- package/dist/{chunk-ITJC3NN4.js → chunk-FZG427QD.js} +88 -2
- package/dist/chunk-FZG427QD.js.map +1 -0
- package/dist/chunk-RD5JOAWS.js +7 -0
- package/dist/chunk-RD5JOAWS.js.map +1 -0
- package/dist/{server-WBDVVHR6.js → server-E34MNLI3.js} +497 -8
- package/dist/server-E34MNLI3.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-3MKSXDJ7.js +0 -7
- package/dist/chunk-3MKSXDJ7.js.map +0 -1
- package/dist/chunk-ITJC3NN4.js.map +0 -1
- package/dist/server-WBDVVHR6.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/version.ts"],"sourcesContent":["export const PACKAGE_VERSION = '0.9.0'\n"],"mappings":";AAAO,IAAM,kBAAkB;","names":[]}
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
registerSerpIntelligenceCaptureTools,
|
|
35
35
|
sanitizeAttempts,
|
|
36
36
|
sanitizeHarvestResult
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-FZG427QD.js";
|
|
38
38
|
import {
|
|
39
39
|
auditImages,
|
|
40
40
|
buildLinkReport,
|
|
@@ -70,7 +70,7 @@ import {
|
|
|
70
70
|
RawMapsOverviewSchema,
|
|
71
71
|
RawMapsReviewStatsSchema
|
|
72
72
|
} from "./chunk-XGIPATLV.js";
|
|
73
|
-
import "./chunk-
|
|
73
|
+
import "./chunk-RD5JOAWS.js";
|
|
74
74
|
import {
|
|
75
75
|
completeExtractJob,
|
|
76
76
|
countSuccessfulPages,
|
|
@@ -20393,6 +20393,306 @@ async function dbUsage(identity, plan) {
|
|
|
20393
20393
|
};
|
|
20394
20394
|
}
|
|
20395
20395
|
|
|
20396
|
+
// src/api/nango-control.ts
|
|
20397
|
+
var DEFAULT_NANGO_CONTROL_URL = "https://mcp-scraper-scheduler.vercel.app";
|
|
20398
|
+
var DISABLED_NANGO_TOOLS = {
|
|
20399
|
+
"google-mail": /* @__PURE__ */ new Set(["list-filters"]),
|
|
20400
|
+
slack: /* @__PURE__ */ new Set(["search-files", "search-messages"])
|
|
20401
|
+
};
|
|
20402
|
+
var NangoControlError = class extends Error {
|
|
20403
|
+
status;
|
|
20404
|
+
constructor(message, status = 502) {
|
|
20405
|
+
super(message);
|
|
20406
|
+
this.name = "NangoControlError";
|
|
20407
|
+
this.status = status;
|
|
20408
|
+
}
|
|
20409
|
+
};
|
|
20410
|
+
var ScheduleConnectionValidationError = class extends Error {
|
|
20411
|
+
constructor(message) {
|
|
20412
|
+
super(message);
|
|
20413
|
+
this.name = "ScheduleConnectionValidationError";
|
|
20414
|
+
}
|
|
20415
|
+
};
|
|
20416
|
+
function controlBaseUrl() {
|
|
20417
|
+
return (process.env.NANGO_CONTROL_URL?.trim() || DEFAULT_NANGO_CONTROL_URL).replace(/\/$/, "");
|
|
20418
|
+
}
|
|
20419
|
+
function controlSecret() {
|
|
20420
|
+
const secret2 = process.env.SCHEDULE_INTEGRATIONS_SECRET?.trim();
|
|
20421
|
+
if (!secret2) throw new NangoControlError("Scheduled service connections are not configured.", 503);
|
|
20422
|
+
return secret2;
|
|
20423
|
+
}
|
|
20424
|
+
function isRecord(value) {
|
|
20425
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
20426
|
+
}
|
|
20427
|
+
function unwrapData(value) {
|
|
20428
|
+
if (isRecord(value) && isRecord(value.data)) return value.data;
|
|
20429
|
+
return value;
|
|
20430
|
+
}
|
|
20431
|
+
function cleanString(value, max = 300) {
|
|
20432
|
+
if (typeof value !== "string") return null;
|
|
20433
|
+
const trimmed = value.trim();
|
|
20434
|
+
return trimmed ? trimmed.slice(0, max) : null;
|
|
20435
|
+
}
|
|
20436
|
+
function firstString(record, keys, max = 300) {
|
|
20437
|
+
for (const key of keys) {
|
|
20438
|
+
const value = cleanString(record[key], max);
|
|
20439
|
+
if (value) return value;
|
|
20440
|
+
}
|
|
20441
|
+
return null;
|
|
20442
|
+
}
|
|
20443
|
+
function cleanTools(value) {
|
|
20444
|
+
if (!Array.isArray(value)) return [];
|
|
20445
|
+
const tools = value.map((tool) => cleanString(tool, 200)).filter((tool) => !!tool);
|
|
20446
|
+
return [...new Set(tools)].slice(0, 200);
|
|
20447
|
+
}
|
|
20448
|
+
function cleanStringArray(value, maxItems = 20, maxLength = 100) {
|
|
20449
|
+
if (!Array.isArray(value)) return [];
|
|
20450
|
+
const items = value.map((item) => cleanString(item, maxLength)).filter((item) => !!item);
|
|
20451
|
+
return [...new Set(items)].slice(0, maxItems);
|
|
20452
|
+
}
|
|
20453
|
+
function cleanHttpsUrl(value) {
|
|
20454
|
+
const candidate = cleanString(value, 2e3);
|
|
20455
|
+
if (!candidate) return null;
|
|
20456
|
+
try {
|
|
20457
|
+
const url = new URL(candidate);
|
|
20458
|
+
return url.protocol === "https:" ? url.toString() : null;
|
|
20459
|
+
} catch {
|
|
20460
|
+
return null;
|
|
20461
|
+
}
|
|
20462
|
+
}
|
|
20463
|
+
function arrayFromPayload(value, keys) {
|
|
20464
|
+
const unwrapped = unwrapData(value);
|
|
20465
|
+
if (Array.isArray(unwrapped)) return unwrapped;
|
|
20466
|
+
if (!isRecord(unwrapped)) return [];
|
|
20467
|
+
for (const key of keys) {
|
|
20468
|
+
if (Array.isArray(unwrapped[key])) return unwrapped[key];
|
|
20469
|
+
}
|
|
20470
|
+
return [];
|
|
20471
|
+
}
|
|
20472
|
+
async function controlRequest(path5, init) {
|
|
20473
|
+
const response = await fetch(`${controlBaseUrl()}${path5}`, {
|
|
20474
|
+
...init,
|
|
20475
|
+
headers: {
|
|
20476
|
+
accept: "application/json",
|
|
20477
|
+
authorization: `Bearer ${controlSecret()}`,
|
|
20478
|
+
...init?.body ? { "content-type": "application/json" } : {},
|
|
20479
|
+
...init?.headers ?? {}
|
|
20480
|
+
},
|
|
20481
|
+
signal: AbortSignal.timeout(2e4)
|
|
20482
|
+
}).catch((err) => {
|
|
20483
|
+
throw new NangoControlError(`Scheduled service connection control is unavailable: ${err instanceof Error ? err.message : "network error"}`);
|
|
20484
|
+
});
|
|
20485
|
+
const text = await response.text();
|
|
20486
|
+
let body = {};
|
|
20487
|
+
try {
|
|
20488
|
+
body = text ? JSON.parse(text) : {};
|
|
20489
|
+
} catch {
|
|
20490
|
+
body = {};
|
|
20491
|
+
}
|
|
20492
|
+
if (!response.ok) {
|
|
20493
|
+
const upstream = isRecord(body) ? cleanString(body.error, 300) : null;
|
|
20494
|
+
throw new NangoControlError(upstream || `Scheduled service connection control failed (${response.status}).`);
|
|
20495
|
+
}
|
|
20496
|
+
return body;
|
|
20497
|
+
}
|
|
20498
|
+
function sanitizeScheduleConnectionSelections(value) {
|
|
20499
|
+
if (value == null) return [];
|
|
20500
|
+
if (!Array.isArray(value)) throw new ScheduleConnectionValidationError("connections must be an array.");
|
|
20501
|
+
if (value.length > 20) throw new ScheduleConnectionValidationError("A scheduled action can use at most 20 service connections.");
|
|
20502
|
+
const selections = [];
|
|
20503
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20504
|
+
for (const item of value) {
|
|
20505
|
+
if (!isRecord(item)) throw new ScheduleConnectionValidationError("Each service connection must be an object.");
|
|
20506
|
+
const connectionId = firstString(item, ["connectionId", "connection_id"]);
|
|
20507
|
+
const providerConfigKey = firstString(item, ["providerConfigKey", "provider_config_key", "integrationId", "integration_id"]);
|
|
20508
|
+
const allowedTools = cleanTools(item.allowedTools ?? item.allowed_tools ?? item.allowedCapabilities ?? item.allowed_capabilities);
|
|
20509
|
+
if (!connectionId || !providerConfigKey) throw new ScheduleConnectionValidationError("Each service connection needs a connectionId and providerConfigKey.");
|
|
20510
|
+
if (allowedTools.length === 0) throw new ScheduleConnectionValidationError("Each selected service needs at least one allowed tool.");
|
|
20511
|
+
const key = `${providerConfigKey}:${connectionId}`;
|
|
20512
|
+
if (seen.has(key)) continue;
|
|
20513
|
+
seen.add(key);
|
|
20514
|
+
selections.push({ connectionId, providerConfigKey, allowedTools });
|
|
20515
|
+
}
|
|
20516
|
+
return selections;
|
|
20517
|
+
}
|
|
20518
|
+
async function getNangoCatalog() {
|
|
20519
|
+
const body = await controlRequest("/api/internal/nango/catalog");
|
|
20520
|
+
const rows = arrayFromPayload(body, ["providers", "catalog", "integrations", "services"]);
|
|
20521
|
+
const result = [];
|
|
20522
|
+
for (const row of rows) {
|
|
20523
|
+
if (!isRecord(row)) continue;
|
|
20524
|
+
const providerConfigKey = firstString(row, ["providerConfigKey", "provider_config_key", "integrationId", "integration_id", "id"]);
|
|
20525
|
+
if (!providerConfigKey) continue;
|
|
20526
|
+
const provider = firstString(row, ["provider"]);
|
|
20527
|
+
const label = firstString(row, ["label", "displayName", "display_name", "name"]) || providerConfigKey;
|
|
20528
|
+
const description = firstString(row, ["description"], 500);
|
|
20529
|
+
const logoUrl = cleanHttpsUrl(row.logoUrl ?? row.logo_url ?? row.logo);
|
|
20530
|
+
const docsUrl = cleanHttpsUrl(row.docsUrl ?? row.docs_url ?? row.docs);
|
|
20531
|
+
const authMode = firstString(row, ["authMode", "auth_mode"], 100);
|
|
20532
|
+
const categories = cleanStringArray(row.categories);
|
|
20533
|
+
const disabledTools = DISABLED_NANGO_TOOLS[providerConfigKey];
|
|
20534
|
+
const safeDefaultAllowedTools = cleanTools(
|
|
20535
|
+
row.safeDefaultAllowedTools ?? row.safe_default_allowed_tools ?? row.defaultAllowedTools ?? row.default_allowed_tools ?? row.allowedTools ?? row.allowed_tools
|
|
20536
|
+
).filter((tool) => !disabledTools?.has(tool));
|
|
20537
|
+
result.push({
|
|
20538
|
+
providerConfigKey,
|
|
20539
|
+
provider,
|
|
20540
|
+
label,
|
|
20541
|
+
description,
|
|
20542
|
+
logoUrl,
|
|
20543
|
+
docsUrl,
|
|
20544
|
+
authMode,
|
|
20545
|
+
categories,
|
|
20546
|
+
safeDefaultAllowedTools
|
|
20547
|
+
});
|
|
20548
|
+
}
|
|
20549
|
+
return result;
|
|
20550
|
+
}
|
|
20551
|
+
async function getNangoConnections(identity) {
|
|
20552
|
+
const query = new URLSearchParams({ identity }).toString();
|
|
20553
|
+
const body = await controlRequest(`/api/internal/nango/connections?${query}`);
|
|
20554
|
+
const rows = arrayFromPayload(body, ["connections"]);
|
|
20555
|
+
const result = [];
|
|
20556
|
+
for (const row of rows) {
|
|
20557
|
+
if (!isRecord(row)) continue;
|
|
20558
|
+
const connectionId = firstString(row, ["connectionId", "connection_id", "id"]);
|
|
20559
|
+
const providerConfigKey = firstString(row, ["providerConfigKey", "provider_config_key", "integrationId", "integration_id", "provider"]);
|
|
20560
|
+
if (!connectionId || !providerConfigKey) continue;
|
|
20561
|
+
const provider = firstString(row, ["provider"]);
|
|
20562
|
+
const label = firstString(row, ["label", "accountLabel", "account_label", "displayName", "display_name", "endUserEmail", "end_user_email"]) || providerConfigKey;
|
|
20563
|
+
const rawStatus = firstString(row, ["status"], 64) || "connected";
|
|
20564
|
+
const reconnectRequired = row.reconnectRequired === true || row.reconnect_required === true || rawStatus === "needs_reauth" || rawStatus === "reauth_required" || rawStatus === "invalid" || rawStatus === "error" || rawStatus === "revoked" || rawStatus === "disabled";
|
|
20565
|
+
result.push({
|
|
20566
|
+
connectionId,
|
|
20567
|
+
providerConfigKey,
|
|
20568
|
+
provider,
|
|
20569
|
+
label,
|
|
20570
|
+
status: reconnectRequired ? "needs_reauth" : "connected",
|
|
20571
|
+
reconnectRequired,
|
|
20572
|
+
actionsEnabled: row.actionsEnabled === true || row.actions_enabled === true,
|
|
20573
|
+
createdAt: firstString(row, ["createdAt", "created_at"], 100),
|
|
20574
|
+
updatedAt: firstString(row, ["updatedAt", "updated_at"], 100)
|
|
20575
|
+
});
|
|
20576
|
+
}
|
|
20577
|
+
return result;
|
|
20578
|
+
}
|
|
20579
|
+
function sanitizeConnectSession(body) {
|
|
20580
|
+
const data = unwrapData(body);
|
|
20581
|
+
if (!isRecord(data)) throw new NangoControlError("The connection service returned an invalid connect session.");
|
|
20582
|
+
const connectLink = firstString(data, ["connectLink", "connect_link"], 2e3);
|
|
20583
|
+
if (!connectLink) throw new NangoControlError("The connection service did not return a connect link.");
|
|
20584
|
+
return {
|
|
20585
|
+
connectLink,
|
|
20586
|
+
sessionToken: firstString(data, ["sessionToken", "session_token", "token"], 2e3),
|
|
20587
|
+
expiresAt: firstString(data, ["expiresAt", "expires_at"], 100)
|
|
20588
|
+
};
|
|
20589
|
+
}
|
|
20590
|
+
async function createNangoConnectSession(identity, providerConfigKey) {
|
|
20591
|
+
const body = await controlRequest("/api/internal/nango/connect-session", {
|
|
20592
|
+
method: "POST",
|
|
20593
|
+
body: JSON.stringify({ identity, email: identity, provider: providerConfigKey })
|
|
20594
|
+
});
|
|
20595
|
+
return sanitizeConnectSession(body);
|
|
20596
|
+
}
|
|
20597
|
+
async function createNangoReconnectSession(identity, connectionId) {
|
|
20598
|
+
const body = await controlRequest("/api/internal/nango/reconnect-session", {
|
|
20599
|
+
method: "POST",
|
|
20600
|
+
body: JSON.stringify({ identity, connectionId, email: identity })
|
|
20601
|
+
});
|
|
20602
|
+
return sanitizeConnectSession(body);
|
|
20603
|
+
}
|
|
20604
|
+
function sanitizeBindingRows(body, defaultScheduleActionId) {
|
|
20605
|
+
const data = unwrapData(body);
|
|
20606
|
+
const flattened = [];
|
|
20607
|
+
if (isRecord(data) && isRecord(data.bindings) && !Array.isArray(data.bindings)) {
|
|
20608
|
+
for (const [scheduleActionId, value] of Object.entries(data.bindings)) {
|
|
20609
|
+
if (Array.isArray(value)) value.forEach((row) => flattened.push({ row, scheduleActionId }));
|
|
20610
|
+
}
|
|
20611
|
+
} else {
|
|
20612
|
+
const rows = arrayFromPayload(data, ["bindings", "connections"]);
|
|
20613
|
+
for (const row of rows) {
|
|
20614
|
+
if (isRecord(row) && Array.isArray(row.connections)) {
|
|
20615
|
+
const scheduleActionId = firstString(row, ["scheduleActionId", "schedule_action_id", "scheduleId", "schedule_id"]) || defaultScheduleActionId;
|
|
20616
|
+
row.connections.forEach((connection) => flattened.push({ row: connection, scheduleActionId }));
|
|
20617
|
+
} else {
|
|
20618
|
+
flattened.push({ row, scheduleActionId: defaultScheduleActionId });
|
|
20619
|
+
}
|
|
20620
|
+
}
|
|
20621
|
+
}
|
|
20622
|
+
const result = [];
|
|
20623
|
+
for (const item of flattened) {
|
|
20624
|
+
if (!isRecord(item.row)) continue;
|
|
20625
|
+
const connectionId = firstString(item.row, ["connectionId", "connection_id"]);
|
|
20626
|
+
const providerConfigKey = firstString(item.row, ["providerConfigKey", "provider_config_key", "integrationId", "integration_id", "provider"]);
|
|
20627
|
+
if (!connectionId || !providerConfigKey) continue;
|
|
20628
|
+
result.push({
|
|
20629
|
+
scheduleActionId: firstString(item.row, ["scheduleActionId", "schedule_action_id", "scheduleId", "schedule_id"]) || item.scheduleActionId,
|
|
20630
|
+
connectionId,
|
|
20631
|
+
providerConfigKey,
|
|
20632
|
+
allowedTools: cleanTools(item.row.allowedTools ?? item.row.allowed_tools ?? item.row.allowedCapabilities ?? item.row.allowed_capabilities),
|
|
20633
|
+
label: firstString(item.row, ["label", "accountLabel", "account_label", "displayName", "display_name"])
|
|
20634
|
+
});
|
|
20635
|
+
}
|
|
20636
|
+
return result;
|
|
20637
|
+
}
|
|
20638
|
+
async function getScheduleConnectionBindings(identity, scheduleActionId) {
|
|
20639
|
+
const query = new URLSearchParams({ identity, scheduleId: scheduleActionId });
|
|
20640
|
+
const body = await controlRequest(`/api/internal/nango/bindings?${query.toString()}`);
|
|
20641
|
+
return sanitizeBindingRows(body, scheduleActionId);
|
|
20642
|
+
}
|
|
20643
|
+
async function putScheduleConnectionBindings(identity, scheduleActionId, connections) {
|
|
20644
|
+
const body = await controlRequest("/api/internal/nango/bindings", {
|
|
20645
|
+
method: "PUT",
|
|
20646
|
+
body: JSON.stringify({
|
|
20647
|
+
identity,
|
|
20648
|
+
scheduleId: scheduleActionId,
|
|
20649
|
+
connections: connections.map((connection) => ({
|
|
20650
|
+
connectionId: connection.connectionId,
|
|
20651
|
+
allowedTools: connection.allowedTools
|
|
20652
|
+
}))
|
|
20653
|
+
})
|
|
20654
|
+
});
|
|
20655
|
+
const sanitized = sanitizeBindingRows(body, scheduleActionId);
|
|
20656
|
+
const expectedByConnectionId = new Map(connections.map((connection) => [
|
|
20657
|
+
connection.connectionId,
|
|
20658
|
+
{
|
|
20659
|
+
providerConfigKey: connection.providerConfigKey,
|
|
20660
|
+
allowedTools: [...connection.allowedTools].sort()
|
|
20661
|
+
}
|
|
20662
|
+
]));
|
|
20663
|
+
const responseMatchesRequest = sanitized.length === connections.length && sanitized.every((binding) => {
|
|
20664
|
+
const expected = expectedByConnectionId.get(binding.connectionId);
|
|
20665
|
+
return !!expected && binding.scheduleActionId === scheduleActionId && binding.providerConfigKey === expected.providerConfigKey && [...binding.allowedTools].sort().join("\0") === expected.allowedTools.join("\0");
|
|
20666
|
+
});
|
|
20667
|
+
if (!responseMatchesRequest) {
|
|
20668
|
+
throw new NangoControlError("Scheduled service connection control returned an invalid binding response.");
|
|
20669
|
+
}
|
|
20670
|
+
return sanitized;
|
|
20671
|
+
}
|
|
20672
|
+
async function deleteScheduleConnectionBindings(identity, scheduleActionId) {
|
|
20673
|
+
await controlRequest("/api/internal/nango/bindings", {
|
|
20674
|
+
method: "DELETE",
|
|
20675
|
+
body: JSON.stringify({ identity, scheduleId: scheduleActionId })
|
|
20676
|
+
});
|
|
20677
|
+
}
|
|
20678
|
+
async function setScheduleConnectionActionsEnabled(identity, connectionId, enabled) {
|
|
20679
|
+
const body = await controlRequest("/api/internal/nango/connections/actions/enable", {
|
|
20680
|
+
method: "POST",
|
|
20681
|
+
body: JSON.stringify({ identity, connectionId, enabled })
|
|
20682
|
+
});
|
|
20683
|
+
const data = unwrapData(body);
|
|
20684
|
+
if (!isRecord(data) || !isRecord(data.connection)) return enabled;
|
|
20685
|
+
return data.connection.actionsEnabled === true;
|
|
20686
|
+
}
|
|
20687
|
+
async function callScheduleConnectionAction(identity, connectionId, input) {
|
|
20688
|
+
const body = await controlRequest("/api/internal/nango/connections/actions/call", {
|
|
20689
|
+
method: "POST",
|
|
20690
|
+
body: JSON.stringify({ identity, connectionId, input })
|
|
20691
|
+
});
|
|
20692
|
+
const data = unwrapData(body);
|
|
20693
|
+
return isRecord(data) ? data.result ?? data : data;
|
|
20694
|
+
}
|
|
20695
|
+
|
|
20396
20696
|
// src/api/server.ts
|
|
20397
20697
|
var secureCookies2 = process.env.NODE_ENV === "production" || process.env.VERCEL === "1";
|
|
20398
20698
|
var isProduction2 = secureCookies2;
|
|
@@ -21005,6 +21305,20 @@ app.post("/schedule-checkout", auth2, async (c) => {
|
|
|
21005
21305
|
return c.json({ error: message }, 500);
|
|
21006
21306
|
}
|
|
21007
21307
|
});
|
|
21308
|
+
function scheduleConnectionError(c, err, fallback) {
|
|
21309
|
+
if (err instanceof ScheduleConnectionValidationError) return c.json({ ok: false, error: err.message }, 400);
|
|
21310
|
+
if (err instanceof NangoControlError) return c.json({ ok: false, error: err.message }, err.status);
|
|
21311
|
+
console.error("[schedule-connections]", err instanceof Error ? err.message : String(err));
|
|
21312
|
+
return c.json({ ok: false, error: fallback }, 502);
|
|
21313
|
+
}
|
|
21314
|
+
function providerConfigKeyFrom(value) {
|
|
21315
|
+
if (typeof value !== "string") return null;
|
|
21316
|
+
const key = value.trim();
|
|
21317
|
+
return key && key.length <= 200 ? key : null;
|
|
21318
|
+
}
|
|
21319
|
+
function bindingsForSchedule(bindings, scheduleActionId) {
|
|
21320
|
+
return bindings.filter((binding) => binding.scheduleActionId === scheduleActionId);
|
|
21321
|
+
}
|
|
21008
21322
|
app.get("/schedule-status", auth2, async (c) => {
|
|
21009
21323
|
try {
|
|
21010
21324
|
const user = c.get("user");
|
|
@@ -21018,6 +21332,103 @@ app.get("/schedule-status", auth2, async (c) => {
|
|
|
21018
21332
|
return c.json({ error: message }, 500);
|
|
21019
21333
|
}
|
|
21020
21334
|
});
|
|
21335
|
+
app.get("/schedule-connection-catalog", auth2, async (c) => {
|
|
21336
|
+
try {
|
|
21337
|
+
return c.json({ ok: true, catalog: await getNangoCatalog() });
|
|
21338
|
+
} catch (err) {
|
|
21339
|
+
return scheduleConnectionError(c, err, "Unable to load the service connection catalog.");
|
|
21340
|
+
}
|
|
21341
|
+
});
|
|
21342
|
+
app.get("/schedule-connections", auth2, async (c) => {
|
|
21343
|
+
try {
|
|
21344
|
+
const user = c.get("user");
|
|
21345
|
+
return c.json({ ok: true, connections: await getNangoConnections(user.email) });
|
|
21346
|
+
} catch (err) {
|
|
21347
|
+
return scheduleConnectionError(c, err, "Unable to load service connections.");
|
|
21348
|
+
}
|
|
21349
|
+
});
|
|
21350
|
+
app.post("/schedule-connections/session", auth2, async (c) => {
|
|
21351
|
+
const user = c.get("user");
|
|
21352
|
+
const body = await c.req.json().catch(() => ({}));
|
|
21353
|
+
const providerConfigKey = providerConfigKeyFrom(body.providerConfigKey);
|
|
21354
|
+
if (!providerConfigKey) return c.json({ ok: false, error: "providerConfigKey is required." }, 400);
|
|
21355
|
+
try {
|
|
21356
|
+
const session = await createNangoConnectSession(user.email, providerConfigKey);
|
|
21357
|
+
return c.json({ ok: true, ...session });
|
|
21358
|
+
} catch (err) {
|
|
21359
|
+
return scheduleConnectionError(c, err, "Unable to start the service connection flow.");
|
|
21360
|
+
}
|
|
21361
|
+
});
|
|
21362
|
+
app.post("/schedule-connections/:id/reconnect-session", auth2, async (c) => {
|
|
21363
|
+
const user = c.get("user");
|
|
21364
|
+
try {
|
|
21365
|
+
const session = await createNangoReconnectSession(user.email, c.req.param("id"));
|
|
21366
|
+
return c.json({ ok: true, ...session });
|
|
21367
|
+
} catch (err) {
|
|
21368
|
+
return scheduleConnectionError(c, err, "Unable to start the service reconnection flow.");
|
|
21369
|
+
}
|
|
21370
|
+
});
|
|
21371
|
+
app.post("/schedule-connections/:id/enable-actions", auth2, async (c) => {
|
|
21372
|
+
const user = c.get("user");
|
|
21373
|
+
const body = await c.req.json().catch(() => ({}));
|
|
21374
|
+
if (typeof body.enabled !== "boolean") return c.json({ ok: false, error: "enabled must be a boolean." }, 400);
|
|
21375
|
+
try {
|
|
21376
|
+
const actionsEnabled = await setScheduleConnectionActionsEnabled(user.email, c.req.param("id"), body.enabled);
|
|
21377
|
+
return c.json({ ok: true, actionsEnabled });
|
|
21378
|
+
} catch (err) {
|
|
21379
|
+
return scheduleConnectionError(c, err, "Unable to update this connection's action setting.");
|
|
21380
|
+
}
|
|
21381
|
+
});
|
|
21382
|
+
app.post("/schedule-connections/actions/slack/send-message", auth2, async (c) => {
|
|
21383
|
+
const user = c.get("user");
|
|
21384
|
+
const body = await c.req.json().catch(() => ({}));
|
|
21385
|
+
const connectionId = providerConfigKeyFrom(body.connectionId);
|
|
21386
|
+
if (!connectionId || typeof body.channel !== "string" || typeof body.text !== "string") {
|
|
21387
|
+
return c.json({ ok: false, error: "connectionId, channel, and text are required." }, 400);
|
|
21388
|
+
}
|
|
21389
|
+
try {
|
|
21390
|
+
const result = await callScheduleConnectionAction(user.email, connectionId, { channel: body.channel, text: body.text });
|
|
21391
|
+
return c.json({ ok: true, result });
|
|
21392
|
+
} catch (err) {
|
|
21393
|
+
return scheduleConnectionError(c, err, "Unable to send the Slack message.");
|
|
21394
|
+
}
|
|
21395
|
+
});
|
|
21396
|
+
app.post("/schedule-connections/actions/gmail/send-message", auth2, async (c) => {
|
|
21397
|
+
const user = c.get("user");
|
|
21398
|
+
const body = await c.req.json().catch(() => ({}));
|
|
21399
|
+
const connectionId = providerConfigKeyFrom(body.connectionId);
|
|
21400
|
+
if (!connectionId || typeof body.to !== "string" || typeof body.subject !== "string" || typeof body.body !== "string") {
|
|
21401
|
+
return c.json({ ok: false, error: "connectionId, to, subject, and body are required." }, 400);
|
|
21402
|
+
}
|
|
21403
|
+
try {
|
|
21404
|
+
const result = await callScheduleConnectionAction(user.email, connectionId, { to: body.to, subject: body.subject, body: body.body });
|
|
21405
|
+
return c.json({ ok: true, result });
|
|
21406
|
+
} catch (err) {
|
|
21407
|
+
return scheduleConnectionError(c, err, "Unable to send the email.");
|
|
21408
|
+
}
|
|
21409
|
+
});
|
|
21410
|
+
app.post("/schedule-connections/actions/google-calendar/create-event", auth2, async (c) => {
|
|
21411
|
+
const user = c.get("user");
|
|
21412
|
+
const body = await c.req.json().catch(() => ({}));
|
|
21413
|
+
const connectionId = providerConfigKeyFrom(body.connectionId);
|
|
21414
|
+
if (!connectionId || typeof body.summary !== "string" || typeof body.startDateTime !== "string" || typeof body.endDateTime !== "string") {
|
|
21415
|
+
return c.json({ ok: false, error: "connectionId, summary, startDateTime, and endDateTime are required." }, 400);
|
|
21416
|
+
}
|
|
21417
|
+
const timeZone = typeof body.timeZone === "string" ? body.timeZone : void 0;
|
|
21418
|
+
try {
|
|
21419
|
+
const result = await callScheduleConnectionAction(user.email, connectionId, {
|
|
21420
|
+
calendarId: typeof body.calendarId === "string" ? body.calendarId : "primary",
|
|
21421
|
+
summary: body.summary,
|
|
21422
|
+
description: typeof body.description === "string" ? body.description : void 0,
|
|
21423
|
+
location: typeof body.location === "string" ? body.location : void 0,
|
|
21424
|
+
start: { dateTime: body.startDateTime, timeZone },
|
|
21425
|
+
end: { dateTime: body.endDateTime, timeZone }
|
|
21426
|
+
});
|
|
21427
|
+
return c.json({ ok: true, result });
|
|
21428
|
+
} catch (err) {
|
|
21429
|
+
return scheduleConnectionError(c, err, "Unable to create the calendar event.");
|
|
21430
|
+
}
|
|
21431
|
+
});
|
|
21021
21432
|
app.post("/schedule-link", auth2, async (c) => {
|
|
21022
21433
|
try {
|
|
21023
21434
|
const user = c.get("user");
|
|
@@ -21036,15 +21447,58 @@ app.get("/schedule-actions", auth2, async (c) => {
|
|
|
21036
21447
|
const { key, error } = await getOrCreateUserMemoryKey(user);
|
|
21037
21448
|
if (!key) return c.json({ ok: false, error: error ?? "memory unavailable" }, 503);
|
|
21038
21449
|
const r = await memoryCall("listScheduledActionsTool", {}, key);
|
|
21039
|
-
return c.json(r);
|
|
21450
|
+
if (!r.ok || !Array.isArray(r.actions)) return c.json(r);
|
|
21451
|
+
try {
|
|
21452
|
+
const actions = await Promise.all(r.actions.map(async (action) => {
|
|
21453
|
+
const id = typeof action.id === "string" ? action.id : "";
|
|
21454
|
+
if (!id) return { ...action, connections: [] };
|
|
21455
|
+
const bindings = await getScheduleConnectionBindings(user.email, id);
|
|
21456
|
+
return { ...action, connections: bindingsForSchedule(bindings, id) };
|
|
21457
|
+
}));
|
|
21458
|
+
return c.json({
|
|
21459
|
+
...r,
|
|
21460
|
+
actions
|
|
21461
|
+
});
|
|
21462
|
+
} catch (err) {
|
|
21463
|
+
console.warn("[schedule-actions] connection bindings unavailable:", err instanceof Error ? err.message : String(err));
|
|
21464
|
+
return c.json({
|
|
21465
|
+
...r,
|
|
21466
|
+
actions: r.actions.map((action) => ({ ...action, connections: [] })),
|
|
21467
|
+
connectionBindingsUnavailable: true
|
|
21468
|
+
});
|
|
21469
|
+
}
|
|
21040
21470
|
});
|
|
21041
21471
|
app.post("/schedule-actions", auth2, async (c) => {
|
|
21042
21472
|
const user = c.get("user");
|
|
21043
21473
|
const { key, error } = await getOrCreateUserMemoryKey(user);
|
|
21044
21474
|
if (!key) return c.json({ ok: false, error: error ?? "memory unavailable" }, 503);
|
|
21045
21475
|
const body = await c.req.json().catch(() => ({}));
|
|
21046
|
-
const
|
|
21047
|
-
|
|
21476
|
+
const hasConnections = Object.prototype.hasOwnProperty.call(body, "connections");
|
|
21477
|
+
let connections;
|
|
21478
|
+
try {
|
|
21479
|
+
connections = sanitizeScheduleConnectionSelections(body.connections);
|
|
21480
|
+
} catch (err) {
|
|
21481
|
+
return scheduleConnectionError(c, err, "Invalid service connections.");
|
|
21482
|
+
}
|
|
21483
|
+
const actionBody = { ...body };
|
|
21484
|
+
delete actionBody.connections;
|
|
21485
|
+
const r = await memoryCall("createScheduledActionTool", actionBody, key);
|
|
21486
|
+
if (!r.ok || !hasConnections) return c.json(r);
|
|
21487
|
+
if (connections.length === 0) return c.json({ ...r, connections: [] });
|
|
21488
|
+
if (!r.id) {
|
|
21489
|
+
console.error("[schedule-actions] create returned no id while service connections were requested");
|
|
21490
|
+
return c.json({ ok: false, error: "The scheduled action was created without a usable id; service connections were not attached." }, 502);
|
|
21491
|
+
}
|
|
21492
|
+
try {
|
|
21493
|
+
const bound = await putScheduleConnectionBindings(user.email, r.id, connections);
|
|
21494
|
+
return c.json({ ...r, connections: bound });
|
|
21495
|
+
} catch (err) {
|
|
21496
|
+
const rollback = await memoryCall("deleteScheduledActionTool", { id: r.id }, key).catch(() => ({ ok: false }));
|
|
21497
|
+
if (!rollback.ok) console.error("[schedule-actions] failed to compensate action after connection binding failure", r.id);
|
|
21498
|
+
const response = scheduleConnectionError(c, err, "The scheduled action was not created because its service connections could not be attached.");
|
|
21499
|
+
response.headers.set("x-schedule-create-rolled-back", rollback.ok ? "true" : "false");
|
|
21500
|
+
return response;
|
|
21501
|
+
}
|
|
21048
21502
|
});
|
|
21049
21503
|
app.post("/schedule-actions/propose", auth2, async (c) => {
|
|
21050
21504
|
const user = c.get("user");
|
|
@@ -21054,6 +21508,33 @@ app.post("/schedule-actions/propose", auth2, async (c) => {
|
|
|
21054
21508
|
const r = await memoryCall("proposeScheduledActionTool", body, key);
|
|
21055
21509
|
return c.json(r);
|
|
21056
21510
|
});
|
|
21511
|
+
app.get("/schedule-actions/:id/connections", auth2, async (c) => {
|
|
21512
|
+
try {
|
|
21513
|
+
const user = c.get("user");
|
|
21514
|
+
const scheduleActionId = c.req.param("id");
|
|
21515
|
+
const bindings = await getScheduleConnectionBindings(user.email, scheduleActionId);
|
|
21516
|
+
return c.json({ ok: true, connections: bindingsForSchedule(bindings, scheduleActionId) });
|
|
21517
|
+
} catch (err) {
|
|
21518
|
+
return scheduleConnectionError(c, err, "Unable to load scheduled action service connections.");
|
|
21519
|
+
}
|
|
21520
|
+
});
|
|
21521
|
+
app.put("/schedule-actions/:id/connections", auth2, async (c) => {
|
|
21522
|
+
const user = c.get("user");
|
|
21523
|
+
const body = await c.req.json().catch(() => ({}));
|
|
21524
|
+
let connections;
|
|
21525
|
+
try {
|
|
21526
|
+
connections = sanitizeScheduleConnectionSelections(body.connections);
|
|
21527
|
+
} catch (err) {
|
|
21528
|
+
return scheduleConnectionError(c, err, "Invalid service connections.");
|
|
21529
|
+
}
|
|
21530
|
+
try {
|
|
21531
|
+
const scheduleActionId = c.req.param("id");
|
|
21532
|
+
const bindings = await putScheduleConnectionBindings(user.email, scheduleActionId, connections);
|
|
21533
|
+
return c.json({ ok: true, connections: bindings });
|
|
21534
|
+
} catch (err) {
|
|
21535
|
+
return scheduleConnectionError(c, err, "Unable to update scheduled action service connections.");
|
|
21536
|
+
}
|
|
21537
|
+
});
|
|
21057
21538
|
app.post("/schedule-actions/:id/pause", auth2, async (c) => {
|
|
21058
21539
|
const user = c.get("user");
|
|
21059
21540
|
const { key, error } = await getOrCreateUserMemoryKey(user);
|
|
@@ -21072,8 +21553,16 @@ app.delete("/schedule-actions/:id", auth2, async (c) => {
|
|
|
21072
21553
|
const user = c.get("user");
|
|
21073
21554
|
const { key, error } = await getOrCreateUserMemoryKey(user);
|
|
21074
21555
|
if (!key) return c.json({ ok: false, error: error ?? "memory unavailable" }, 503);
|
|
21075
|
-
const
|
|
21076
|
-
|
|
21556
|
+
const scheduleActionId = c.req.param("id");
|
|
21557
|
+
const r = await memoryCall("deleteScheduledActionTool", { id: scheduleActionId }, key);
|
|
21558
|
+
if (!r.ok) return c.json(r);
|
|
21559
|
+
try {
|
|
21560
|
+
await deleteScheduleConnectionBindings(user.email, scheduleActionId);
|
|
21561
|
+
return c.json(r);
|
|
21562
|
+
} catch (err) {
|
|
21563
|
+
console.error("[schedule-actions] action deleted but connection binding cleanup failed", scheduleActionId, err instanceof Error ? err.message : String(err));
|
|
21564
|
+
return c.json({ ...r, connectionBindingCleanupPending: true });
|
|
21565
|
+
}
|
|
21077
21566
|
});
|
|
21078
21567
|
app.post("/schedule-link/revoke", auth2, async (c) => {
|
|
21079
21568
|
try {
|
|
@@ -22126,4 +22615,4 @@ app.get("/blog/:slug/", (c) => {
|
|
|
22126
22615
|
export {
|
|
22127
22616
|
app
|
|
22128
22617
|
};
|
|
22129
|
-
//# sourceMappingURL=server-
|
|
22618
|
+
//# sourceMappingURL=server-E34MNLI3.js.map
|