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.
@@ -0,0 +1,7 @@
1
+ // src/version.ts
2
+ var PACKAGE_VERSION = "0.9.0";
3
+
4
+ export {
5
+ PACKAGE_VERSION
6
+ };
7
+ //# sourceMappingURL=chunk-RD5JOAWS.js.map
@@ -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-ITJC3NN4.js";
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-3MKSXDJ7.js";
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 r = await memoryCall("createScheduledActionTool", body, key);
21047
- return c.json(r);
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 r = await memoryCall("deleteScheduledActionTool", { id: c.req.param("id") }, key);
21076
- return c.json(r);
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-WBDVVHR6.js.map
22618
+ //# sourceMappingURL=server-E34MNLI3.js.map