mcp-scraper 0.45.0 → 0.46.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.
@@ -33664,7 +33664,7 @@ async function extractAISurfacesFromDocument(config) {
33664
33664
  }
33665
33665
  return null;
33666
33666
  }
33667
- function cleanText(target) {
33667
+ function cleanText2(target) {
33668
33668
  if (!target) return null;
33669
33669
  const clone = target.cloneNode(true);
33670
33670
  clone.querySelectorAll([
@@ -33791,14 +33791,14 @@ async function extractAISurfacesFromDocument(config) {
33791
33791
  const contentSubtree = aioRoot.querySelector(selectors.aio.contentSubtree);
33792
33792
  const showMore = aioRoot.querySelector(selectors.aio.showMoreButton);
33793
33793
  aioFullyExpanded = controller?.getAttribute("data-trnct") === "false" || showMore?.getAttribute("aria-expanded") === "true" || !showMore;
33794
- aioText = cleanText(contentSubtree ?? controller ?? aioRoot);
33794
+ aioText = cleanText2(contentSubtree ?? controller ?? aioRoot);
33795
33795
  aioSections = (aioText ?? "").split("\n").map((line) => line.trim()).filter((line) => /^\d+\.\s+.+/.test(line));
33796
33796
  aioCitations = extractCitations(aioRoot);
33797
33797
  }
33798
33798
  const aimRoot = document.querySelector(selectors.aim.root);
33799
33799
  const aimRootDetected = surface === "aim" && !!aimRoot;
33800
33800
  const aimContainer = aimRoot?.closest(selectors.aim.wrapper) ?? aimRoot;
33801
- const rawAimText = aimRootDetected ? cleanText(aimContainer) : null;
33801
+ const rawAimText = aimRootDetected ? cleanText2(aimContainer) : null;
33802
33802
  const aimDetected = aimRootDetected && hasSubstantiveContent(rawAimText);
33803
33803
  const aimText = aimDetected ? rawAimText : null;
33804
33804
  const aimCitations = aimDetected ? extractCitations(aimContainer) : [];
@@ -33872,7 +33872,7 @@ var init_PAAExtractor = __esm({
33872
33872
  async extractVisibleItems(page) {
33873
33873
  const sels = PAASelectors;
33874
33874
  const raw = await page.evaluate((selectors) => {
33875
- function cleanText(el2) {
33875
+ function cleanText2(el2) {
33876
33876
  if (!el2) return "";
33877
33877
  const parts = [];
33878
33878
  for (const n of el2.childNodes) {
@@ -33882,7 +33882,7 @@ var init_PAAExtractor = __esm({
33882
33882
  } else if (n.tagName === "STYLE" || n.tagName === "SCRIPT") {
33883
33883
  continue;
33884
33884
  } else {
33885
- const text2 = cleanText(n);
33885
+ const text2 = cleanText2(n);
33886
33886
  if (text2) parts.push(text2);
33887
33887
  }
33888
33888
  }
@@ -33936,7 +33936,7 @@ var init_PAAExtractor = __esm({
33936
33936
  }
33937
33937
  return {
33938
33938
  question: pair.getAttribute(selectors.itemDataQ) || pair.getAttribute(selectors.itemDataInitQ) || pair.querySelector(selectors.itemQuestionEl)?.innerText?.trim() || "",
33939
- answer: cleanText(answerElement) || void 0,
33939
+ answer: cleanText2(answerElement) || void 0,
33940
33940
  sourceTitle,
33941
33941
  sourceSite,
33942
33942
  sourceCite
@@ -41371,7 +41371,7 @@ var PACKAGE_VERSION;
41371
41371
  var init_version = __esm({
41372
41372
  "src/version.ts"() {
41373
41373
  "use strict";
41374
- PACKAGE_VERSION = "0.45.0";
41374
+ PACKAGE_VERSION = "0.46.0";
41375
41375
  }
41376
41376
  });
41377
41377
 
@@ -41454,6 +41454,18 @@ seam is noted so you can chain them.
41454
41454
  Use saved filters when a person wants their MCP to operate inside a reading room, trail, source scope,
41455
41455
  category, tag bundle, media constraint, or other personalized subset of the shared Commons graph.
41456
41456
 
41457
+ ## Transparent Commons user publications
41458
+ - A user-owned editorial publication is separate from the neutral public wiki. Start with
41459
+ **commons_prepare_publication** to normalize the requested subdomain, check availability, and read the live
41460
+ ownership contract. Then call **commons_validate_publication** with operation \`claim\` before
41461
+ **commons_claim_publication**.
41462
+ - Each account may claim one stable name at \`https://{name}.transparent-commons.cc\`. Claims require an
41463
+ idempotency key and never publish content by themselves.
41464
+ - To publish, use **editorial_reading_room_guide** when needed, research and author the complete source-grounded
41465
+ edition, validate it with **commons_validate_publication** operation \`publish\`, then call
41466
+ **commons_publish_editorial**. The publish result returns the permanent publication, archive, and edition URLs.
41467
+ - Use **commons_get_publication** to recover the caller's publication and current edition revisions. Existing
41468
+ edition edits require the returned baseRevision. Only the account that claimed the subdomain can publish to it.
41457
41469
 
41458
41470
  ## Google Maps
41459
41471
  - Find multiple places/competitors/prospects -> **maps_search** (returns \`results[]\` with name,
@@ -42175,7 +42187,7 @@ var init_contracts = __esm({
42175
42187
  });
42176
42188
 
42177
42189
  // src/mcp/mcp-tool-schemas.ts
42178
- var import_zod42, WEBSITE_URL_OR_DOMAIN_ERROR, WebsiteUrlOrDomainSchema, HarvestPaaInputSchema, ExtractUrlBaseInputSchema, ExtractUrlInputSchema, ExtractUrlLocalInputSchema, DiffPageBaseInputSchema, DiffPageInputSchema, DiffPageLocalInputSchema, MapSiteUrlsInputSchema, MapWaybackSnapshotsInputSchema, ExtractSiteInputSchema, AuditSiteInputSchema, CheckSiteExportInputSchema, ArchiveReadInputSchema, YoutubeHarvestInputSchema, YoutubeTranscribeInputSchema, FacebookPageIntelInputSchema, FacebookAdSearchInputSchema, RedditThreadInputSchema, RedditTrendingInputSchema, VideoFrameAnalysisInputSchema, VideoFrameAnalysisStatusInputSchema, FacebookAdTranscribeInputSchema, FacebookVideoTranscribeInputSchema, GoogleAdsSearchInputSchema, GoogleAdsPageIntelInputSchema, GoogleAdsTranscribeInputSchema, InstagramProfileContentInputSchema, InstagramMediaDownloadInputSchema, MapsPlaceIntelInputSchema, TrustpilotReviewsInputSchema, G2ReviewsInputSchema, ReviewCardSchema, MapsSearchInputSchema, DirectoryWorkflowInputSchema, LocationMarketsInputSchema, CommonsSearchEntitiesInputSchema, CommonsGetEntityInputSchema, CommonsFeaturedImageInputSchema, CommonsMediaInputSchema, CommonsCitationInputSchema, CommonsSourceInputSchema, CommonsRelatedLinkInputSchema, CommonsPrepareEntityInputSchema, CommonsSubmitEntityInputSchema, CommonsValidateEntityInputSchema, CommonsGetEntityLedgerInputSchema, CommonsSaveFilterInputSchema, CommonsListFiltersInputSchema, CommonsListNeedsLinksInputSchema, CommonsGenericOutputSchema, DirectoryWorkflowStatusInputSchema, LocalSourcebookSubmitInputSchema, LocalSourcebookCategorySchema, LocalSourcebookSchemaTypeInputSchema, LocalSourcebookTagCandidateObjectSchema, LocalSourcebookTagDecisionObjectSchema, LocalSourcebookIdentityObjectSchema, GetLocalSourcebookContractInputSchema, ListLocalSourcebookTagsInputSchema, ResolveLocalSourcebookTagsInputSchema, PrepareLocalSourcebookWriteInputSchema, ValidateLocalSourcebookWriteInputSchema, LocalSourcebookCaptureInputSchema, LocalSourcebookSubmissionStatusInputSchema, LocalSourcebookRefreshInputSchema, LocalSourcebookOutputSchema, ArtifactPointerOutputSchema, EditorialReadingRoomSiteSchema, EditorialReadingRoomArticleSchema, EditorialReadingRoomGuideInputSchema, EditorialReadingRoomGuideOutputSchema, CreateEditorialReadingRoomInputSchema, EditorialReadingRoomArtifactSchema, CreateEditorialReadingRoomOutputSchema, RenewEditorialReadingRoomDownloadInputSchema, RenewEditorialReadingRoomDownloadOutputSchema, RankTrackerModeSchema, RankTrackerBlueprintInputSchema, NullableString, MapsSearchAttemptOutput, MapsSearchOutputSchema, DirectoryMapsBusinessOutput, DirectoryCsvArtifactOutput, DirectoryWorkflowOutputSchema, LocationDatasetProvenanceOutput, LocationMarketsOutputSchema, RankTrackerToolPlanOutput, RankTrackerTableOutput, RankTrackerCronJobOutput, RankTrackerBlueprintOutputSchema, OrganicResultOutput, AiOverviewOutput, EntityIdsOutput, HarvestPaaOutputSchema, SearchSerpOutputSchema, ExtractUrlOutputSchema, DiffPageOutputSchema, ExtractSiteOutputSchema, AuditSiteOutputSchema, CheckSiteExportOutputSchema, ArchiveEntryOutputSchema, ArchiveReadOutputSchema, MapsPlaceIntelOutputSchema, TrustpilotReviewsOutputSchema, G2ReviewsOutputSchema, CreditsInfoOutputSchema, MapSiteUrlsOutputSchema, WaybackCaptureOutputSchema, MapWaybackSnapshotsOutputSchema, YoutubeHarvestOutputSchema, FacebookAdSearchOutputSchema, VideoFrameAnalysisOutputSchema, VideoFrameAnalysisStatusOutputSchema, RedditThreadOutputSchema, RedditTrendingOutputSchema, FacebookPageIntelOutputSchema, GoogleAdsSearchOutputSchema, GoogleAdsPageIntelOutputSchema, TranscriptSignalOutput, FacebookVideoTranscribeOutputSchema, TranscriptChunkOutput, InstagramBrowserOutput, InstagramPaginationOutput, InstagramProfileContentOutputSchema, InstagramMediaTrackOutput, InstagramDownloadOutput, InstagramMediaDownloadOutputSchema, YoutubeTranscribeOutputSchema, FacebookAdTranscribeOutputSchema, GoogleAdsTranscribeOutputSchema, CaptureSerpSnapshotOutputSchema, CaptureSerpPageSnapshotsOutputSchema, CreditsInfoInputSchema, WorkflowIdSchema2, WorkflowListInputSchema, WorkflowSuggestInputSchema, WorkflowRunInputSchema, WorkflowStepInputSchema, WorkflowStatusInputSchema, WorkflowArtifactReadInputSchema, WorkflowRecipeOutput, WorkflowDefinitionOutput, WorkflowArtifactOutput, WorkflowListOutputSchema, WorkflowSuggestOutputSchema, WorkflowRunOutputSchema, WorkflowStepOutputSchema, WorkflowStatusOutputSchema, WorkflowArtifactReadOutputSchema, SearchSerpInputSchema, CaptureSerpSnapshotInputSchema, ScreenshotInputSchema, CaptureSerpPageSnapshotsInputSchema, ReportArtifactReadInputSchema, ReportArtifactReadOutputSchema, ListServiceConnectionsInputSchema, ListServiceConnectionsOutputSchema, TestServiceConnectionInputSchema, TestServiceConnectionOutputSchema, ReadServiceConnectionInputSchema, ReadServiceConnectionOutputSchema, MetaAdCreativeMediaInputSchema, MetaAdCreativeMediaOutputSchema, ImportServiceConnectionToMemoryInputSchema, ImportServiceConnectionToMemoryOutputSchema, DescribeServiceConnectionToolInputSchema, DescribeServiceConnectionToolOutputSchema, ConnectedDataContinuationSchema, ExportConnectedServiceDataInputSchema, ConnectedDataArtifactSchema, ExportConnectedServiceDataOutputSchema, SearchConsoleTableColumnSchema, SearchConsoleTableFilterSchema, ExportSearchConsoleTableDataInputSchema, ExportSearchConsoleTableDataOutputSchema, RenewConnectedDataExportDownloadInputSchema, RenewConnectedDataExportDownloadOutputSchema, CallServiceConnectionActionInputSchema, CallServiceConnectionActionOutputSchema, SetScheduledActionConnectionsInputSchema, SetScheduledActionConnectionsOutputSchema, SlackSendMessageInputSchema, SlackSendMessageOutputSchema, GmailSendMessageInputSchema, GmailSendMessageOutputSchema, GmailSearchContactsInputSchema, GmailSearchContactsOutputSchema, GoogleCalendarCreateEventInputSchema, GoogleCalendarCreateEventOutputSchema, ZoomCreateMeetingInputSchema, ZoomCreateMeetingOutputSchema;
42190
+ var import_zod42, WEBSITE_URL_OR_DOMAIN_ERROR, WebsiteUrlOrDomainSchema, HarvestPaaInputSchema, ExtractUrlBaseInputSchema, ExtractUrlInputSchema, ExtractUrlLocalInputSchema, DiffPageBaseInputSchema, DiffPageInputSchema, DiffPageLocalInputSchema, MapSiteUrlsInputSchema, MapWaybackSnapshotsInputSchema, ExtractSiteInputSchema, AuditSiteInputSchema, CheckSiteExportInputSchema, ArchiveReadInputSchema, YoutubeHarvestInputSchema, YoutubeTranscribeInputSchema, FacebookPageIntelInputSchema, FacebookAdSearchInputSchema, RedditThreadInputSchema, RedditTrendingInputSchema, VideoFrameAnalysisInputSchema, VideoFrameAnalysisStatusInputSchema, FacebookAdTranscribeInputSchema, FacebookVideoTranscribeInputSchema, GoogleAdsSearchInputSchema, GoogleAdsPageIntelInputSchema, GoogleAdsTranscribeInputSchema, InstagramProfileContentInputSchema, InstagramMediaDownloadInputSchema, MapsPlaceIntelInputSchema, TrustpilotReviewsInputSchema, G2ReviewsInputSchema, ReviewCardSchema, MapsSearchInputSchema, DirectoryWorkflowInputSchema, LocationMarketsInputSchema, CommonsSearchEntitiesInputSchema, CommonsGetEntityInputSchema, CommonsFeaturedImageInputSchema, CommonsMediaInputSchema, CommonsCitationInputSchema, CommonsSourceInputSchema, CommonsRelatedLinkInputSchema, CommonsPrepareEntityInputSchema, CommonsSubmitEntityInputSchema, CommonsValidateEntityInputSchema, CommonsGetEntityLedgerInputSchema, CommonsSaveFilterInputSchema, CommonsListFiltersInputSchema, CommonsListNeedsLinksInputSchema, CommonsGenericOutputSchema, DirectoryWorkflowStatusInputSchema, LocalSourcebookSubmitInputSchema, LocalSourcebookCategorySchema, LocalSourcebookSchemaTypeInputSchema, LocalSourcebookTagCandidateObjectSchema, LocalSourcebookTagDecisionObjectSchema, LocalSourcebookIdentityObjectSchema, GetLocalSourcebookContractInputSchema, ListLocalSourcebookTagsInputSchema, ResolveLocalSourcebookTagsInputSchema, PrepareLocalSourcebookWriteInputSchema, ValidateLocalSourcebookWriteInputSchema, LocalSourcebookCaptureInputSchema, LocalSourcebookSubmissionStatusInputSchema, LocalSourcebookRefreshInputSchema, LocalSourcebookOutputSchema, ArtifactPointerOutputSchema, EditorialReadingRoomSiteSchema, EditorialReadingRoomArticleSchema, EditorialReadingRoomGuideInputSchema, EditorialReadingRoomGuideOutputSchema, CreateEditorialReadingRoomInputSchema, EditorialReadingRoomArtifactSchema, CreateEditorialReadingRoomOutputSchema, RenewEditorialReadingRoomDownloadInputSchema, RenewEditorialReadingRoomDownloadOutputSchema, CommonsPublicationSubdomainSchema, CommonsPreparePublicationInputSchema, CommonsValidatePublicationInputSchema, CommonsClaimPublicationInputSchema, CommonsPublishEditorialInputSchema, CommonsGetPublicationInputSchema, RankTrackerModeSchema, RankTrackerBlueprintInputSchema, NullableString, MapsSearchAttemptOutput, MapsSearchOutputSchema, DirectoryMapsBusinessOutput, DirectoryCsvArtifactOutput, DirectoryWorkflowOutputSchema, LocationDatasetProvenanceOutput, LocationMarketsOutputSchema, RankTrackerToolPlanOutput, RankTrackerTableOutput, RankTrackerCronJobOutput, RankTrackerBlueprintOutputSchema, OrganicResultOutput, AiOverviewOutput, EntityIdsOutput, HarvestPaaOutputSchema, SearchSerpOutputSchema, ExtractUrlOutputSchema, DiffPageOutputSchema, ExtractSiteOutputSchema, AuditSiteOutputSchema, CheckSiteExportOutputSchema, ArchiveEntryOutputSchema, ArchiveReadOutputSchema, MapsPlaceIntelOutputSchema, TrustpilotReviewsOutputSchema, G2ReviewsOutputSchema, CreditsInfoOutputSchema, MapSiteUrlsOutputSchema, WaybackCaptureOutputSchema, MapWaybackSnapshotsOutputSchema, YoutubeHarvestOutputSchema, FacebookAdSearchOutputSchema, VideoFrameAnalysisOutputSchema, VideoFrameAnalysisStatusOutputSchema, RedditThreadOutputSchema, RedditTrendingOutputSchema, FacebookPageIntelOutputSchema, GoogleAdsSearchOutputSchema, GoogleAdsPageIntelOutputSchema, TranscriptSignalOutput, FacebookVideoTranscribeOutputSchema, TranscriptChunkOutput, InstagramBrowserOutput, InstagramPaginationOutput, InstagramProfileContentOutputSchema, InstagramMediaTrackOutput, InstagramDownloadOutput, InstagramMediaDownloadOutputSchema, YoutubeTranscribeOutputSchema, FacebookAdTranscribeOutputSchema, GoogleAdsTranscribeOutputSchema, CaptureSerpSnapshotOutputSchema, CaptureSerpPageSnapshotsOutputSchema, CreditsInfoInputSchema, WorkflowIdSchema2, WorkflowListInputSchema, WorkflowSuggestInputSchema, WorkflowRunInputSchema, WorkflowStepInputSchema, WorkflowStatusInputSchema, WorkflowArtifactReadInputSchema, WorkflowRecipeOutput, WorkflowDefinitionOutput, WorkflowArtifactOutput, WorkflowListOutputSchema, WorkflowSuggestOutputSchema, WorkflowRunOutputSchema, WorkflowStepOutputSchema, WorkflowStatusOutputSchema, WorkflowArtifactReadOutputSchema, SearchSerpInputSchema, CaptureSerpSnapshotInputSchema, ScreenshotInputSchema, CaptureSerpPageSnapshotsInputSchema, ReportArtifactReadInputSchema, ReportArtifactReadOutputSchema, ListServiceConnectionsInputSchema, ListServiceConnectionsOutputSchema, TestServiceConnectionInputSchema, TestServiceConnectionOutputSchema, ReadServiceConnectionInputSchema, ReadServiceConnectionOutputSchema, MetaAdCreativeMediaInputSchema, MetaAdCreativeMediaOutputSchema, ImportServiceConnectionToMemoryInputSchema, ImportServiceConnectionToMemoryOutputSchema, DescribeServiceConnectionToolInputSchema, DescribeServiceConnectionToolOutputSchema, ConnectedDataContinuationSchema, ExportConnectedServiceDataInputSchema, ConnectedDataArtifactSchema, ExportConnectedServiceDataOutputSchema, SearchConsoleTableColumnSchema, SearchConsoleTableFilterSchema, ExportSearchConsoleTableDataInputSchema, ExportSearchConsoleTableDataOutputSchema, RenewConnectedDataExportDownloadInputSchema, RenewConnectedDataExportDownloadOutputSchema, CallServiceConnectionActionInputSchema, CallServiceConnectionActionOutputSchema, SetScheduledActionConnectionsInputSchema, SetScheduledActionConnectionsOutputSchema, SlackSendMessageInputSchema, SlackSendMessageOutputSchema, GmailSendMessageInputSchema, GmailSendMessageOutputSchema, GmailSearchContactsInputSchema, GmailSearchContactsOutputSchema, GoogleCalendarCreateEventInputSchema, GoogleCalendarCreateEventOutputSchema, ZoomCreateMeetingInputSchema, ZoomCreateMeetingOutputSchema;
42179
42191
  var init_mcp_tool_schemas = __esm({
42180
42192
  "src/mcp/mcp-tool-schemas.ts"() {
42181
42193
  "use strict";
@@ -42794,6 +42806,37 @@ var init_mcp_tool_schemas = __esm({
42794
42806
  downloadUrlExpiresAt: import_zod42.z.string(),
42795
42807
  expiresAt: import_zod42.z.string()
42796
42808
  };
42809
+ CommonsPublicationSubdomainSchema = import_zod42.z.string().trim().min(3).max(50).describe("Requested publication name under transparent-commons.cc. The server normalizes spaces to hyphens, rejects reserved names, and enforces one globally unique name per account.");
42810
+ CommonsPreparePublicationInputSchema = {
42811
+ requestedSubdomain: CommonsPublicationSubdomainSchema,
42812
+ title: import_zod42.z.string().trim().min(1).max(140).optional().describe("Reader-facing publication title. Omit to derive it from the chosen subdomain."),
42813
+ description: import_zod42.z.string().trim().max(500).optional().describe("Short description used on the publication archive and discovery surfaces.")
42814
+ };
42815
+ CommonsValidatePublicationInputSchema = {
42816
+ operation: import_zod42.z.enum(["claim", "publish"]).describe("Validate either a new name claim or a finished editorial edition without writing."),
42817
+ requestedSubdomain: CommonsPublicationSubdomainSchema.optional().describe("Publication name to validate when operation is claim."),
42818
+ publicationSubdomain: CommonsPublicationSubdomainSchema.optional().describe("Already claimed publication name to validate when operation is publish."),
42819
+ title: import_zod42.z.string().trim().min(1).max(140).optional(),
42820
+ description: import_zod42.z.string().trim().max(500).optional(),
42821
+ edition: import_zod42.z.object(CreateEditorialReadingRoomInputSchema).strict().optional().describe("Complete source-grounded reading-room payload to validate when operation is publish."),
42822
+ editionSlug: import_zod42.z.string().trim().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).max(80).optional().describe("Stable public edition slug. Defaults to edition.site.slug."),
42823
+ baseRevision: import_zod42.z.number().int().positive().optional().describe("Current edition revision when validating an edit.")
42824
+ };
42825
+ CommonsClaimPublicationInputSchema = {
42826
+ ...CommonsPreparePublicationInputSchema,
42827
+ idempotencyKey: import_zod42.z.string().trim().min(8).max(200).describe("Unique key for this intended claim. Reuse it only when retrying the same claim.")
42828
+ };
42829
+ CommonsPublishEditorialInputSchema = {
42830
+ publicationSubdomain: CommonsPublicationSubdomainSchema.describe("Publication owned by the authenticated account."),
42831
+ editionSlug: import_zod42.z.string().trim().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).max(80).optional().describe("Stable public edition slug. Defaults to site.slug."),
42832
+ idempotencyKey: import_zod42.z.string().trim().min(8).max(200).describe("Unique key for this intended publish. Reuse it only when retrying the same revision."),
42833
+ baseRevision: import_zod42.z.number().int().positive().optional().describe("Required when revising an existing edition; use the current revision from commons_get_publication."),
42834
+ ...CreateEditorialReadingRoomInputSchema
42835
+ };
42836
+ CommonsGetPublicationInputSchema = {
42837
+ subdomain: CommonsPublicationSubdomainSchema.optional().describe("Public publication name to inspect. Omit to return the publication owned by the authenticated account."),
42838
+ includeEditions: import_zod42.z.boolean().default(true).describe("Include the latest revision of every published edition.")
42839
+ };
42797
42840
  RankTrackerModeSchema = import_zod42.z.enum(["maps", "organic", "ai_overview", "paa"]);
42798
42841
  RankTrackerBlueprintInputSchema = {
42799
42842
  projectName: import_zod42.z.string().min(1).optional().describe("Optional name for the rank tracker project, client, or campaign."),
@@ -45614,6 +45657,41 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
45614
45657
  openWorldHint: false
45615
45658
  }
45616
45659
  }, async (input) => executor.commonsListFilters(input));
45660
+ server.registerTool("commons_prepare_publication", {
45661
+ title: "Prepare Transparent Commons Publication",
45662
+ description: "Check and normalize a subscriber-chosen Transparent Commons publication name before any write. Returns name availability, the caller's existing publication, permanent URL shape, ownership rules, and the required validate/claim/publish workflow. This is for a user-owned editorial publication, not a neutral /wiki/ entity.",
45663
+ inputSchema: CommonsPreparePublicationInputSchema,
45664
+ outputSchema: recordOutputSchema("commons_prepare_publication", CommonsGenericOutputSchema),
45665
+ annotations: { title: "Prepare Transparent Commons Publication", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
45666
+ }, async (input) => executor.commonsPreparePublication(input));
45667
+ server.registerTool("commons_validate_publication", {
45668
+ title: "Validate Transparent Commons Publication",
45669
+ description: "Validate a publication name claim or a complete source-grounded editorial edition without writing. Use operation claim before commons_claim_publication and operation publish before commons_publish_editorial. This uses the editorial reading-room contract and checks ownership plus revision conflicts.",
45670
+ inputSchema: CommonsValidatePublicationInputSchema,
45671
+ outputSchema: recordOutputSchema("commons_validate_publication", CommonsGenericOutputSchema),
45672
+ annotations: { title: "Validate Transparent Commons Publication", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
45673
+ }, async (input) => executor.commonsValidatePublication(input));
45674
+ server.registerTool("commons_claim_publication", {
45675
+ title: "Claim Transparent Commons Publication",
45676
+ description: "Permanently claim one globally unique publication subdomain for the authenticated MCP Scraper account. Call commons_prepare_publication and commons_validate_publication first. This creates public identity state but does not publish an edition. Requires an idempotencyKey; retries with the same intended claim are safe.",
45677
+ inputSchema: CommonsClaimPublicationInputSchema,
45678
+ outputSchema: recordOutputSchema("commons_claim_publication", CommonsGenericOutputSchema),
45679
+ annotations: { title: "Claim Transparent Commons Publication", readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true }
45680
+ }, async (input) => executor.commonsClaimPublication(input));
45681
+ server.registerTool("commons_publish_editorial", {
45682
+ title: "Publish Transparent Commons Editorial Edition",
45683
+ description: "Publish a fully authored editorial reading-room edition to the caller-owned Transparent Commons subdomain and return permanent root, archive, and edition URLs. The calling AI must research and author the source-grounded edition first; this tool validates, renders, and persists it. For an existing edition, pass its current baseRevision. Requires an idempotencyKey; this is not the neutral wiki write tool.",
45684
+ inputSchema: CommonsPublishEditorialInputSchema,
45685
+ outputSchema: recordOutputSchema("commons_publish_editorial", CommonsGenericOutputSchema),
45686
+ annotations: { title: "Publish Transparent Commons Editorial Edition", readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true }
45687
+ }, async (input) => executor.commonsPublishEditorial(input));
45688
+ server.registerTool("commons_get_publication", {
45689
+ title: "Get Transparent Commons Publication",
45690
+ description: "Read a Transparent Commons publication and its latest edition revisions. Omit subdomain to inspect the caller-owned publication; pass a name to inspect a public publication. Returns the permanent public and archive URLs needed for sharing or later edits.",
45691
+ inputSchema: CommonsGetPublicationInputSchema,
45692
+ outputSchema: recordOutputSchema("commons_get_publication", CommonsGenericOutputSchema),
45693
+ annotations: { title: "Get Transparent Commons Publication", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
45694
+ }, async (input) => executor.commonsGetPublication(input));
45617
45695
  server.registerTool("directory_workflow", {
45618
45696
  title: "Directory Workflow: Markets + Maps",
45619
45697
  description: `Start a durable directory/prospecting job: selects US city markets from versioned hosted Census-place data, optionally joins the active hosted ZIP dataset, then runs Google Maps business searches per city. Pass a new idempotencyKey for each intended job and reuse it only when retrying that call. Production does not read server-local location CSVs. Always returns a background jobId; poll with directory_workflow_status. ${fileBehavior("Saves a CSV of results per city.", "Completed jobs return an owner-scoped CSV artifact.")}`,
@@ -46482,6 +46560,28 @@ var init_http_mcp_tool_executor = __esm({
46482
46560
  commonsListNeedsLinks(input) {
46483
46561
  return this.call("/commons/needs-links/search", input);
46484
46562
  }
46563
+ commonsPreparePublication(input) {
46564
+ return this.call("/commons/publications/prepare", input);
46565
+ }
46566
+ commonsValidatePublication(input) {
46567
+ return this.call("/commons/publications/validate", input);
46568
+ }
46569
+ commonsClaimPublication(input) {
46570
+ const { idempotencyKey: idempotencyKey3, ...body } = input;
46571
+ return this.call("/commons/publications/claim", { ...body, idempotencyKey: idempotencyKey3 }, this.timeoutMs, "POST", {
46572
+ "Idempotency-Key": `commons-publication-claim-${(0, import_node_crypto26.createHash)("sha256").update(idempotencyKey3).digest("hex")}`
46573
+ });
46574
+ }
46575
+ commonsPublishEditorial(input) {
46576
+ const { idempotencyKey: idempotencyKey3, ...body } = input;
46577
+ return this.call("/commons/publications/publish", { ...body, idempotencyKey: idempotencyKey3 }, this.timeoutMs, "POST", {
46578
+ "Idempotency-Key": `commons-publication-publish-${(0, import_node_crypto26.createHash)("sha256").update(idempotencyKey3).digest("hex")}`
46579
+ });
46580
+ }
46581
+ commonsGetPublication(input) {
46582
+ const query = new URLSearchParams({ includeEditions: String(input.includeEditions ?? true) });
46583
+ return this.getJson(input.subdomain ? `/commons/publications/${encodeURIComponent(input.subdomain)}?${query}` : `/commons/publications/me?${query}`);
46584
+ }
46485
46585
  async captureSerpSnapshot(input) {
46486
46586
  const fingerprint = (0, import_node_crypto26.createHash)("sha256").update(JSON.stringify(input)).digest("hex");
46487
46587
  const now = Date.now();
@@ -58451,6 +58551,54 @@ async function ensureCommonsSchema() {
58451
58551
  },
58452
58552
  { sql: `CREATE INDEX IF NOT EXISTS commons_index_documents_status_updated ON commons_index_documents(embedding_status, updated_at DESC)`, args: [] },
58453
58553
  { sql: `CREATE INDEX IF NOT EXISTS commons_index_documents_entity_type ON commons_index_documents(entity_id, document_type)`, args: [] },
58554
+ {
58555
+ sql: `
58556
+ CREATE TABLE IF NOT EXISTS commons_publications (
58557
+ id TEXT PRIMARY KEY,
58558
+ owner_user_id INTEGER NOT NULL REFERENCES users(id),
58559
+ subdomain TEXT NOT NULL UNIQUE,
58560
+ title TEXT NOT NULL,
58561
+ description TEXT NOT NULL DEFAULT '',
58562
+ claim_idempotency_key TEXT NOT NULL,
58563
+ latest_edition_id TEXT,
58564
+ created_at TEXT NOT NULL,
58565
+ updated_at TEXT NOT NULL,
58566
+ UNIQUE(owner_user_id),
58567
+ UNIQUE(owner_user_id, claim_idempotency_key)
58568
+ )
58569
+ `,
58570
+ args: []
58571
+ },
58572
+ { sql: `CREATE INDEX IF NOT EXISTS commons_publications_updated ON commons_publications(updated_at DESC)`, args: [] },
58573
+ {
58574
+ sql: `
58575
+ CREATE TABLE IF NOT EXISTS commons_publication_editions (
58576
+ id TEXT PRIMARY KEY,
58577
+ publication_id TEXT NOT NULL REFERENCES commons_publications(id),
58578
+ owner_user_id INTEGER NOT NULL REFERENCES users(id),
58579
+ edition_slug TEXT NOT NULL,
58580
+ revision INTEGER NOT NULL,
58581
+ title TEXT NOT NULL,
58582
+ site_json TEXT NOT NULL,
58583
+ deck TEXT NOT NULL,
58584
+ articles_json TEXT NOT NULL,
58585
+ html TEXT NOT NULL,
58586
+ filename TEXT NOT NULL,
58587
+ sha256 TEXT NOT NULL,
58588
+ article_count INTEGER NOT NULL,
58589
+ word_count INTEGER NOT NULL,
58590
+ bytes INTEGER NOT NULL,
58591
+ warnings_json TEXT NOT NULL DEFAULT '[]',
58592
+ idempotency_key TEXT NOT NULL,
58593
+ created_at TEXT NOT NULL,
58594
+ published_at TEXT NOT NULL,
58595
+ UNIQUE(publication_id, edition_slug, revision),
58596
+ UNIQUE(owner_user_id, idempotency_key)
58597
+ )
58598
+ `,
58599
+ args: []
58600
+ },
58601
+ { sql: `CREATE INDEX IF NOT EXISTS commons_publication_editions_publication_published ON commons_publication_editions(publication_id, published_at DESC)`, args: [] },
58454
58602
  {
58455
58603
  sql: "INSERT OR IGNORE INTO schema_migrations (version) VALUES (?)",
58456
58604
  args: [COMMONS_SCHEMA_VERSION]
@@ -60024,7 +60172,7 @@ var init_commons_repository = __esm({
60024
60172
  import_node_crypto34 = require("crypto");
60025
60173
  init_db();
60026
60174
  init_rates();
60027
- COMMONS_SCHEMA_VERSION = "2026-08-04.2";
60175
+ COMMONS_SCHEMA_VERSION = "2026-08-04.3";
60028
60176
  DEFAULT_COMMONS_BASE_URL = "https://transparent-commons.cc";
60029
60177
  DEFAULT_ENTITY_TYPE = "PublicArticle";
60030
60178
  COMMONS_ENTITY_PROFILES = {
@@ -60186,6 +60334,401 @@ var init_commons_repository = __esm({
60186
60334
  }
60187
60335
  });
60188
60336
 
60337
+ // src/api/commons-publication-repository.ts
60338
+ async function prepareCommonsPublication(input, user) {
60339
+ await ensureCommonsSchema();
60340
+ const subdomain = normalizePublicationSubdomain(input.requestedSubdomain);
60341
+ const [owned, claimed] = await Promise.all([
60342
+ getCommonsPublicationForOwner(Number(user.id)),
60343
+ getCommonsPublicationBySubdomain(subdomain)
60344
+ ]);
60345
+ const availability = claimed ? claimed.ownerUserId === Number(user.id) ? "owned_by_caller" : "unavailable" : "available";
60346
+ return {
60347
+ requestedSubdomain: input.requestedSubdomain,
60348
+ normalizedSubdomain: subdomain,
60349
+ availability,
60350
+ requestedPublicUrl: publicationPublicUrl(subdomain),
60351
+ currentPublication: owned,
60352
+ contract: {
60353
+ ownership: "One publication per authenticated MCP Scraper account.",
60354
+ permanence: "Claimed names are stable and globally unique.",
60355
+ renderer: "Published editions use the MCP Scraper editorial reading-room renderer.",
60356
+ revisionRule: "Editing an existing edition requires its current baseRevision.",
60357
+ publicRoutes: ["/", "/archive", "/editions/{editionSlug}"],
60358
+ workflow: [
60359
+ "commons_prepare_publication",
60360
+ "commons_validate_publication",
60361
+ "commons_claim_publication",
60362
+ "commons_publish_editorial"
60363
+ ]
60364
+ },
60365
+ proposed: {
60366
+ title: cleanText(input.title, 140) || publicationTitleFromSubdomain(subdomain),
60367
+ description: cleanText(input.description, 500)
60368
+ }
60369
+ };
60370
+ }
60371
+ async function validateCommonsPublication(input, user) {
60372
+ await ensureCommonsSchema();
60373
+ const errors = [];
60374
+ const warnings = [];
60375
+ let subdomain = "";
60376
+ try {
60377
+ subdomain = normalizePublicationSubdomain(input.requestedSubdomain || input.publicationSubdomain || "");
60378
+ } catch (error) {
60379
+ errors.push(error instanceof Error ? error.message : String(error));
60380
+ }
60381
+ const publication = subdomain ? await getCommonsPublicationBySubdomain(subdomain) : null;
60382
+ if (input.operation === "claim") {
60383
+ const owned = await getCommonsPublicationForOwner(Number(user.id));
60384
+ if (publication && publication.ownerUserId !== Number(user.id)) errors.push("That publication name is already claimed by another account.");
60385
+ if (owned && owned.subdomain !== subdomain) errors.push(`This account already owns ${owned.publicUrl}.`);
60386
+ if (!cleanText(input.title, 140)) warnings.push("No display title was supplied; the publication name will be title-cased.");
60387
+ } else {
60388
+ if (!publication) errors.push("Claim this publication name before publishing an edition.");
60389
+ else if (publication.ownerUserId !== Number(user.id)) errors.push("Only the account that claimed this publication can publish to it.");
60390
+ if (!input.edition) errors.push("A complete editorial reading-room edition is required for publish validation.");
60391
+ if (input.edition) {
60392
+ try {
60393
+ renderEditorialReadingRoom(input.edition);
60394
+ } catch (error) {
60395
+ errors.push(error instanceof Error ? error.message : String(error));
60396
+ }
60397
+ const slug2 = normalizeEditionSlug(input.editionSlug || input.edition.site.slug);
60398
+ const latest = publication ? await getLatestEdition(publication.id, slug2) : null;
60399
+ if (latest && input.baseRevision === void 0) errors.push(`Edition ${slug2} already exists at revision ${latest.revision}; supply baseRevision to edit it.`);
60400
+ if (latest && input.baseRevision !== void 0 && latest.revision !== input.baseRevision) {
60401
+ errors.push(`Edition ${slug2} is revision ${latest.revision}; the proposed edit targeted ${input.baseRevision}.`);
60402
+ }
60403
+ }
60404
+ }
60405
+ return {
60406
+ valid: errors.length === 0,
60407
+ operation: input.operation,
60408
+ normalizedSubdomain: subdomain || null,
60409
+ publicUrl: subdomain ? publicationPublicUrl(subdomain) : null,
60410
+ errors,
60411
+ warnings,
60412
+ publication
60413
+ };
60414
+ }
60415
+ async function claimCommonsPublication(input, user) {
60416
+ await ensureCommonsSchema();
60417
+ const subdomain = normalizePublicationSubdomain(input.requestedSubdomain);
60418
+ const userId = Number(user.id);
60419
+ const existingOwned = await getCommonsPublicationForOwner(userId);
60420
+ if (existingOwned) {
60421
+ if (existingOwned.subdomain === subdomain) return { publication: existingOwned, idempotentReplay: true };
60422
+ throw new CommonsPublicationError("publication_already_claimed", `This account already owns ${existingOwned.publicUrl}.`, 409);
60423
+ }
60424
+ const existingName = await getCommonsPublicationBySubdomain(subdomain);
60425
+ if (existingName) throw new CommonsPublicationError("publication_name_unavailable", "That publication name is already claimed.", 409);
60426
+ const id = `tcpub_${(0, import_node_crypto35.randomUUID)()}`;
60427
+ const now = (/* @__PURE__ */ new Date()).toISOString();
60428
+ try {
60429
+ await getDb().execute({
60430
+ sql: `
60431
+ INSERT INTO commons_publications (
60432
+ id, owner_user_id, subdomain, title, description, claim_idempotency_key, created_at, updated_at
60433
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
60434
+ `,
60435
+ args: [
60436
+ id,
60437
+ userId,
60438
+ subdomain,
60439
+ cleanText(input.title, 140) || publicationTitleFromSubdomain(subdomain),
60440
+ cleanText(input.description, 500),
60441
+ requiredIdempotencyKey(input.idempotencyKey),
60442
+ now,
60443
+ now
60444
+ ]
60445
+ });
60446
+ } catch (error) {
60447
+ const replay = await getCommonsPublicationForOwner(userId);
60448
+ if (replay?.subdomain === subdomain) return { publication: replay, idempotentReplay: true };
60449
+ throw error;
60450
+ }
60451
+ const publication = await getCommonsPublicationBySubdomain(subdomain);
60452
+ if (!publication) throw new CommonsPublicationError("publication_claim_failed", "The publication claim was not persisted.");
60453
+ return { publication, idempotentReplay: false };
60454
+ }
60455
+ async function publishCommonsEditorial(input, user) {
60456
+ await ensureCommonsSchema();
60457
+ const subdomain = normalizePublicationSubdomain(input.publicationSubdomain);
60458
+ const publication = await getCommonsPublicationBySubdomain(subdomain);
60459
+ if (!publication) throw new CommonsPublicationError("publication_not_found", "Claim this publication name before publishing an edition.", 404);
60460
+ if (publication.ownerUserId !== Number(user.id)) {
60461
+ throw new CommonsPublicationError("publication_not_owned", "Only the account that claimed this publication can publish to it.", 404);
60462
+ }
60463
+ const idempotencyKey3 = requiredIdempotencyKey(input.idempotencyKey);
60464
+ const replay = await getEditionByIdempotency(Number(user.id), idempotencyKey3);
60465
+ if (replay) return publicationResult(publication, replay, true);
60466
+ const editionSlug = normalizeEditionSlug(input.editionSlug || input.site.slug);
60467
+ const latest = await getLatestEdition(publication.id, editionSlug);
60468
+ if (latest && input.baseRevision === void 0) {
60469
+ throw new CommonsPublicationError("publication_base_revision_required", `Edition ${editionSlug} already exists at revision ${latest.revision}; supply baseRevision to edit it.`, 409);
60470
+ }
60471
+ if (latest && input.baseRevision !== latest.revision) {
60472
+ throw new CommonsPublicationError("publication_revision_conflict", `Edition ${editionSlug} is revision ${latest.revision}; refresh it before publishing an edit.`, 409);
60473
+ }
60474
+ const { publicationSubdomain: _publicationSubdomain, editionSlug: _editionSlug, idempotencyKey: _idempotencyKey, baseRevision: _baseRevision, ...editionInput } = input;
60475
+ const canonicalUrl = editionPublicUrl(subdomain, editionSlug);
60476
+ const rendered = renderEditorialReadingRoom(editionInput);
60477
+ const html = addPublicMetadata(rendered.html, canonicalUrl, publication.title);
60478
+ const editionId = `tced_${(0, import_node_crypto35.randomUUID)()}`;
60479
+ const revision = (latest?.revision ?? 0) + 1;
60480
+ const now = (/* @__PURE__ */ new Date()).toISOString();
60481
+ await getDb().batch([
60482
+ {
60483
+ sql: `
60484
+ INSERT INTO commons_publication_editions (
60485
+ id, publication_id, owner_user_id, edition_slug, revision, title, site_json, deck,
60486
+ articles_json, html, filename, sha256, article_count, word_count, bytes, warnings_json,
60487
+ idempotency_key, created_at, published_at
60488
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
60489
+ `,
60490
+ args: [
60491
+ editionId,
60492
+ publication.id,
60493
+ Number(user.id),
60494
+ editionSlug,
60495
+ revision,
60496
+ editionInput.site.title,
60497
+ JSON.stringify(editionInput.site),
60498
+ editionInput.deck,
60499
+ JSON.stringify(editionInput.articles),
60500
+ html,
60501
+ rendered.filename,
60502
+ (0, import_node_crypto35.createHash)("sha256").update(html).digest("hex"),
60503
+ rendered.articleCount,
60504
+ rendered.wordCount,
60505
+ Buffer.byteLength(html),
60506
+ JSON.stringify(rendered.warnings),
60507
+ idempotencyKey3,
60508
+ now,
60509
+ now
60510
+ ]
60511
+ },
60512
+ {
60513
+ sql: "UPDATE commons_publications SET latest_edition_id = ?, updated_at = ? WHERE id = ? AND owner_user_id = ?",
60514
+ args: [editionId, now, publication.id, Number(user.id)]
60515
+ }
60516
+ ], "write");
60517
+ const edition = await getPublicationEditionById(editionId);
60518
+ if (!edition) throw new CommonsPublicationError("publication_publish_failed", "The published edition was not persisted.");
60519
+ return publicationResult({ ...publication, latestEditionId: editionId, updatedAt: now }, edition, false);
60520
+ }
60521
+ async function getCommonsPublicationBySubdomain(subdomainInput) {
60522
+ await ensureCommonsSchema();
60523
+ const subdomain = normalizePublicationSubdomain(subdomainInput);
60524
+ const result = await getDb().execute({ sql: "SELECT * FROM commons_publications WHERE subdomain = ? LIMIT 1", args: [subdomain] });
60525
+ return result.rows[0] ? rowToPublication(result.rows[0]) : null;
60526
+ }
60527
+ async function getCommonsPublicationForOwner(userId) {
60528
+ await ensureCommonsSchema();
60529
+ const result = await getDb().execute({ sql: "SELECT * FROM commons_publications WHERE owner_user_id = ? LIMIT 1", args: [userId] });
60530
+ return result.rows[0] ? rowToPublication(result.rows[0]) : null;
60531
+ }
60532
+ async function listCommonsPublicationEditions(publicationId) {
60533
+ await ensureCommonsSchema();
60534
+ const result = await getDb().execute({
60535
+ sql: `
60536
+ SELECT edition.* FROM commons_publication_editions edition
60537
+ JOIN (
60538
+ SELECT edition_slug, MAX(revision) AS revision
60539
+ FROM commons_publication_editions WHERE publication_id = ? GROUP BY edition_slug
60540
+ ) latest ON latest.edition_slug = edition.edition_slug AND latest.revision = edition.revision
60541
+ WHERE edition.publication_id = ? ORDER BY edition.published_at DESC
60542
+ `,
60543
+ args: [publicationId, publicationId]
60544
+ });
60545
+ const publication = await getPublicationById(publicationId);
60546
+ if (!publication) return [];
60547
+ return result.rows.map((row) => rowToEdition(row, publication.subdomain));
60548
+ }
60549
+ async function getCommonsPublicationEditionHtml(subdomainInput, editionSlugInput) {
60550
+ const publication = await getCommonsPublicationBySubdomain(subdomainInput);
60551
+ if (!publication) return null;
60552
+ const result = editionSlugInput ? await getDb().execute({
60553
+ sql: "SELECT * FROM commons_publication_editions WHERE publication_id = ? AND edition_slug = ? ORDER BY revision DESC LIMIT 1",
60554
+ args: [publication.id, normalizeEditionSlug(editionSlugInput)]
60555
+ }) : await getDb().execute({
60556
+ sql: "SELECT * FROM commons_publication_editions WHERE id = ? AND publication_id = ? LIMIT 1",
60557
+ args: [publication.latestEditionId || "", publication.id]
60558
+ });
60559
+ if (!result.rows[0]) return null;
60560
+ const record = result.rows[0];
60561
+ return { publication, edition: rowToEdition(record, publication.subdomain), html: String(record.html || "") };
60562
+ }
60563
+ async function getPublicationById(id) {
60564
+ const result = await getDb().execute({ sql: "SELECT * FROM commons_publications WHERE id = ? LIMIT 1", args: [id] });
60565
+ return result.rows[0] ? rowToPublication(result.rows[0]) : null;
60566
+ }
60567
+ async function getLatestEdition(publicationId, editionSlug) {
60568
+ const publication = await getPublicationById(publicationId);
60569
+ if (!publication) return null;
60570
+ const result = await getDb().execute({
60571
+ sql: "SELECT * FROM commons_publication_editions WHERE publication_id = ? AND edition_slug = ? ORDER BY revision DESC LIMIT 1",
60572
+ args: [publicationId, editionSlug]
60573
+ });
60574
+ return result.rows[0] ? rowToEdition(result.rows[0], publication.subdomain) : null;
60575
+ }
60576
+ async function getEditionByIdempotency(userId, idempotencyKey3) {
60577
+ const result = await getDb().execute({
60578
+ sql: `SELECT edition.*, publication.subdomain FROM commons_publication_editions edition JOIN commons_publications publication ON publication.id = edition.publication_id WHERE edition.owner_user_id = ? AND edition.idempotency_key = ? LIMIT 1`,
60579
+ args: [userId, idempotencyKey3]
60580
+ });
60581
+ if (!result.rows[0]) return null;
60582
+ const record = result.rows[0];
60583
+ return rowToEdition(record, String(record.subdomain));
60584
+ }
60585
+ async function getPublicationEditionById(id) {
60586
+ const result = await getDb().execute({
60587
+ sql: `SELECT edition.*, publication.subdomain FROM commons_publication_editions edition JOIN commons_publications publication ON publication.id = edition.publication_id WHERE edition.id = ? LIMIT 1`,
60588
+ args: [id]
60589
+ });
60590
+ if (!result.rows[0]) return null;
60591
+ const record = result.rows[0];
60592
+ return rowToEdition(record, String(record.subdomain));
60593
+ }
60594
+ function rowToPublication(row) {
60595
+ const subdomain = String(row.subdomain);
60596
+ return {
60597
+ id: String(row.id),
60598
+ ownerUserId: Number(row.owner_user_id),
60599
+ subdomain,
60600
+ title: String(row.title),
60601
+ description: String(row.description || ""),
60602
+ latestEditionId: row.latest_edition_id ? String(row.latest_edition_id) : null,
60603
+ createdAt: String(row.created_at),
60604
+ updatedAt: String(row.updated_at),
60605
+ publicUrl: publicationPublicUrl(subdomain),
60606
+ archiveUrl: `${publicationPublicUrl(subdomain)}/archive`
60607
+ };
60608
+ }
60609
+ function rowToEdition(row, subdomain) {
60610
+ const editionSlug = String(row.edition_slug);
60611
+ return {
60612
+ id: String(row.id),
60613
+ publicationId: String(row.publication_id),
60614
+ editionSlug,
60615
+ revision: Number(row.revision),
60616
+ title: String(row.title),
60617
+ site: parseJson4(row.site_json, {}),
60618
+ deck: String(row.deck),
60619
+ filename: String(row.filename),
60620
+ sha256: String(row.sha256),
60621
+ articleCount: Number(row.article_count),
60622
+ wordCount: Number(row.word_count),
60623
+ bytes: Number(row.bytes),
60624
+ warnings: parseJson4(row.warnings_json, []),
60625
+ createdAt: String(row.created_at),
60626
+ publishedAt: String(row.published_at),
60627
+ publicUrl: editionPublicUrl(subdomain, editionSlug)
60628
+ };
60629
+ }
60630
+ function publicationResult(publication, edition, idempotentReplay) {
60631
+ return {
60632
+ publication,
60633
+ edition,
60634
+ publicUrl: publication.publicUrl,
60635
+ archiveUrl: publication.archiveUrl,
60636
+ editionUrl: edition.publicUrl,
60637
+ idempotentReplay
60638
+ };
60639
+ }
60640
+ function normalizePublicationSubdomain(value) {
60641
+ const normalized = String(value || "").trim().toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
60642
+ if (!/^[a-z0-9](?:[a-z0-9-]{1,48}[a-z0-9])$/.test(normalized)) {
60643
+ throw new CommonsPublicationError("publication_name_invalid", "Choose a publication name between 3 and 50 characters using letters, numbers, and interior hyphens.");
60644
+ }
60645
+ if (RESERVED_SUBDOMAINS.has(normalized)) throw new CommonsPublicationError("publication_name_reserved", "That publication name is reserved; choose a more specific name.", 409);
60646
+ return normalized;
60647
+ }
60648
+ function normalizeEditionSlug(value) {
60649
+ const normalized = String(value || "").trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 80);
60650
+ if (!normalized) throw new CommonsPublicationError("edition_slug_invalid", "A published edition needs a stable slug.");
60651
+ return normalized;
60652
+ }
60653
+ function requiredIdempotencyKey(value) {
60654
+ const normalized = cleanText(value, 200);
60655
+ if (normalized.length < 8) throw new CommonsPublicationError("idempotency_key_invalid", "Use an idempotency key of at least 8 characters and reuse it only when retrying the same write.");
60656
+ return normalized;
60657
+ }
60658
+ function publicationPublicUrl(subdomain) {
60659
+ return `https://${subdomain}.${PUBLICATION_ROOT_DOMAIN}`;
60660
+ }
60661
+ function editionPublicUrl(subdomain, editionSlug) {
60662
+ return `${publicationPublicUrl(subdomain)}/editions/${encodeURIComponent(editionSlug)}`;
60663
+ }
60664
+ function publicationTitleFromSubdomain(subdomain) {
60665
+ return subdomain.split("-").map((part) => part ? `${part[0]?.toUpperCase()}${part.slice(1)}` : "").join(" ");
60666
+ }
60667
+ function cleanText(value, max) {
60668
+ return String(value || "").replace(/\s+/g, " ").trim().slice(0, max);
60669
+ }
60670
+ function parseJson4(value, fallback) {
60671
+ try {
60672
+ return JSON.parse(String(value || ""));
60673
+ } catch {
60674
+ return fallback;
60675
+ }
60676
+ }
60677
+ function addPublicMetadata(html, canonicalUrl, publicationTitle) {
60678
+ const escapedUrl = canonicalUrl.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
60679
+ const escapedTitle = publicationTitle.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
60680
+ return html.replace("</head>", [
60681
+ ` <link rel="canonical" href="${escapedUrl}">`,
60682
+ ` <meta property="og:url" content="${escapedUrl}">`,
60683
+ ` <meta property="og:site_name" content="${escapedTitle}">`,
60684
+ ' <meta name="robots" content="index,follow">',
60685
+ "</head>"
60686
+ ].join("\n"));
60687
+ }
60688
+ var import_node_crypto35, PUBLICATION_ROOT_DOMAIN, RESERVED_SUBDOMAINS, CommonsPublicationError;
60689
+ var init_commons_publication_repository = __esm({
60690
+ "src/api/commons-publication-repository.ts"() {
60691
+ "use strict";
60692
+ import_node_crypto35 = require("crypto");
60693
+ init_db();
60694
+ init_commons_repository();
60695
+ init_render();
60696
+ PUBLICATION_ROOT_DOMAIN = process.env.COMMONS_PUBLICATION_ROOT_DOMAIN || "transparent-commons.cc";
60697
+ RESERVED_SUBDOMAINS = /* @__PURE__ */ new Set([
60698
+ "admin",
60699
+ "api",
60700
+ "app",
60701
+ "assets",
60702
+ "auth",
60703
+ "blog",
60704
+ "cdn",
60705
+ "docs",
60706
+ "help",
60707
+ "login",
60708
+ "mail",
60709
+ "mcp",
60710
+ "signup",
60711
+ "sitemap",
60712
+ "static",
60713
+ "support",
60714
+ "transparent-commons",
60715
+ "transparentcommons",
60716
+ "www",
60717
+ "wiki"
60718
+ ]);
60719
+ CommonsPublicationError = class extends Error {
60720
+ constructor(code, message, httpStatus = 400) {
60721
+ super(message);
60722
+ this.code = code;
60723
+ this.httpStatus = httpStatus;
60724
+ this.name = "CommonsPublicationError";
60725
+ }
60726
+ code;
60727
+ httpStatus;
60728
+ };
60729
+ }
60730
+ });
60731
+
60189
60732
  // src/api/commons-routes.ts
60190
60733
  function filtersFromQuery(query) {
60191
60734
  return {
@@ -60231,10 +60774,23 @@ function repositoryStatus(error) {
60231
60774
  if (error.httpStatus === 409) return 409;
60232
60775
  return 400;
60233
60776
  }
60777
+ async function publicationOperation(c, operation, successStatus = 200) {
60778
+ try {
60779
+ return c.json({ ok: true, data: await operation() }, successStatus);
60780
+ } catch (error) {
60781
+ return publicationError(c, error);
60782
+ }
60783
+ }
60784
+ function publicationError(c, error) {
60785
+ if (error instanceof CommonsPublicationError) {
60786
+ return c.json({ ok: false, error: error.code, message: error.message }, error.httpStatus);
60787
+ }
60788
+ throw error;
60789
+ }
60234
60790
  function xmlEscape(value) {
60235
60791
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
60236
60792
  }
60237
- var import_hono31, import_zod50, commonsApp, auth2, SearchBodySchema, FeaturedImageSchema, MediaSchema, CitationSchema, RelatedLinkSchema, SubmitEntitySchema, PrepareEntitySchema, ValidateEntitySchema, SaveFilterSchema, NeedsLinkBodySchema;
60793
+ var import_hono31, import_zod50, commonsApp, auth2, SearchBodySchema, FeaturedImageSchema, MediaSchema, CitationSchema, RelatedLinkSchema, SubmitEntitySchema, PrepareEntitySchema, ValidateEntitySchema, SaveFilterSchema, NeedsLinkBodySchema, PreparePublicationSchema, ValidatePublicationSchema, ClaimPublicationSchema, PublishEditorialSchema;
60238
60794
  var init_commons_routes = __esm({
60239
60795
  "src/api/commons-routes.ts"() {
60240
60796
  "use strict";
@@ -60243,6 +60799,8 @@ var init_commons_routes = __esm({
60243
60799
  init_api_auth();
60244
60800
  init_commons_repository();
60245
60801
  init_listing_write_billing();
60802
+ init_commons_publication_repository();
60803
+ init_mcp_tool_schemas();
60246
60804
  commonsApp = new import_hono31.Hono();
60247
60805
  auth2 = createApiKeyAuth();
60248
60806
  SearchBodySchema = import_zod50.z.object({
@@ -60403,10 +60961,83 @@ var init_commons_routes = __esm({
60403
60961
  limit: import_zod50.z.number().int().min(1).max(100).optional(),
60404
60962
  offset: import_zod50.z.number().int().min(0).max(1e4).optional()
60405
60963
  }).strict();
60964
+ PreparePublicationSchema = import_zod50.z.object(CommonsPreparePublicationInputSchema).strict();
60965
+ ValidatePublicationSchema = import_zod50.z.object(CommonsValidatePublicationInputSchema).strict();
60966
+ ClaimPublicationSchema = import_zod50.z.object(CommonsClaimPublicationInputSchema).strict();
60967
+ PublishEditorialSchema = import_zod50.z.object(CommonsPublishEditorialInputSchema).strict();
60406
60968
  commonsApp.get("/health", async (c) => {
60407
60969
  await ensureCommonsSchema();
60408
60970
  return c.json({ ok: true, data: commonsDatabaseReport() });
60409
60971
  });
60972
+ commonsApp.post("/publications/prepare", auth2, async (c) => {
60973
+ const parsed = PreparePublicationSchema.safeParse(await c.req.json().catch(() => ({})));
60974
+ if (!parsed.success) return validationError(c, parsed.error);
60975
+ return publicationOperation(c, () => prepareCommonsPublication(parsed.data, c.get("user")));
60976
+ });
60977
+ commonsApp.post("/publications/validate", auth2, async (c) => {
60978
+ const parsed = ValidatePublicationSchema.safeParse(await c.req.json().catch(() => ({})));
60979
+ if (!parsed.success) return validationError(c, parsed.error);
60980
+ return publicationOperation(c, () => validateCommonsPublication(parsed.data, c.get("user")));
60981
+ });
60982
+ commonsApp.post("/publications/claim", auth2, async (c) => {
60983
+ const parsed = ClaimPublicationSchema.safeParse(await c.req.json().catch(() => ({})));
60984
+ if (!parsed.success) return validationError(c, parsed.error);
60985
+ return publicationOperation(c, () => claimCommonsPublication(parsed.data, c.get("user")), 201);
60986
+ });
60987
+ commonsApp.post("/publications/publish", auth2, async (c) => {
60988
+ const parsed = PublishEditorialSchema.safeParse(await c.req.json().catch(() => ({})));
60989
+ if (!parsed.success) return validationError(c, parsed.error);
60990
+ return publicationOperation(c, () => publishCommonsEditorial(parsed.data, c.get("user")), 201);
60991
+ });
60992
+ commonsApp.get("/publications/me", auth2, async (c) => {
60993
+ const publication = await getCommonsPublicationForOwner(Number(c.get("user").id));
60994
+ if (!publication) return c.json({ ok: false, error: "publication_not_found", message: "This account has not claimed a Commons publication yet." }, 404);
60995
+ const editions = c.req.query("includeEditions") === "false" ? [] : await listCommonsPublicationEditions(publication.id);
60996
+ return c.json({ ok: true, data: { publication, editions } });
60997
+ });
60998
+ commonsApp.get("/publications/:subdomain/site", async (c) => {
60999
+ try {
61000
+ const result = await getCommonsPublicationEditionHtml(c.req.param("subdomain"));
61001
+ if (!result) return c.json({ ok: false, error: "publication_not_published", message: "No published edition exists for this publication." }, 404);
61002
+ return c.html(result.html, 200, {
61003
+ "cache-control": "public, max-age=60, s-maxage=300",
61004
+ "x-robots-tag": "index, follow"
61005
+ });
61006
+ } catch (error) {
61007
+ return publicationError(c, error);
61008
+ }
61009
+ });
61010
+ commonsApp.get("/publications/:subdomain/editions/:editionSlug/site", async (c) => {
61011
+ try {
61012
+ const result = await getCommonsPublicationEditionHtml(c.req.param("subdomain"), c.req.param("editionSlug"));
61013
+ if (!result) return c.json({ ok: false, error: "edition_not_found", message: "No published edition matched that publication and slug." }, 404);
61014
+ return c.html(result.html, 200, {
61015
+ "cache-control": "public, max-age=60, s-maxage=300",
61016
+ "x-robots-tag": "index, follow"
61017
+ });
61018
+ } catch (error) {
61019
+ return publicationError(c, error);
61020
+ }
61021
+ });
61022
+ commonsApp.get("/publications/:subdomain/editions", async (c) => {
61023
+ try {
61024
+ const publication = await getCommonsPublicationBySubdomain(c.req.param("subdomain"));
61025
+ if (!publication) return c.json({ ok: false, error: "publication_not_found", message: "No Commons publication matched that name." }, 404);
61026
+ return c.json({ ok: true, data: { publication, editions: await listCommonsPublicationEditions(publication.id) } });
61027
+ } catch (error) {
61028
+ return publicationError(c, error);
61029
+ }
61030
+ });
61031
+ commonsApp.get("/publications/:subdomain", async (c) => {
61032
+ try {
61033
+ const publication = await getCommonsPublicationBySubdomain(c.req.param("subdomain"));
61034
+ if (!publication) return c.json({ ok: false, error: "publication_not_found", message: "No Commons publication matched that name." }, 404);
61035
+ const editions = c.req.query("includeEditions") === "false" ? [] : await listCommonsPublicationEditions(publication.id);
61036
+ return c.json({ ok: true, data: { publication, editions } });
61037
+ } catch (error) {
61038
+ return publicationError(c, error);
61039
+ }
61040
+ });
60410
61041
  commonsApp.get("/entities", async (c) => {
60411
61042
  const result = await searchCommonsEntities(filtersFromQuery(c.req.query()));
60412
61043
  return c.json({ ok: true, data: result });
@@ -60529,13 +61160,13 @@ ${urls.join("\n")}
60529
61160
 
60530
61161
  // src/api/scheduled-artifact-owner.ts
60531
61162
  function scheduledArtifactOwnerIdForApiKey(apiKey) {
60532
- return (0, import_node_crypto35.createHash)("sha256").update(apiKey).digest("hex").slice(0, 24);
61163
+ return (0, import_node_crypto36.createHash)("sha256").update(apiKey).digest("hex").slice(0, 24);
60533
61164
  }
60534
- var import_node_crypto35;
61165
+ var import_node_crypto36;
60535
61166
  var init_scheduled_artifact_owner = __esm({
60536
61167
  "src/api/scheduled-artifact-owner.ts"() {
60537
61168
  "use strict";
60538
- import_node_crypto35 = require("crypto");
61169
+ import_node_crypto36 = require("crypto");
60539
61170
  }
60540
61171
  });
60541
61172
 
@@ -60569,7 +61200,7 @@ async function ensureScheduledRunViewLinksSchema() {
60569
61200
  schemaReady3 = true;
60570
61201
  }
60571
61202
  function tokenHash2(token4) {
60572
- return (0, import_node_crypto36.createHash)("sha256").update(token4).digest("hex");
61203
+ return (0, import_node_crypto37.createHash)("sha256").update(token4).digest("hex");
60573
61204
  }
60574
61205
  function mapRow(row) {
60575
61206
  return {
@@ -60589,9 +61220,9 @@ function mapRow(row) {
60589
61220
  async function createScheduledRunViewLink(input) {
60590
61221
  await ensureScheduledRunViewLinksSchema();
60591
61222
  const now = input.now ?? /* @__PURE__ */ new Date();
60592
- const token4 = (0, import_node_crypto36.randomBytes)(32).toString("base64url");
61223
+ const token4 = (0, import_node_crypto37.randomBytes)(32).toString("base64url");
60593
61224
  const record = {
60594
- shareId: (0, import_node_crypto36.randomUUID)(),
61225
+ shareId: (0, import_node_crypto37.randomUUID)(),
60595
61226
  ownerId: input.ownerId,
60596
61227
  runId: input.runId,
60597
61228
  artifactId: input.artifactId,
@@ -60654,11 +61285,11 @@ async function revokeScheduledRunViewLink(ownerId, runId, shareId, now = /* @__P
60654
61285
  });
60655
61286
  return result.rowsAffected > 0;
60656
61287
  }
60657
- var import_node_crypto36, schemaReady3;
61288
+ var import_node_crypto37, schemaReady3;
60658
61289
  var init_scheduled_run_view_links = __esm({
60659
61290
  "src/api/scheduled-run-view-links.ts"() {
60660
61291
  "use strict";
60661
- import_node_crypto36 = require("crypto");
61292
+ import_node_crypto37 = require("crypto");
60662
61293
  init_db();
60663
61294
  schemaReady3 = false;
60664
61295
  }
@@ -60859,7 +61490,7 @@ function policy4() {
60859
61490
  };
60860
61491
  }
60861
61492
  function runStorageSegment(runId) {
60862
- return /^[a-zA-Z0-9_-]{1,160}$/.test(runId) ? runId : `run-${(0, import_node_crypto37.createHash)("sha256").update(runId).digest("hex").slice(0, 32)}`;
61493
+ return /^[a-zA-Z0-9_-]{1,160}$/.test(runId) ? runId : `run-${(0, import_node_crypto38.createHash)("sha256").update(runId).digest("hex").slice(0, 32)}`;
60863
61494
  }
60864
61495
  async function createScheduledRunArtifact(args) {
60865
61496
  if (args.rendered.bytes > SCHEDULED_RUN_ARTIFACT_MAX_BYTES) {
@@ -60900,12 +61531,12 @@ async function readScheduledRunArtifact(args) {
60900
61531
  if (!window2 || window2.nextOffset !== null || window2.totalBytes > SCHEDULED_RUN_ARTIFACT_MAX_BYTES) return null;
60901
61532
  return window2.text;
60902
61533
  }
60903
- var import_node_crypto37, SCHEDULED_RUN_ARTIFACT_PREFIX, SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS, SCHEDULED_RUN_ARTIFACT_MAX_BYTES;
61534
+ var import_node_crypto38, SCHEDULED_RUN_ARTIFACT_PREFIX, SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS, SCHEDULED_RUN_ARTIFACT_MAX_BYTES;
60904
61535
  var init_scheduled_run_artifact_store = __esm({
60905
61536
  "src/scheduled-artifacts/scheduled-run-artifact-store.ts"() {
60906
61537
  "use strict";
60907
61538
  init_private_artifacts();
60908
- import_node_crypto37 = require("crypto");
61539
+ import_node_crypto38 = require("crypto");
60909
61540
  SCHEDULED_RUN_ARTIFACT_PREFIX = "scheduled-run-artifacts/";
60910
61541
  SCHEDULED_RUN_ARTIFACT_DOWNLOAD_TTL_MS = 15 * 60 * 1e3;
60911
61542
  SCHEDULED_RUN_ARTIFACT_MAX_BYTES = 2e6;
@@ -61123,7 +61754,7 @@ async function reconcileDiscoveredNangoConnections(identity, discovered) {
61123
61754
  updated_at = excluded.updated_at
61124
61755
  `,
61125
61756
  args: [
61126
- (0, import_node_crypto38.randomUUID)(),
61757
+ (0, import_node_crypto39.randomUUID)(),
61127
61758
  userId,
61128
61759
  connection.providerConfigKey,
61129
61760
  connection.provider,
@@ -61194,7 +61825,7 @@ async function recordServiceConnectionHealth(args) {
61194
61825
  });
61195
61826
  await getDb().execute({
61196
61827
  sql: `INSERT INTO service_connection_health_events (id, connection_id, operational_status, failure_code, retryable, evidence_source) VALUES (?, ?, ?, ?, ?, ?)`,
61197
- args: [(0, import_node_crypto38.randomUUID)(), args.connectionId, args.operationalStatus, args.failureCode ?? null, args.retryable == null ? null : args.retryable ? 1 : 0, args.evidenceSource]
61828
+ args: [(0, import_node_crypto39.randomUUID)(), args.connectionId, args.operationalStatus, args.failureCode ?? null, args.retryable == null ? null : args.retryable ? 1 : 0, args.evidenceSource]
61198
61829
  });
61199
61830
  }
61200
61831
  async function setServiceConnectionActions(identity, connectionId, enabled) {
@@ -61234,7 +61865,7 @@ async function claimServiceConnectionAction(args) {
61234
61865
  if (!connection) throw new Error("service_connection_not_found");
61235
61866
  const inserted = await getDb().execute({
61236
61867
  sql: `INSERT OR IGNORE INTO service_connection_action_audit (id, connection_id, user_id, tool, request_id, status, request_digest) VALUES (?, ?, ?, ?, ?, 'started', ?)`,
61237
- args: [(0, import_node_crypto38.randomUUID)(), connection.id, connection.userId, args.tool, args.requestId, args.requestDigest]
61868
+ args: [(0, import_node_crypto39.randomUUID)(), connection.id, connection.userId, args.tool, args.requestId, args.requestDigest]
61238
61869
  });
61239
61870
  if (Number(inserted.rowsAffected ?? 0) === 1) return { claimed: true };
61240
61871
  const existing = await getDb().execute({
@@ -61259,11 +61890,11 @@ async function claimServiceConnectionAction(args) {
61259
61890
  ...result !== void 0 ? { result } : {}
61260
61891
  };
61261
61892
  }
61262
- var import_node_crypto38, schemaReady4, schemaDb4;
61893
+ var import_node_crypto39, schemaReady4, schemaDb4;
61263
61894
  var init_service_connections = __esm({
61264
61895
  "src/api/service-connections.ts"() {
61265
61896
  "use strict";
61266
- import_node_crypto38 = require("crypto");
61897
+ import_node_crypto39 = require("crypto");
61267
61898
  init_db();
61268
61899
  schemaReady4 = null;
61269
61900
  schemaDb4 = null;
@@ -61277,8 +61908,8 @@ function signingSecret() {
61277
61908
  return secret2;
61278
61909
  }
61279
61910
  function schedulerIntegrationSignature(args) {
61280
- const bodyHash = (0, import_node_crypto39.createHash)("sha256").update(args.body).digest("hex");
61281
- return (0, import_node_crypto39.createHmac)("sha256", args.secret).update(`${args.method.toUpperCase()}
61911
+ const bodyHash = (0, import_node_crypto40.createHash)("sha256").update(args.body).digest("hex");
61912
+ return (0, import_node_crypto40.createHmac)("sha256", args.secret).update(`${args.method.toUpperCase()}
61282
61913
  ${args.path}
61283
61914
  ${args.timestamp}
61284
61915
  ${args.nonce}
@@ -61325,17 +61956,17 @@ async function verifySchedulerIntegrationRequest(request, rawBody) {
61325
61956
  });
61326
61957
  const suppliedBytes = Buffer.from(signature, "hex");
61327
61958
  const expectedBytes = Buffer.from(expected, "hex");
61328
- if (suppliedBytes.length !== expectedBytes.length || !(0, import_node_crypto39.timingSafeEqual)(suppliedBytes, expectedBytes)) {
61959
+ if (suppliedBytes.length !== expectedBytes.length || !(0, import_node_crypto40.timingSafeEqual)(suppliedBytes, expectedBytes)) {
61329
61960
  throw new SchedulerIntegrationAuthError("invalid_signature");
61330
61961
  }
61331
61962
  await claimNonce(nonce, timestampMs);
61332
61963
  return { requestId };
61333
61964
  }
61334
- var import_node_crypto39, MAX_CLOCK_SKEW_MS, SchedulerIntegrationAuthError;
61965
+ var import_node_crypto40, MAX_CLOCK_SKEW_MS, SchedulerIntegrationAuthError;
61335
61966
  var init_scheduler_integration_auth = __esm({
61336
61967
  "src/api/scheduler-integration-auth.ts"() {
61337
61968
  "use strict";
61338
- import_node_crypto39 = require("crypto");
61969
+ import_node_crypto40 = require("crypto");
61339
61970
  init_db();
61340
61971
  init_service_connections();
61341
61972
  MAX_CLOCK_SKEW_MS = 5 * 60 * 1e3;
@@ -61619,7 +62250,7 @@ var init_site_extract_reconciliation = __esm({
61619
62250
 
61620
62251
  // src/api/page-diff.ts
61621
62252
  function sha256Hex(value) {
61622
- return (0, import_node_crypto40.createHash)("sha256").update(value).digest("hex");
62253
+ return (0, import_node_crypto41.createHash)("sha256").update(value).digest("hex");
61623
62254
  }
61624
62255
  function truncateForStorage(value, maxChars = MAX_SNAPSHOT_CONTENT_CHARS) {
61625
62256
  if (value.length <= maxChars) return { value, truncated: false };
@@ -61676,11 +62307,11 @@ function diffPageContent(oldContent, newContent) {
61676
62307
  totalChangedLineCount
61677
62308
  };
61678
62309
  }
61679
- var import_node_crypto40, import_diff, MAX_SNAPSHOT_CONTENT_CHARS, MAX_DIFF_HUNKS, MAX_DIFF_LINES_PER_RESPONSE;
62310
+ var import_node_crypto41, import_diff, MAX_SNAPSHOT_CONTENT_CHARS, MAX_DIFF_HUNKS, MAX_DIFF_LINES_PER_RESPONSE;
61680
62311
  var init_page_diff = __esm({
61681
62312
  "src/api/page-diff.ts"() {
61682
62313
  "use strict";
61683
- import_node_crypto40 = require("crypto");
62314
+ import_node_crypto41 = require("crypto");
61684
62315
  import_diff = require("diff");
61685
62316
  MAX_SNAPSHOT_CONTENT_CHARS = 25e4;
61686
62317
  MAX_DIFF_HUNKS = 200;
@@ -61754,7 +62385,7 @@ var init_scrape_vault_sink = __esm({
61754
62385
 
61755
62386
  // src/api/scrape-image-sink.ts
61756
62387
  function idempotencyKey2(userId, vault, input) {
61757
- return `scrape-image-${(0, import_node_crypto41.createHash)("sha256").update(`${userId}\0${vault}\0${input.sourceKind}\0${input.sourceUrl}\0${input.imageUrl ?? ""}\0${input.imageBase64 ?? ""}`).digest("hex")}`;
62388
+ return `scrape-image-${(0, import_node_crypto42.createHash)("sha256").update(`${userId}\0${vault}\0${input.sourceKind}\0${input.sourceUrl}\0${input.imageUrl ?? ""}\0${input.imageBase64 ?? ""}`).digest("hex")}`;
61758
62389
  }
61759
62390
  async function persistScrapeImagesToMemory(user, inputs, vault) {
61760
62391
  const selected = inputs.slice(0, MAX_IMAGES_PER_SCRAPE);
@@ -61796,11 +62427,11 @@ async function persistScrapeImagesToMemory(user, inputs, vault) {
61796
62427
  assets
61797
62428
  };
61798
62429
  }
61799
- var import_node_crypto41, MAX_IMAGES_PER_SCRAPE;
62430
+ var import_node_crypto42, MAX_IMAGES_PER_SCRAPE;
61800
62431
  var init_scrape_image_sink = __esm({
61801
62432
  "src/api/scrape-image-sink.ts"() {
61802
62433
  "use strict";
61803
- import_node_crypto41 = require("crypto");
62434
+ import_node_crypto42 = require("crypto");
61804
62435
  init_memory();
61805
62436
  MAX_IMAGES_PER_SCRAPE = 25;
61806
62437
  }
@@ -62253,7 +62884,7 @@ function canonicalJson(value) {
62253
62884
  return JSON.stringify(value);
62254
62885
  }
62255
62886
  function sha2562(value) {
62256
- return (0, import_node_crypto42.createHash)("sha256").update(value).digest("hex");
62887
+ return (0, import_node_crypto43.createHash)("sha256").update(value).digest("hex");
62257
62888
  }
62258
62889
  function sensitiveKey(key) {
62259
62890
  const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
@@ -62468,11 +63099,11 @@ async function importServiceConnectionToMemory(identity, input, dependencies) {
62468
63099
  ...!searchReady ? { warning: "The snapshot was stored, but no search chunks were indexed yet." } : {}
62469
63100
  };
62470
63101
  }
62471
- var import_node_crypto42, CONNECTION_MEMORY_IMPORT_MAX_ARGS_BYTES, CONNECTION_MEMORY_IMPORT_MAX_RESULT_BYTES, CONNECTION_MEMORY_IMPORT_MAX_STRING_CHARS, CONNECTION_MEMORY_IMPORT_MAX_DEPTH, ConnectionMemoryImportError;
63102
+ var import_node_crypto43, CONNECTION_MEMORY_IMPORT_MAX_ARGS_BYTES, CONNECTION_MEMORY_IMPORT_MAX_RESULT_BYTES, CONNECTION_MEMORY_IMPORT_MAX_STRING_CHARS, CONNECTION_MEMORY_IMPORT_MAX_DEPTH, ConnectionMemoryImportError;
62472
63103
  var init_connection_memory_import = __esm({
62473
63104
  "src/api/connection-memory-import.ts"() {
62474
63105
  "use strict";
62475
- import_node_crypto42 = require("crypto");
63106
+ import_node_crypto43 = require("crypto");
62476
63107
  init_slugify();
62477
63108
  CONNECTION_MEMORY_IMPORT_MAX_ARGS_BYTES = 64 * 1024;
62478
63109
  CONNECTION_MEMORY_IMPORT_MAX_RESULT_BYTES = 1e6;
@@ -62690,7 +63321,7 @@ function finitePositive(value, fallback) {
62690
63321
  return Number.isFinite(value) && value > 0 ? value : fallback;
62691
63322
  }
62692
63323
  async function collectConnectedDataExport(args) {
62693
- const exportId = (0, import_node_crypto43.randomUUID)();
63324
+ const exportId = (0, import_node_crypto44.randomUUID)();
62694
63325
  const now = args.now ?? Date.now;
62695
63326
  const startedAt = now();
62696
63327
  const budgetMs = finitePositive(CONNECTED_DATA_EXPORT_BUDGET_MS, 24e4);
@@ -62804,11 +63435,11 @@ ${lines.length ? `${lines.join("\n")}
62804
63435
  untrustedContent: true
62805
63436
  };
62806
63437
  }
62807
- var import_node_crypto43, CONNECTED_DATA_INLINE_BUDGET_BYTES, CONNECTED_DATA_MAX_EXPORT_BYTES, CONNECTED_DATA_EXPORT_BUDGET_MS, CONNECTED_DATA_PAGE_START_HEADROOM_MS, CONNECTED_DATA_DATASETS, ConnectedDataExportValidationError;
63438
+ var import_node_crypto44, CONNECTED_DATA_INLINE_BUDGET_BYTES, CONNECTED_DATA_MAX_EXPORT_BYTES, CONNECTED_DATA_EXPORT_BUDGET_MS, CONNECTED_DATA_PAGE_START_HEADROOM_MS, CONNECTED_DATA_DATASETS, ConnectedDataExportValidationError;
62808
63439
  var init_connected_data_export = __esm({
62809
63440
  "src/api/connected-data-export.ts"() {
62810
63441
  "use strict";
62811
- import_node_crypto43 = require("crypto");
63442
+ import_node_crypto44 = require("crypto");
62812
63443
  CONNECTED_DATA_INLINE_BUDGET_BYTES = Number(
62813
63444
  process.env.MCP_SCRAPER_CONNECTED_DATA_INLINE_BUDGET_BYTES ?? 5e4
62814
63445
  );
@@ -62922,7 +63553,7 @@ async function exportSearchConsoleTableData(args) {
62922
63553
  offset += rows.length;
62923
63554
  if (stoppedForBytes || rows.length < limit || offset >= matchedRows) break;
62924
63555
  }
62925
- const exportId = (0, import_node_crypto44.randomUUID)();
63556
+ const exportId = (0, import_node_crypto45.randomUUID)();
62926
63557
  const artifact = await args.writeArtifact({
62927
63558
  ownerId: args.ownerId,
62928
63559
  exportId,
@@ -62944,11 +63575,11 @@ async function exportSearchConsoleTableData(args) {
62944
63575
  warnings
62945
63576
  };
62946
63577
  }
62947
- var import_node_crypto44, SEARCH_CONSOLE_TABLE_EXPORT_MAX_ROWS, SEARCH_CONSOLE_TABLE_EXPORT_PAGE_SIZE, SEARCH_CONSOLE_TABLE_EXPORT_MAX_BYTES, SEARCH_CONSOLE_TABLE_COLUMNS, SearchConsoleTableExportValidationError;
63578
+ var import_node_crypto45, SEARCH_CONSOLE_TABLE_EXPORT_MAX_ROWS, SEARCH_CONSOLE_TABLE_EXPORT_PAGE_SIZE, SEARCH_CONSOLE_TABLE_EXPORT_MAX_BYTES, SEARCH_CONSOLE_TABLE_COLUMNS, SearchConsoleTableExportValidationError;
62948
63579
  var init_search_console_table_export = __esm({
62949
63580
  "src/api/search-console-table-export.ts"() {
62950
63581
  "use strict";
62951
- import_node_crypto44 = require("crypto");
63582
+ import_node_crypto45 = require("crypto");
62952
63583
  SEARCH_CONSOLE_TABLE_EXPORT_MAX_ROWS = 5e4;
62953
63584
  SEARCH_CONSOLE_TABLE_EXPORT_PAGE_SIZE = 2e3;
62954
63585
  SEARCH_CONSOLE_TABLE_EXPORT_MAX_BYTES = 50 * 1024 * 1024;
@@ -64127,7 +64758,7 @@ async function listNangoToolsDirect(identity, connectionId) {
64127
64758
  });
64128
64759
  const readTools = [...policies.values()].filter((policy5) => policy5.classification === "read").map((policy5) => policy5.name);
64129
64760
  const actionTools = [...policies.values()].filter((policy5) => policy5.classification === "action").map((policy5) => policy5.name);
64130
- const revision = (0, import_node_crypto45.createHash)("sha256").update(JSON.stringify(tools.map((tool) => ({ name: tool.name, inputSchema: tool.inputSchema })))).digest("hex");
64761
+ const revision = (0, import_node_crypto46.createHash)("sha256").update(JSON.stringify(tools.map((tool) => ({ name: tool.name, inputSchema: tool.inputSchema })))).digest("hex");
64131
64762
  await updateServiceConnectionTools(connection.id, readTools, actionTools, revision);
64132
64763
  const refreshed = await getOwnedServiceConnection(identity, connection.id);
64133
64764
  return { connection: refreshed ?? { ...connection, readTools, actionTools, toolRevision: revision }, tools };
@@ -64154,8 +64785,8 @@ async function callNangoToolDirect(args) {
64154
64785
  identity: args.identity,
64155
64786
  ratePolicyVersion: CONNECTED_USAGE_RATE_POLICY_VERSION
64156
64787
  });
64157
- const requestId = args.requestId?.trim() || (0, import_node_crypto45.randomUUID)();
64158
- const idempotencyKey3 = `main-nango:${(0, import_node_crypto45.createHash)("sha256").update(args.identity.toLowerCase()).update("\0").update(connection.id).update("\0").update(args.tool).update("\0").update(requestId).digest("hex")}`;
64788
+ const requestId = args.requestId?.trim() || (0, import_node_crypto46.randomUUID)();
64789
+ const idempotencyKey3 = `main-nango:${(0, import_node_crypto46.createHash)("sha256").update(args.identity.toLowerCase()).update("\0").update(connection.id).update("\0").update(args.tool).update("\0").update(requestId).digest("hex")}`;
64159
64790
  const startedAt = /* @__PURE__ */ new Date();
64160
64791
  const started = performance.now();
64161
64792
  let result;
@@ -64180,7 +64811,7 @@ async function callNangoToolDirect(args) {
64180
64811
  toolName: args.tool,
64181
64812
  operationKind: args.operationKind ?? args.classification,
64182
64813
  outcome: providerError ? "error" : "partial",
64183
- requestId: requestId.length <= 200 ? requestId : (0, import_node_crypto45.createHash)("sha256").update(requestId).digest("hex"),
64814
+ requestId: requestId.length <= 200 ? requestId : (0, import_node_crypto46.createHash)("sha256").update(requestId).digest("hex"),
64184
64815
  startedAt: startedAt.toISOString(),
64185
64816
  completedAt: completedAt.toISOString()
64186
64817
  }
@@ -64222,15 +64853,15 @@ async function describeNangoToolDirect(identity, connectionId, toolName) {
64222
64853
  providerContractHash: MAIN_INTEGRATION_CONTRACT_HASH,
64223
64854
  protocolVersion: null,
64224
64855
  schemaSource: "live_tools_list",
64225
- schemaHash: (0, import_node_crypto45.createHash)("sha256").update(JSON.stringify(projected)).digest("hex"),
64856
+ schemaHash: (0, import_node_crypto46.createHash)("sha256").update(JSON.stringify(projected)).digest("hex"),
64226
64857
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
64227
64858
  };
64228
64859
  }
64229
- var import_node_crypto45, import_client13, DEFAULT_NANGO_MCP_URL, NANGO_TIMEOUT_MS, NANGO_CONNECTION_PAGE_SIZE, NANGO_CONNECTION_MAX_PAGES, MAIN_INTEGRATION_CONTRACT_VERSION, MAIN_INTEGRATION_CONTRACT_HASH, MainNangoTransportError;
64860
+ var import_node_crypto46, import_client13, DEFAULT_NANGO_MCP_URL, NANGO_TIMEOUT_MS, NANGO_CONNECTION_PAGE_SIZE, NANGO_CONNECTION_MAX_PAGES, MAIN_INTEGRATION_CONTRACT_VERSION, MAIN_INTEGRATION_CONTRACT_HASH, MainNangoTransportError;
64230
64861
  var init_main_nango_transport = __esm({
64231
64862
  "src/api/main-nango-transport.ts"() {
64232
64863
  "use strict";
64233
- import_node_crypto45 = require("crypto");
64864
+ import_node_crypto46 = require("crypto");
64234
64865
  import_client13 = require("@modelcontextprotocol/client");
64235
64866
  init_service_connections();
64236
64867
  init_connected_usage_billing();
@@ -64854,8 +65485,8 @@ async function setScheduleConnectionActionsEnabled(identity, connectionId, enabl
64854
65485
  return data.connection.actionsEnabled === true;
64855
65486
  }
64856
65487
  async function callScheduleConnectionAction(identity, connectionId, input, tool, idempotencyKey3) {
64857
- const requestId = `main-connected-action:${(0, import_node_crypto46.createHash)("sha256").update(identity).update("\0").update(idempotencyKey3?.trim() || (0, import_node_crypto46.randomUUID)()).digest("hex")}`;
64858
- const requestDigest = (0, import_node_crypto46.createHash)("sha256").update(connectionId).update("\0").update(tool?.trim() ?? "").update("\0").update(canonicalJson2(input)).digest("hex");
65488
+ const requestId = `main-connected-action:${(0, import_node_crypto47.createHash)("sha256").update(identity).update("\0").update(idempotencyKey3?.trim() || (0, import_node_crypto47.randomUUID)()).digest("hex")}`;
65489
+ const requestDigest = (0, import_node_crypto47.createHash)("sha256").update(connectionId).update("\0").update(tool?.trim() ?? "").update("\0").update(canonicalJson2(input)).digest("hex");
64859
65490
  if (mainOwnsIntegrations()) {
64860
65491
  const selectedTool = tool?.trim();
64861
65492
  if (!selectedTool) throw new NangoControlError("An action tool is required.", 400, "invalid_request", false);
@@ -65006,7 +65637,7 @@ function canonicalJson2(value) {
65006
65637
  return JSON.stringify(value);
65007
65638
  }
65008
65639
  function projectedToolSchemaHash(tool) {
65009
- return (0, import_node_crypto46.createHash)("sha256").update(canonicalJson2(tool)).digest("hex");
65640
+ return (0, import_node_crypto47.createHash)("sha256").update(canonicalJson2(tool)).digest("hex");
65010
65641
  }
65011
65642
  async function describeNangoTool(identity, connectionId, tool, fresh) {
65012
65643
  if (mainOwnsIntegrations()) {
@@ -65283,11 +65914,11 @@ async function callMainOwnedExportPage(identity, input) {
65283
65914
  untrustedContent: true
65284
65915
  };
65285
65916
  }
65286
- var import_node_crypto46, DEFAULT_NANGO_CONTROL_URL, DISABLED_NANGO_TOOLS, CONNECTION_SYNC_REQUIRED_TOOLS, CONNECTION_SYNC_OPTIONAL_TOOLS, NangoControlError, ScheduleConnectionValidationError, SAFE_CONTROL_ERROR_CODES, FIXED_CONTROL_ERROR_MESSAGES, CONTROL_ERROR_CODE_ALIASES;
65917
+ var import_node_crypto47, DEFAULT_NANGO_CONTROL_URL, DISABLED_NANGO_TOOLS, CONNECTION_SYNC_REQUIRED_TOOLS, CONNECTION_SYNC_OPTIONAL_TOOLS, NangoControlError, ScheduleConnectionValidationError, SAFE_CONTROL_ERROR_CODES, FIXED_CONTROL_ERROR_MESSAGES, CONTROL_ERROR_CODE_ALIASES;
65287
65918
  var init_nango_control = __esm({
65288
65919
  "src/api/nango-control.ts"() {
65289
65920
  "use strict";
65290
- import_node_crypto46 = require("crypto");
65921
+ import_node_crypto47 = require("crypto");
65291
65922
  init_connected_data_export();
65292
65923
  init_slack_connected_data_export();
65293
65924
  init_main_nango_transport();
@@ -65643,7 +66274,7 @@ async function callResendRead(identity, connectionId, tool, args) {
65643
66274
  return isRecord5(data) ? data.result ?? data : data;
65644
66275
  }
65645
66276
  async function callResendAction(identity, connectionId, tool, input, idempotencyKey3) {
65646
- const requestId = `main-resend-action:${(0, import_node_crypto47.createHash)("sha256").update(identity).update("\0").update(idempotencyKey3.trim()).digest("hex")}`;
66277
+ const requestId = `main-resend-action:${(0, import_node_crypto48.createHash)("sha256").update(identity).update("\0").update(idempotencyKey3.trim()).digest("hex")}`;
65647
66278
  const body = await controlRequest2("/api/internal/resend/actions/call", {
65648
66279
  method: "POST",
65649
66280
  headers: { "x-request-id": requestId },
@@ -65708,12 +66339,12 @@ async function callResendExportPage(identity, input) {
65708
66339
  untrustedContent: true
65709
66340
  };
65710
66341
  }
65711
- var import_node_crypto47, DEFAULT_CONNECTION_CONTROL_URL, RESEND_PROVIDER_CONFIG_KEY, RESEND_LOGO_URL, RESEND_DOCS_URL, RESEND_ADMIN_BLOCKED_TOOLS, RESEND_CONNECTION_SYNC_REQUIRED_TOOLS, ResendControlError;
66342
+ var import_node_crypto48, DEFAULT_CONNECTION_CONTROL_URL, RESEND_PROVIDER_CONFIG_KEY, RESEND_LOGO_URL, RESEND_DOCS_URL, RESEND_ADMIN_BLOCKED_TOOLS, RESEND_CONNECTION_SYNC_REQUIRED_TOOLS, ResendControlError;
65712
66343
  var init_resend_control = __esm({
65713
66344
  "src/api/resend-control.ts"() {
65714
66345
  "use strict";
65715
66346
  init_connected_data_export();
65716
- import_node_crypto47 = require("crypto");
66347
+ import_node_crypto48 = require("crypto");
65717
66348
  DEFAULT_CONNECTION_CONTROL_URL = "https://mcp-scraper-scheduler.vercel.app";
65718
66349
  RESEND_PROVIDER_CONFIG_KEY = "resend";
65719
66350
  RESEND_LOGO_URL = "https://cdn.resend.com/brand/resend-icon-black.svg";
@@ -66231,7 +66862,7 @@ async function chargeTierChangeNow(stripeClient, subscriptionId, customerId) {
66231
66862
  return { ok: false, amountDue: 0, error: err instanceof Error ? err.message : "Unable to charge the plan change immediately." };
66232
66863
  }
66233
66864
  }
66234
- var import_resend3, import_node_crypto48, import_hono35, import_hono36, import_factory7, import_cookie2, import_stripe2, secureCookies2, isProduction2, sessionCookieOptions2, requireAllowedOrigin, auth3, sessionAuth, requireIntegrationsTier, requirePaidSchedulingTier, app, deploymentProfile, STRIPE_API_VERSION, SYNC_HARVEST_TIMEOUT_OVERRIDE_MS;
66865
+ var import_resend3, import_node_crypto49, import_hono35, import_hono36, import_factory7, import_cookie2, import_stripe2, secureCookies2, isProduction2, sessionCookieOptions2, requireAllowedOrigin, auth3, sessionAuth, requireIntegrationsTier, requirePaidSchedulingTier, app, deploymentProfile, STRIPE_API_VERSION, SYNC_HARVEST_TIMEOUT_OVERRIDE_MS;
66235
66866
  var init_server = __esm({
66236
66867
  "src/api/server.ts"() {
66237
66868
  "use strict";
@@ -66244,7 +66875,7 @@ var init_server = __esm({
66244
66875
  init_og();
66245
66876
  import_resend3 = require("resend");
66246
66877
  init_url_utils();
66247
- import_node_crypto48 = require("crypto");
66878
+ import_node_crypto49 = require("crypto");
66248
66879
  init_kpo_extractor();
66249
66880
  init_screenshot();
66250
66881
  init_media_extractor();
@@ -67776,7 +68407,7 @@ var init_server = __esm({
67776
68407
  if (!harvestOk) return c.json(insufficientBalanceResponse(harvestBal, harvestCost), 402);
67777
68408
  jobId2 = await createJob(user.id, options.query, { ...options, billingHoldMc: harvestCost }, body.callback_url);
67778
68409
  } else {
67779
- jobId2 = (0, import_node_crypto48.randomUUID)();
68410
+ jobId2 = (0, import_node_crypto49.randomUUID)();
67780
68411
  const billingDebitKey = `paa-harvest:${jobId2}:hold`;
67781
68412
  const description = `PAA harvest: ${options.query}`.slice(0, 500);
67782
68413
  const hold = await debitMcIdempotent(