@ainyc/canonry 4.78.0 → 4.81.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.
Files changed (26) hide show
  1. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +38 -12
  2. package/assets/assets/{BacklinksPage-CwXveumn.js → BacklinksPage-DHShKKpo.js} +1 -1
  3. package/assets/assets/{ChartPrimitives-DntKGI5J.js → ChartPrimitives-udHScxjY.js} +1 -1
  4. package/assets/assets/ProjectPage-BsS1anh7.js +6 -0
  5. package/assets/assets/{RunRow-DMtYXaxG.js → RunRow-CXyPHMVQ.js} +1 -1
  6. package/assets/assets/{RunsPage-Cz-YlucO.js → RunsPage-BpQ_NpFt.js} +1 -1
  7. package/assets/assets/{SettingsPage-BCuG3C-0.js → SettingsPage-1ep4ch7n.js} +1 -1
  8. package/assets/assets/{TrafficPage-DV8X47wa.js → TrafficPage-C3Hx-sE7.js} +1 -1
  9. package/assets/assets/TrafficSourceDetailPage-B26n2R6G.js +1 -0
  10. package/assets/assets/{arrow-left-CUmHyNnF.js → arrow-left-Dc_IPJxw.js} +1 -1
  11. package/assets/assets/{extract-error-message-DFjy9_zi.js → extract-error-message-B3PoKkHW.js} +1 -1
  12. package/assets/assets/{index-D9smxU6R.js → index-DhdFTQkU.js} +86 -86
  13. package/assets/assets/{trash-2-B_UtEEm8.js → trash-2-BQ69cGl0.js} +1 -1
  14. package/assets/index.html +1 -1
  15. package/dist/{chunk-XI6YSTGE.js → chunk-6XOZSS3Y.js} +258 -8
  16. package/dist/{chunk-KPN22EWK.js → chunk-GMT3YPLT.js} +214 -4
  17. package/dist/{chunk-3WXARKUE.js → chunk-UAQ42NVJ.js} +1346 -357
  18. package/dist/{chunk-QKTIP6GC.js → chunk-VX5C7DK7.js} +902 -313
  19. package/dist/cli.js +468 -152
  20. package/dist/index.d.ts +17 -0
  21. package/dist/index.js +4 -4
  22. package/dist/{intelligence-service-CDVUUG7O.js → intelligence-service-CAAQAKPN.js} +2 -2
  23. package/dist/mcp.js +9 -3
  24. package/package.json +9 -8
  25. package/assets/assets/ProjectPage-CVudiU8X.js +0 -6
  26. package/assets/assets/TrafficSourceDetailPage-BmYhK9jm.js +0 -1
@@ -1 +1 @@
1
- import{c}from"./index-D9smxU6R.js";const a=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],h=c("circle-check",a);const e=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]],n=c("circle-question-mark",e);const o=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],s=c("download",o);const t=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],r=c("trash-2",t);export{h as C,s as D,r as T,n as a};
1
+ import{c}from"./index-DhdFTQkU.js";const a=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],h=c("circle-check",a);const e=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]],n=c("circle-question-mark",e);const o=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],s=c("download",o);const t=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],r=c("trash-2",t);export{h as C,s as D,r as T,n as a};
package/assets/index.html CHANGED
@@ -12,7 +12,7 @@
12
12
  <link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png" />
13
13
  <link rel="apple-touch-icon" href="./apple-touch-icon.png" />
14
14
  <title>Canonry</title>
15
- <script type="module" crossorigin src="./assets/index-D9smxU6R.js"></script>
15
+ <script type="module" crossorigin src="./assets/index-DhdFTQkU.js"></script>
16
16
  <link rel="modulepreload" crossorigin href="./assets/vendor-tanstack-Dq7p98wZ.js">
17
17
  <link rel="modulepreload" crossorigin href="./assets/vendor-radix-B57xfQbP.js">
18
18
  <link rel="modulepreload" crossorigin href="./assets/vendor-recharts-ClRVR6aX.js">
@@ -2,6 +2,7 @@ import {
2
2
  AGENT_MEMORY_KEY_MAX_LENGTH,
3
3
  AGENT_MEMORY_VALUE_MAX_BYTES,
4
4
  DISCOVERY_MAX_PROBES_CAP,
5
+ backlinkSourceSchema,
5
6
  competitorBatchRequestSchema,
6
7
  discoveryBucketSchema,
7
8
  discoveryCompetitorTypeSchema,
@@ -22,7 +23,7 @@ import {
22
23
  trafficConnectVercelRequestSchema,
23
24
  trafficConnectWordpressRequestSchema,
24
25
  trafficEventKindSchema
25
- } from "./chunk-KPN22EWK.js";
26
+ } from "./chunk-GMT3YPLT.js";
26
27
 
27
28
  // src/config.ts
28
29
  import fs from "fs";
@@ -2193,6 +2194,94 @@ var getApiV1ProjectsByNameGbpSummary = (options) => {
2193
2194
  ...options
2194
2195
  });
2195
2196
  };
2197
+ var postApiV1ProjectsByNameAdsConnect = (options) => {
2198
+ return (options.client ?? client).post({
2199
+ security: [
2200
+ {
2201
+ scheme: "bearer",
2202
+ type: "http"
2203
+ }
2204
+ ],
2205
+ url: "/api/v1/projects/{name}/ads/connect",
2206
+ ...options,
2207
+ headers: {
2208
+ "Content-Type": "application/json",
2209
+ ...options.headers
2210
+ }
2211
+ });
2212
+ };
2213
+ var deleteApiV1ProjectsByNameAdsConnection = (options) => {
2214
+ return (options.client ?? client).delete({
2215
+ security: [
2216
+ {
2217
+ scheme: "bearer",
2218
+ type: "http"
2219
+ }
2220
+ ],
2221
+ url: "/api/v1/projects/{name}/ads/connection",
2222
+ ...options
2223
+ });
2224
+ };
2225
+ var getApiV1ProjectsByNameAdsStatus = (options) => {
2226
+ return (options.client ?? client).get({
2227
+ security: [
2228
+ {
2229
+ scheme: "bearer",
2230
+ type: "http"
2231
+ }
2232
+ ],
2233
+ url: "/api/v1/projects/{name}/ads/status",
2234
+ ...options
2235
+ });
2236
+ };
2237
+ var postApiV1ProjectsByNameAdsSync = (options) => {
2238
+ return (options.client ?? client).post({
2239
+ security: [
2240
+ {
2241
+ scheme: "bearer",
2242
+ type: "http"
2243
+ }
2244
+ ],
2245
+ url: "/api/v1/projects/{name}/ads/sync",
2246
+ ...options
2247
+ });
2248
+ };
2249
+ var getApiV1ProjectsByNameAdsCampaigns = (options) => {
2250
+ return (options.client ?? client).get({
2251
+ security: [
2252
+ {
2253
+ scheme: "bearer",
2254
+ type: "http"
2255
+ }
2256
+ ],
2257
+ url: "/api/v1/projects/{name}/ads/campaigns",
2258
+ ...options
2259
+ });
2260
+ };
2261
+ var getApiV1ProjectsByNameAdsInsights = (options) => {
2262
+ return (options.client ?? client).get({
2263
+ security: [
2264
+ {
2265
+ scheme: "bearer",
2266
+ type: "http"
2267
+ }
2268
+ ],
2269
+ url: "/api/v1/projects/{name}/ads/insights",
2270
+ ...options
2271
+ });
2272
+ };
2273
+ var getApiV1ProjectsByNameAdsSummary = (options) => {
2274
+ return (options.client ?? client).get({
2275
+ security: [
2276
+ {
2277
+ scheme: "bearer",
2278
+ type: "http"
2279
+ }
2280
+ ],
2281
+ url: "/api/v1/projects/{name}/ads/summary",
2282
+ ...options
2283
+ });
2284
+ };
2196
2285
  var postApiV1ProjectsByNameBingConnect = (options) => {
2197
2286
  return (options.client ?? client).post({
2198
2287
  security: [
@@ -3133,6 +3222,30 @@ var getApiV1ProjectsByNameBacklinksHistory = (options) => {
3133
3222
  ...options
3134
3223
  });
3135
3224
  };
3225
+ var getApiV1ProjectsByNameBacklinksSources = (options) => {
3226
+ return (options.client ?? client).get({
3227
+ security: [
3228
+ {
3229
+ scheme: "bearer",
3230
+ type: "http"
3231
+ }
3232
+ ],
3233
+ url: "/api/v1/projects/{name}/backlinks/sources",
3234
+ ...options
3235
+ });
3236
+ };
3237
+ var postApiV1ProjectsByNameBacklinksBingSync = (options) => {
3238
+ return (options.client ?? client).post({
3239
+ security: [
3240
+ {
3241
+ scheme: "bearer",
3242
+ type: "http"
3243
+ }
3244
+ ],
3245
+ url: "/api/v1/projects/{name}/backlinks/bing-sync",
3246
+ ...options
3247
+ });
3248
+ };
3136
3249
  var postApiV1ProjectsByNameTrafficConnectCloudRun = (options) => {
3137
3250
  return (options.client ?? client).post({
3138
3251
  security: [
@@ -4129,6 +4242,41 @@ var ApiClient = class {
4129
4242
  })
4130
4243
  );
4131
4244
  }
4245
+ async adsConnect(project, body) {
4246
+ return this.invoke(
4247
+ () => postApiV1ProjectsByNameAdsConnect({ client: this.heyClient, path: { name: project }, body })
4248
+ );
4249
+ }
4250
+ async adsDisconnect(project) {
4251
+ return this.invoke(
4252
+ () => deleteApiV1ProjectsByNameAdsConnection({ client: this.heyClient, path: { name: project } })
4253
+ );
4254
+ }
4255
+ async getAdsStatus(project) {
4256
+ return this.invoke(
4257
+ () => getApiV1ProjectsByNameAdsStatus({ client: this.heyClient, path: { name: project } })
4258
+ );
4259
+ }
4260
+ async triggerAdsSync(project) {
4261
+ return this.invoke(
4262
+ () => postApiV1ProjectsByNameAdsSync({ client: this.heyClient, path: { name: project } })
4263
+ );
4264
+ }
4265
+ async getAdsCampaigns(project) {
4266
+ return this.invoke(
4267
+ () => getApiV1ProjectsByNameAdsCampaigns({ client: this.heyClient, path: { name: project } })
4268
+ );
4269
+ }
4270
+ async getAdsInsights(project, query) {
4271
+ return this.invoke(
4272
+ () => getApiV1ProjectsByNameAdsInsights({ client: this.heyClient, path: { name: project }, query })
4273
+ );
4274
+ }
4275
+ async getAdsSummary(project) {
4276
+ return this.invoke(
4277
+ () => getApiV1ProjectsByNameAdsSummary({ client: this.heyClient, path: { name: project } })
4278
+ );
4279
+ }
4132
4280
  async listGbpMetrics(project, opts) {
4133
4281
  return this.invoke(
4134
4282
  () => getApiV1ProjectsByNameGbpMetrics({
@@ -4937,6 +5085,23 @@ var ApiClient = class {
4937
5085
  })
4938
5086
  );
4939
5087
  }
5088
+ async backlinksBingSync(project) {
5089
+ return this.invoke(
5090
+ () => postApiV1ProjectsByNameBacklinksBingSync({
5091
+ client: this.heyClient,
5092
+ path: { name: project }
5093
+ })
5094
+ );
5095
+ }
5096
+ async backlinksSources(project, opts = {}) {
5097
+ return this.invoke(
5098
+ () => getApiV1ProjectsByNameBacklinksSources({
5099
+ client: this.heyClient,
5100
+ path: { name: project },
5101
+ query: { excludeCrawlers: opts.excludeCrawlers ? "1" : void 0 }
5102
+ })
5103
+ );
5104
+ }
4940
5105
  async backlinksSummary(project, opts = {}) {
4941
5106
  return this.invoke(
4942
5107
  () => getApiV1ProjectsByNameBacklinksSummary({
@@ -4944,7 +5109,8 @@ var ApiClient = class {
4944
5109
  path: { name: project },
4945
5110
  query: {
4946
5111
  release: opts.release,
4947
- excludeCrawlers: opts.excludeCrawlers ? "1" : void 0
5112
+ excludeCrawlers: opts.excludeCrawlers ? "1" : void 0,
5113
+ source: opts.source
4948
5114
  }
4949
5115
  })
4950
5116
  );
@@ -4958,14 +5124,19 @@ var ApiClient = class {
4958
5124
  limit: opts.limit,
4959
5125
  offset: opts.offset,
4960
5126
  release: opts.release,
4961
- excludeCrawlers: opts.excludeCrawlers ? "1" : void 0
5127
+ excludeCrawlers: opts.excludeCrawlers ? "1" : void 0,
5128
+ source: opts.source
4962
5129
  }
4963
5130
  })
4964
5131
  );
4965
5132
  }
4966
- async backlinksHistory(project) {
5133
+ async backlinksHistory(project, opts = {}) {
4967
5134
  return this.invoke(
4968
- () => getApiV1ProjectsByNameBacklinksHistory({ client: this.heyClient, path: { name: project } })
5135
+ () => getApiV1ProjectsByNameBacklinksHistory({
5136
+ client: this.heyClient,
5137
+ path: { name: project },
5138
+ query: { source: opts.source }
5139
+ })
4969
5140
  );
4970
5141
  }
4971
5142
  };
@@ -5147,6 +5318,13 @@ var gbpLocationScopedInputSchema = z2.object({
5147
5318
  var gbpAccountsInputSchema = z2.object({
5148
5319
  project: projectNameSchema
5149
5320
  });
5321
+ var adsInsightsInputSchema = z2.object({
5322
+ project: projectNameSchema,
5323
+ level: z2.enum(["campaign", "ad_group"]).optional(),
5324
+ entityId: z2.string().optional(),
5325
+ from: z2.string().optional(),
5326
+ to: z2.string().optional()
5327
+ });
5150
5328
  var keywordsInputSchema = z2.object({
5151
5329
  project: projectNameSchema,
5152
5330
  request: keywordBatchRequestSchema
@@ -5202,7 +5380,11 @@ var contentMapInputSchema = z2.object({
5202
5380
  var backlinksDomainsInputSchema = z2.object({
5203
5381
  project: projectNameSchema,
5204
5382
  limit: z2.number().int().positive().max(200).optional().describe("Max linking-domain rows. Default 50, max 200."),
5205
- release: z2.string().optional().describe("Common Crawl release id (e.g., cc-main-2026-jan-feb-mar). Omit for the most recent release with data.")
5383
+ release: z2.string().optional().describe("Window id (Common Crawl release e.g. cc-main-2026-jan-feb-mar, or Bing window e.g. bing-2026-06-15). Omit for the most recent window with data."),
5384
+ source: backlinkSourceSchema.optional().describe("Backlink source: commoncrawl (default) or bing-webmaster.")
5385
+ });
5386
+ var backlinksSourcesInputSchema = z2.object({
5387
+ project: projectNameSchema
5206
5388
  });
5207
5389
  var memoryUpsertInputSchema = z2.object({
5208
5390
  project: projectNameSchema,
@@ -5692,7 +5874,7 @@ var canonryMcpTools = [
5692
5874
  defineTool({
5693
5875
  name: "canonry_backlinks_domains",
5694
5876
  title: "List backlink domains",
5695
- description: "Backlink summary and top linking domains from the most recent ready Common Crawl release for a project. Off-site authority signal that correlates with citation likelihood. Returns null summary when no release sync has completed for this workspace.",
5877
+ description: "Source-aware backlink summary + top linking domains for a project. `source=commoncrawl` (default) reads the most recent ready Common Crawl release; `source=bing-webmaster` reads the latest live Bing Webmaster inbound-link window. Off-site authority signal that correlates with citation likelihood. Returns null summary when the source has no data yet.",
5696
5878
  access: "read",
5697
5879
  tier: "setup",
5698
5880
  inputSchema: backlinksDomainsInputSchema,
@@ -5700,9 +5882,21 @@ var canonryMcpTools = [
5700
5882
  openApiOperations: ["GET /api/v1/projects/{name}/backlinks/domains"],
5701
5883
  handler: (client2, input) => client2.backlinksDomains(input.project, {
5702
5884
  limit: input.limit ?? 50,
5703
- release: input.release
5885
+ release: input.release,
5886
+ source: input.source
5704
5887
  })
5705
5888
  }),
5889
+ defineTool({
5890
+ name: "canonry_backlinks_sources",
5891
+ title: "Backlink source availability",
5892
+ description: "Reports which backlink sources are set up for a project (commoncrawl, bing-webmaster): connected, has data, latest window, linking-domain count, and freshness. Use to decide whether to read CC vs Bing or prompt the operator to connect a source.",
5893
+ access: "read",
5894
+ tier: "setup",
5895
+ inputSchema: backlinksSourcesInputSchema,
5896
+ annotations: readAnnotations(),
5897
+ openApiOperations: ["GET /api/v1/projects/{name}/backlinks/sources"],
5898
+ handler: (client2, input) => client2.backlinksSources(input.project)
5899
+ }),
5706
5900
  defineTool({
5707
5901
  name: "canonry_settings_get",
5708
5902
  title: "Get settings",
@@ -6564,6 +6758,62 @@ var canonryMcpTools = [
6564
6758
  sitemapUrl: input.sitemapUrl,
6565
6759
  limit: input.limit
6566
6760
  })
6761
+ }),
6762
+ // ----- OpenAI ads (ChatGPT ads) -----
6763
+ defineTool({
6764
+ name: "canonry_ads_status",
6765
+ title: "OpenAI ads connection status",
6766
+ description: "Connection status and last sync time for the project's OpenAI ad account (ChatGPT ads).",
6767
+ access: "read",
6768
+ tier: "ads",
6769
+ inputSchema: projectInputSchema,
6770
+ annotations: readAnnotations(),
6771
+ openApiOperations: ["GET /api/v1/projects/{name}/ads/status"],
6772
+ handler: (client2, input) => client2.getAdsStatus(input.project)
6773
+ }),
6774
+ defineTool({
6775
+ name: "canonry_ads_campaigns",
6776
+ title: "List synced ad campaigns",
6777
+ description: "Synced campaign snapshots with nested ad groups (context hints \u2014 the targeting primitive) and ads. Paid-surface structure; never conflate with organic cited/mentioned signals.",
6778
+ access: "read",
6779
+ tier: "ads",
6780
+ inputSchema: projectInputSchema,
6781
+ annotations: readAnnotations(),
6782
+ openApiOperations: ["GET /api/v1/projects/{name}/ads/campaigns"],
6783
+ handler: (client2, input) => client2.getAdsCampaigns(input.project)
6784
+ }),
6785
+ defineTool({
6786
+ name: "canonry_ads_insights",
6787
+ title: "Daily paid-performance rollups",
6788
+ description: "Daily paid-performance rollups per level (campaign/ad_group). Spend is integer micros; ctr/cpcMicros are derived server-side (null on zero denominators).",
6789
+ access: "read",
6790
+ tier: "ads",
6791
+ inputSchema: adsInsightsInputSchema,
6792
+ annotations: readAnnotations(),
6793
+ openApiOperations: ["GET /api/v1/projects/{name}/ads/insights"],
6794
+ handler: (client2, input) => client2.getAdsInsights(input.project, compactStringParams(input, ["level", "entityId", "from", "to"]))
6795
+ }),
6796
+ defineTool({
6797
+ name: "canonry_ads_summary",
6798
+ title: "Paid-performance summary",
6799
+ description: "Composite paid summary: campaign/ad-group/ad counts, campaign-level totals (impressions, clicks, spend micros, derived ctr/cpc) and the covered date window.",
6800
+ access: "read",
6801
+ tier: "ads",
6802
+ inputSchema: projectInputSchema,
6803
+ annotations: readAnnotations(),
6804
+ openApiOperations: ["GET /api/v1/projects/{name}/ads/summary"],
6805
+ handler: (client2, input) => client2.getAdsSummary(input.project)
6806
+ }),
6807
+ defineTool({
6808
+ name: "canonry_ads_sync",
6809
+ title: "Trigger ads sync",
6810
+ description: "Trigger an ads-sync run (entity snapshots + daily paid-performance rollups) for the connected OpenAI ad account. Returns the run id; poll canonry_run_get for status.",
6811
+ access: "write",
6812
+ tier: "ads",
6813
+ inputSchema: projectInputSchema,
6814
+ annotations: writeAnnotations({ idempotentHint: true }),
6815
+ openApiOperations: ["POST /api/v1/projects/{name}/ads/sync"],
6816
+ handler: (client2, input) => client2.triggerAdsSync(input.project)
6567
6817
  })
6568
6818
  ];
6569
6819
  var CANONRY_MCP_TOOL_COUNT = canonryMcpTools.length;
@@ -383,7 +383,8 @@ var runKindSchema = z3.enum([
383
383
  "traffic-sync",
384
384
  "aeo-discover-seed",
385
385
  "aeo-discover-probe",
386
- "gbp-sync"
386
+ "gbp-sync",
387
+ "ads-sync"
387
388
  ]);
388
389
  var RunKinds = runKindSchema.enum;
389
390
  var runTriggerSchema = z3.enum(["manual", "scheduled", "config-apply", "backfill", "probe"]);
@@ -1618,7 +1619,7 @@ var siteAuditRunResponseSchema = z14.object({
1618
1619
 
1619
1620
  // ../contracts/src/schedule.ts
1620
1621
  import { z as z15 } from "zod";
1621
- var schedulableRunKindSchema = z15.enum(["answer-visibility", "traffic-sync", "gbp-sync", "data-refresh", "backlinks-sync", "site-audit"]);
1622
+ var schedulableRunKindSchema = z15.enum(["answer-visibility", "traffic-sync", "gbp-sync", "data-refresh", "backlinks-sync", "site-audit", "ads-sync"]);
1622
1623
  var SchedulableRunKinds = schedulableRunKindSchema.enum;
1623
1624
  var scheduleDtoSchema = z15.object({
1624
1625
  id: z15.string(),
@@ -2500,6 +2501,22 @@ var agentMemoryDeleteRequestSchema = z21.object({
2500
2501
 
2501
2502
  // ../contracts/src/backlinks.ts
2502
2503
  import { z as z22 } from "zod";
2504
+ var backlinkSourceSchema = z22.enum(["commoncrawl", "bing-webmaster"]);
2505
+ var BacklinkSources = backlinkSourceSchema.enum;
2506
+ function computeBacklinkSummaryMetrics(rows) {
2507
+ if (rows.length === 0) {
2508
+ return { totalLinkingDomains: 0, totalHosts: 0, top10HostsShare: "0" };
2509
+ }
2510
+ const sorted = [...rows].sort((a, b) => b.numHosts - a.numHosts);
2511
+ const totalHosts = sorted.reduce((acc, r) => acc + r.numHosts, 0);
2512
+ const top10Hosts = sorted.slice(0, 10).reduce((acc, r) => acc + r.numHosts, 0);
2513
+ const share = totalHosts > 0 ? top10Hosts / totalHosts : 0;
2514
+ return {
2515
+ totalLinkingDomains: rows.length,
2516
+ totalHosts,
2517
+ top10HostsShare: share.toFixed(6)
2518
+ };
2519
+ }
2503
2520
  var ccReleaseSyncStatusSchema = z22.enum(["queued", "downloading", "querying", "ready", "failed"]);
2504
2521
  var CcReleaseSyncStatuses = ccReleaseSyncStatusSchema.enum;
2505
2522
  var ccReleaseSyncDtoSchema = z22.object({
@@ -2525,16 +2542,24 @@ var ccReleaseSyncDtoSchema = z22.object({
2525
2542
  });
2526
2543
  var backlinkDomainDtoSchema = z22.object({
2527
2544
  linkingDomain: z22.string(),
2528
- numHosts: z22.number().int()
2545
+ // For Common Crawl this is the count of distinct hosts within the linking
2546
+ // domain; for Bing Webmaster it is the count of distinct linking pages (URLs)
2547
+ // from that linking host. Read alongside `source` — the unit differs per source.
2548
+ numHosts: z22.number().int(),
2549
+ source: backlinkSourceSchema
2529
2550
  });
2530
2551
  var backlinkSummaryDtoSchema = z22.object({
2531
2552
  projectId: z22.string(),
2553
+ // Window identifier. Common Crawl uses the release slug
2554
+ // (`cc-main-YYYY-<mon>-<mon>-<mon>`); Bing Webmaster uses a synthetic
2555
+ // per-sync-day window (`bing-YYYY-MM-DD`).
2532
2556
  release: z22.string(),
2533
2557
  targetDomain: z22.string(),
2534
2558
  totalLinkingDomains: z22.number().int(),
2535
2559
  totalHosts: z22.number().int(),
2536
2560
  top10HostsShare: z22.string(),
2537
2561
  queriedAt: z22.string(),
2562
+ source: backlinkSourceSchema,
2538
2563
  // Populated when the response is filtered (e.g. ?excludeCrawlers=1).
2539
2564
  // Counts the rows omitted from totalLinkingDomains/totalHosts so callers
2540
2565
  // can show "N hidden" hints without re-deriving them.
@@ -2542,6 +2567,9 @@ var backlinkSummaryDtoSchema = z22.object({
2542
2567
  excludedHosts: z22.number().int().optional()
2543
2568
  });
2544
2569
  var backlinkListResponseSchema = z22.object({
2570
+ // The source this response was filtered to (defaults to commoncrawl when the
2571
+ // caller omits `?source`).
2572
+ source: backlinkSourceSchema,
2545
2573
  summary: backlinkSummaryDtoSchema.nullable(),
2546
2574
  total: z22.number().int(),
2547
2575
  rows: z22.array(backlinkDomainDtoSchema)
@@ -2551,7 +2579,38 @@ var backlinkHistoryEntrySchema = z22.object({
2551
2579
  totalLinkingDomains: z22.number().int(),
2552
2580
  totalHosts: z22.number().int(),
2553
2581
  top10HostsShare: z22.string(),
2554
- queriedAt: z22.string()
2582
+ queriedAt: z22.string(),
2583
+ source: backlinkSourceSchema
2584
+ });
2585
+ var backlinkSourceAvailabilityDtoSchema = z22.object({
2586
+ source: backlinkSourceSchema,
2587
+ /**
2588
+ * The source is set up for this project:
2589
+ * - commoncrawl: `autoExtractBacklinks` enabled AND a `ready` release sync exists.
2590
+ * - bing-webmaster: a Bing Webmaster connection exists for the project domain.
2591
+ */
2592
+ connected: z22.boolean(),
2593
+ /** Backlink rows exist for this project + source. */
2594
+ hasData: z22.boolean(),
2595
+ /** Latest window id with data for this source, null when none. */
2596
+ latestRelease: z22.string().nullable(),
2597
+ /**
2598
+ * Linking-domain count in the latest window. Excludes crawler/proxy hosts only
2599
+ * when the request sets `?excludeCrawlers=1` (default off, matching the summary
2600
+ * and domains endpoints); the dashboard passes it so the switcher pill matches
2601
+ * the metric card.
2602
+ */
2603
+ totalLinkingDomains: z22.number().int(),
2604
+ /** Freshness: `queriedAt` of the latest summary for this source, null when none. */
2605
+ lastSyncedAt: z22.string().nullable()
2606
+ });
2607
+ var backlinkSourcesResponseSchema = z22.object({
2608
+ projectId: z22.string(),
2609
+ targetDomain: z22.string(),
2610
+ /** Availability for every known source, in a stable order. */
2611
+ sources: z22.array(backlinkSourceAvailabilityDtoSchema),
2612
+ anyConnected: z22.boolean(),
2613
+ anyData: z22.boolean()
2555
2614
  });
2556
2615
  var backlinksInstallStatusDtoSchema = z22.object({
2557
2616
  duckdbInstalled: z22.boolean(),
@@ -3022,6 +3081,17 @@ function absolutizeProjectUrl(url, canonicalDomain) {
3022
3081
  if (trimmed.startsWith("/")) return `https://${host}${trimmed}`;
3023
3082
  return `https://${host}/${trimmed}`;
3024
3083
  }
3084
+ function hostOf(value) {
3085
+ if (value == null) return null;
3086
+ const trimmed = value.trim();
3087
+ if (!trimmed) return null;
3088
+ try {
3089
+ const url = trimmed.includes("://") ? new URL(trimmed) : new URL(`https://${trimmed}`);
3090
+ return url.hostname.replace(/^www\./, "").toLowerCase();
3091
+ } catch {
3092
+ return null;
3093
+ }
3094
+ }
3025
3095
  function normalizeUrlPath(input) {
3026
3096
  if (input == null) return null;
3027
3097
  let trimmed = input.trim();
@@ -4257,6 +4327,129 @@ function formatDeltaCopy(d, suffix, windowLabel = "vs prior 7 days") {
4257
4327
  return `Flat ${windowLabel} (${formatNumber(d.prior)} ${suffix})`;
4258
4328
  }
4259
4329
 
4330
+ // ../contracts/src/ads.ts
4331
+ import { z as z30 } from "zod";
4332
+ var adsConnectRequestSchema = z30.object({
4333
+ /** Ads Manager "SDK key" scoped to one ad account. Stored in config.yaml, never the DB. */
4334
+ apiKey: z30.string().min(1)
4335
+ });
4336
+ var adsConnectionStatusDtoSchema = z30.object({
4337
+ connected: z30.boolean(),
4338
+ adAccountId: z30.string().nullable().optional(),
4339
+ displayName: z30.string().nullable().optional(),
4340
+ currencyCode: z30.string().nullable().optional(),
4341
+ timezone: z30.string().nullable().optional(),
4342
+ status: z30.string().nullable().optional(),
4343
+ lastSyncedAt: z30.string().nullable().optional()
4344
+ });
4345
+ var adsDisconnectResponseSchema = z30.object({
4346
+ disconnected: z30.boolean()
4347
+ });
4348
+ var adsSyncResponseSchema = z30.object({
4349
+ runId: z30.string(),
4350
+ status: z30.string()
4351
+ });
4352
+ var adsCreativeDtoSchema = z30.object({
4353
+ type: z30.string().nullable().optional(),
4354
+ title: z30.string().nullable().optional(),
4355
+ body: z30.string().nullable().optional(),
4356
+ targetUrl: z30.string().nullable().optional()
4357
+ });
4358
+ var adsAdDtoSchema = z30.object({
4359
+ id: z30.string(),
4360
+ adGroupId: z30.string(),
4361
+ name: z30.string(),
4362
+ status: z30.string(),
4363
+ reviewStatus: z30.string().nullable().optional(),
4364
+ creative: adsCreativeDtoSchema.nullable().optional()
4365
+ });
4366
+ var adsAdGroupDtoSchema = z30.object({
4367
+ id: z30.string(),
4368
+ campaignId: z30.string(),
4369
+ name: z30.string(),
4370
+ status: z30.string(),
4371
+ billingEventType: z30.string().nullable().optional(),
4372
+ maxBidMicros: z30.number().int().nullable().optional(),
4373
+ /**
4374
+ * The targeting primitive: entries are multi-line strings of
4375
+ * newline-separated example queries (the live Ads Manager format).
4376
+ */
4377
+ contextHints: z30.array(z30.string()).default([]),
4378
+ ads: z30.array(adsAdDtoSchema).default([])
4379
+ });
4380
+ var adsCampaignDtoSchema = z30.object({
4381
+ id: z30.string(),
4382
+ name: z30.string(),
4383
+ status: z30.string(),
4384
+ biddingType: z30.string().nullable().optional(),
4385
+ dailySpendLimitMicros: z30.number().int().nullable().optional(),
4386
+ lifetimeSpendLimitMicros: z30.number().int().nullable().optional(),
4387
+ adGroups: z30.array(adsAdGroupDtoSchema).default([])
4388
+ });
4389
+ var adsCampaignListResponseSchema = z30.object({
4390
+ campaigns: z30.array(adsCampaignDtoSchema)
4391
+ });
4392
+ var adsInsightLevelSchema = z30.enum(["campaign", "ad_group"]);
4393
+ var AdsInsightLevels = adsInsightLevelSchema.enum;
4394
+ var adsInsightRowDtoSchema = z30.object({
4395
+ level: adsInsightLevelSchema,
4396
+ entityId: z30.string(),
4397
+ date: z30.string(),
4398
+ impressions: z30.number().int(),
4399
+ clicks: z30.number().int(),
4400
+ spendMicros: z30.number().int(),
4401
+ /** clicks / impressions; null when impressions is 0. */
4402
+ ctr: z30.number().nullable(),
4403
+ /** spendMicros / clicks, rounded to integer micros; null when clicks is 0. */
4404
+ cpcMicros: z30.number().int().nullable()
4405
+ });
4406
+ var adsInsightsResponseSchema = z30.object({
4407
+ rows: z30.array(adsInsightRowDtoSchema),
4408
+ /** Account currency for rendering spend/cpc; null before the first sync. */
4409
+ currencyCode: z30.string().nullable().optional()
4410
+ });
4411
+ var adsTotalsDtoSchema = z30.object({
4412
+ impressions: z30.number().int(),
4413
+ clicks: z30.number().int(),
4414
+ spendMicros: z30.number().int(),
4415
+ ctr: z30.number().nullable(),
4416
+ cpcMicros: z30.number().int().nullable()
4417
+ });
4418
+ var adsSummaryDtoSchema = z30.object({
4419
+ connected: z30.boolean(),
4420
+ displayName: z30.string().nullable().optional(),
4421
+ currencyCode: z30.string().nullable().optional(),
4422
+ lastSyncedAt: z30.string().nullable().optional(),
4423
+ campaignCount: z30.number().int(),
4424
+ adGroupCount: z30.number().int(),
4425
+ adCount: z30.number().int(),
4426
+ /** Date range the totals cover (oldest/newest rollup date), null when empty. */
4427
+ window: z30.object({
4428
+ from: z30.string().nullable(),
4429
+ to: z30.string().nullable()
4430
+ }),
4431
+ /** Campaign-level rollup totals over the window (levels are not summed across). */
4432
+ totals: adsTotalsDtoSchema
4433
+ });
4434
+ function adsCtr(clicks, impressions) {
4435
+ return impressions > 0 ? clicks / impressions : null;
4436
+ }
4437
+ function adsCpcMicros(spendMicros, clicks) {
4438
+ return clicks > 0 ? Math.round(spendMicros / clicks) : null;
4439
+ }
4440
+
4441
+ // ../contracts/src/money.ts
4442
+ var MICROS_PER_UNIT = 1e6;
4443
+ function dollarsToMicros(dollars) {
4444
+ return Math.round(dollars * MICROS_PER_UNIT);
4445
+ }
4446
+ function formatMicros(micros, currencyCode = "USD") {
4447
+ return new Intl.NumberFormat("en-US", {
4448
+ style: "currency",
4449
+ currency: currencyCode
4450
+ }).format(micros / MICROS_PER_UNIT);
4451
+ }
4452
+
4260
4453
  // ../contracts/src/ai-engines.ts
4261
4454
  var AI_ENGINE_DOMAINS = {
4262
4455
  /** OpenAI marketing/blog/API surface. */
@@ -4486,11 +4679,15 @@ export {
4486
4679
  AGENT_MEMORY_KEY_MAX_LENGTH,
4487
4680
  agentMemoryUpsertRequestSchema,
4488
4681
  agentMemoryDeleteRequestSchema,
4682
+ backlinkSourceSchema,
4683
+ BacklinkSources,
4684
+ computeBacklinkSummaryMetrics,
4489
4685
  CcReleaseSyncStatuses,
4490
4686
  ccReleaseSyncDtoSchema,
4491
4687
  backlinkSummaryDtoSchema,
4492
4688
  backlinkListResponseSchema,
4493
4689
  backlinkHistoryEntrySchema,
4690
+ backlinkSourcesResponseSchema,
4494
4691
  backlinksInstallStatusDtoSchema,
4495
4692
  backlinksInstallResultDtoSchema,
4496
4693
  ccAvailableReleaseSchema,
@@ -4519,6 +4716,7 @@ export {
4519
4716
  doctorReportSchema,
4520
4717
  summarizeCheckResults,
4521
4718
  absolutizeProjectUrl,
4719
+ hostOf,
4522
4720
  normalizeUrlPath,
4523
4721
  citationVisibilityResponseSchema,
4524
4722
  emptyCitationVisibility,
@@ -4566,6 +4764,18 @@ export {
4566
4764
  deltaPercent,
4567
4765
  deltaTone,
4568
4766
  formatDeltaCopy,
4767
+ adsConnectRequestSchema,
4768
+ adsConnectionStatusDtoSchema,
4769
+ adsDisconnectResponseSchema,
4770
+ adsSyncResponseSchema,
4771
+ adsCampaignListResponseSchema,
4772
+ adsInsightLevelSchema,
4773
+ adsInsightsResponseSchema,
4774
+ adsSummaryDtoSchema,
4775
+ adsCtr,
4776
+ adsCpcMicros,
4777
+ dollarsToMicros,
4778
+ formatMicros,
4569
4779
  AI_ENGINE_DOMAINS,
4570
4780
  AI_ENGINE_SELF_DOMAINS,
4571
4781
  VERTEX_AI_SEARCH_PROXY_DOMAIN,