@canonry/canonry 7.90.2 → 7.91.1

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 (136) hide show
  1. package/README.md +182 -125
  2. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +4 -0
  3. package/assets/assets/{AgentSetupPage-FfxjZWI8.js → AgentSetupPage-DpBlChWi.js} +1 -1
  4. package/assets/assets/{AuditHistoryPanel-YrEggsyA.js → AuditHistoryPanel-DAotR_zD.js} +1 -1
  5. package/assets/assets/{BacklinksPage-vXUxxplH.js → BacklinksPage-CFoi9mrt.js} +1 -1
  6. package/assets/assets/{HistoryPage-VJ_JQ7R3.js → HistoryPage-DIGmY4wi.js} +1 -1
  7. package/assets/assets/{MeasurementPropertyPage-DVm7CHU8.js → MeasurementPropertyPage-C6o9U2R6.js} +1 -1
  8. package/assets/assets/{ProjectPage-D6mHRRSs.js → ProjectPage-00Vsnxv4.js} +1 -1
  9. package/assets/assets/{RunsPage-DwtYzU9w.js → RunsPage-ChSbIrF9.js} +1 -1
  10. package/assets/assets/{SettingsPage-DsmgLFb7.js → SettingsPage-CIrzXeP8.js} +1 -1
  11. package/assets/assets/{SiteHealthSection-CFu6JBTb.js → SiteHealthSection-HZXdeAOx.js} +2 -2
  12. package/assets/assets/ToneBadge-ht0n6RUH.js +60 -0
  13. package/assets/assets/{TrafficPage-DGPSaWTl.js → TrafficPage-ZYsFSEuq.js} +1 -1
  14. package/assets/assets/{TrafficSourceDetailPage-C79eQilP.js → TrafficSourceDetailPage-D5uJZpZ4.js} +1 -1
  15. package/assets/assets/{account-context-GstYbqpX.js → account-context-DMLbDVNZ.js} +1 -1
  16. package/assets/assets/{agent-trust-copy-Ca4ys8J2.js → agent-trust-copy-DbpAXPxc.js} +1 -1
  17. package/assets/assets/{card-CgdGerYK.js → card-Be_eN_-Q.js} +1 -1
  18. package/assets/assets/{format-helpers-ByAY4dv0.js → format-helpers-IKCsn-Iw.js} +1 -1
  19. package/assets/assets/{index-D1ox2bx5.js → index-3jdPlyTf.js} +4 -4
  20. package/assets/assets/{query-client-DjRrRIsq.js → query-client-CoKNm49W.js} +1 -1
  21. package/assets/assets/{react-query-B8JD_K7g.js → react-query-DIdeX4Dn.js} +1 -1
  22. package/assets/assets/{react-sigma_core.esm.min-BGXzc_-G.js → react-sigma_core.esm.min-De3OcL3T.js} +1 -1
  23. package/assets/assets/{server-traffic-Bt9bexaP.js → server-traffic-DvN44H1n.js} +1 -1
  24. package/assets/assets/{service-SgtD5EOS.js → service-RpR2yqfP.js} +1 -1
  25. package/assets/index.html +7 -7
  26. package/dist/{ads-6BAITIBY.js → ads-QS4QEBHX.js} +3 -3
  27. package/dist/{agent-BE3D65U4.js → agent-TTGLGLPD.js} +7 -7
  28. package/dist/{agent-ask-TT6BAV2T.js → agent-ask-EBYJSSGR.js} +3 -3
  29. package/dist/{agent-conversations-GJDAWU36.js → agent-conversations-LPBMYN4F.js} +3 -3
  30. package/dist/{agent-journeys-UEL27GNY.js → agent-journeys-EL27YZKQ.js} +3 -3
  31. package/dist/{agent-memory-PB74OP46.js → agent-memory-YNWTLZGS.js} +3 -3
  32. package/dist/{agent-providers-5WFMNRRK.js → agent-providers-T7HJEQYM.js} +3 -3
  33. package/dist/{agent-transcript-IAGB3JXG.js → agent-transcript-EY6KUYYG.js} +3 -3
  34. package/dist/{analytics-5BVE7KUN.js → analytics-NTO6DGQ2.js} +3 -3
  35. package/dist/{apply-YJJWQ4JX.js → apply-UGT7DZHG.js} +3 -3
  36. package/dist/{backfill-6OLGZIEV.js → backfill-KDTC6JEP.js} +10 -10
  37. package/dist/{backlinks-4JK26OIO.js → backlinks-JRG57JRJ.js} +3 -3
  38. package/dist/{bing-NZYYPB2M.js → bing-ZV6QDFTJ.js} +3 -3
  39. package/dist/{bootstrap-6KHK63W3.js → bootstrap-GJHNMV5P.js} +10 -10
  40. package/dist/{cdp-PTXWVX6T.js → cdp-ZN47QU5O.js} +7 -7
  41. package/dist/{chunk-N7LRPMWR.js → chunk-2N7BYEZO.js} +2 -2
  42. package/dist/{chunk-ZV6PMFF4.js → chunk-3HNHOS6O.js} +1 -1
  43. package/dist/{chunk-HUQDD26S.js → chunk-3MIZP4IB.js} +7 -7
  44. package/dist/{chunk-SDXMG4VX.js → chunk-42OW42AR.js} +33 -7
  45. package/dist/{chunk-WYMBWCG5.js → chunk-4FFIIZB5.js} +1 -1
  46. package/dist/{chunk-AFYKUX2X.js → chunk-56EXMS52.js} +2 -2
  47. package/dist/{chunk-DZO6CATO.js → chunk-6DP2YPZY.js} +4 -4
  48. package/dist/{chunk-NKGQCCUS.js → chunk-6PNFPMLD.js} +6 -6
  49. package/dist/{chunk-7VJJXLRP.js → chunk-7KHSKOV6.js} +3 -3
  50. package/dist/{chunk-TA4L7U2M.js → chunk-AXWUCOYT.js} +1 -1
  51. package/dist/{chunk-USHY3SH6.js → chunk-C3BSCGUH.js} +15 -1
  52. package/dist/{chunk-K34Y6HQZ.js → chunk-DXXHZHPE.js} +390 -362
  53. package/dist/{chunk-QWNDNZJ3.js → chunk-EOIH5XHT.js} +2 -2
  54. package/dist/{chunk-DF4B6OGX.js → chunk-FAXYDTV5.js} +4 -4
  55. package/dist/{chunk-WNDBACO7.js → chunk-IHMODTQ7.js} +4 -4
  56. package/dist/{chunk-7ASKZHS4.js → chunk-IQ5RKJKQ.js} +1 -1
  57. package/dist/{chunk-PID3C4B7.js → chunk-L5U7VT4A.js} +1 -1
  58. package/dist/{chunk-UG7TZJIG.js → chunk-LSIE5MHG.js} +1 -1
  59. package/dist/{chunk-MGBUQHQZ.js → chunk-M322ZEOQ.js} +4 -4
  60. package/dist/{chunk-NPX5SL54.js → chunk-NAPFULBT.js} +1 -1
  61. package/dist/{chunk-YB2GAK32.js → chunk-NL2WM6OB.js} +5 -5
  62. package/dist/{chunk-2UULABPI.js → chunk-OI6YQMSB.js} +3 -3
  63. package/dist/{chunk-GKX5F7NX.js → chunk-P4MNYMJJ.js} +1 -1
  64. package/dist/{chunk-I7DAEE3L.js → chunk-PAXP5KZC.js} +5 -5
  65. package/dist/{chunk-ETIIPSWL.js → chunk-PQNT2JUO.js} +25 -0
  66. package/dist/{chunk-LTZNUDSI.js → chunk-QUS7755A.js} +3 -3
  67. package/dist/{chunk-XCEA5OMI.js → chunk-SYOQULRL.js} +2 -2
  68. package/dist/{chunk-Q6EZEVXE.js → chunk-UQVUDSAV.js} +1 -1
  69. package/dist/{chunk-K2B64IEQ.js → chunk-VFQMRUGN.js} +30 -23
  70. package/dist/{chunk-L2MDMZFT.js → chunk-W6H7WDHW.js} +3 -3
  71. package/dist/{chunk-LQWMNDZR.js → chunk-WOEGZKXQ.js} +1 -1
  72. package/dist/{chunk-F6WNKKGG.js → chunk-WTKGSKCS.js} +2 -2
  73. package/dist/{chunk-QLVTMPFW.js → chunk-YCRSIOIN.js} +2 -2
  74. package/dist/{chunk-F442MSX2.js → chunk-YFOTI7LJ.js} +2 -2
  75. package/dist/{chunk-OOVWMWMT.js → chunk-YVY23HNW.js} +1 -1
  76. package/dist/{citations-WX4VO6ZI.js → citations-6723MJLH.js} +3 -3
  77. package/dist/{cli-catalog-BTRNR2UP.js → cli-catalog-RVBNEPWA.js} +3 -3
  78. package/dist/{cli-commands-TJBEUBBX.js → cli-commands-6ZOUHE3V.js} +7 -7
  79. package/dist/cli.js +13 -13
  80. package/dist/{competitor-HZRZWCEN.js → competitor-ENYN5RAD.js} +3 -3
  81. package/dist/{content-NOZDHI3M.js → content-TT3PF4CR.js} +3 -3
  82. package/dist/{dashboard-TZLWHXP4.js → dashboard-JLCJDU2V.js} +6 -6
  83. package/dist/{demo-2PYBQD47.js → demo-KMVR3ZWW.js} +1 -1
  84. package/dist/{demo-server-JEQNGMMV.js → demo-server-QXI7BGMM.js} +12 -12
  85. package/dist/{discover-A2XFXHOC.js → discover-HATQTTDG.js} +3 -3
  86. package/dist/{discover-eval-A6SBUYXF.js → discover-eval-64TWPB5Z.js} +3 -3
  87. package/dist/{doctor-GZACELPF.js → doctor-6C5TXOG3.js} +3 -3
  88. package/dist/{evidence-RVFMUWIL.js → evidence-K43NRSOT.js} +3 -3
  89. package/dist/{export-cmd-X2TFZGNJ.js → export-cmd-KU64GUQ5.js} +3 -3
  90. package/dist/{ga-TOT3V67A.js → ga-KXKLUQBQ.js} +3 -3
  91. package/dist/{gbp-KP4YZU6S.js → gbp-5CZTROVP.js} +3 -3
  92. package/dist/{google-GCFSTV5N.js → google-6TNU7SVF.js} +4 -4
  93. package/dist/{google-marketing-ANS65KQN.js → google-marketing-UCUIJBQG.js} +1 -1
  94. package/dist/{health-cmd-UKMNLU6D.js → health-cmd-ZFJ7ZOWI.js} +3 -3
  95. package/dist/{history-TWGYSAZZ.js → history-V664SORR.js} +3 -3
  96. package/dist/index.d.ts +59 -32
  97. package/dist/index.js +25 -25
  98. package/dist/{init-3GSNOIUH.js → init-TAIUP5GV.js} +12 -12
  99. package/dist/{insights-7IOC7WIR.js → insights-JFW7HBPR.js} +3 -3
  100. package/dist/intelligence-service-RRKHXZ2N.js +16 -0
  101. package/dist/{journeys-NWKT45PT.js → journeys-QTZXBKBT.js} +3 -3
  102. package/dist/{keys-ZZYZUHUK.js → keys-KGI5S2IE.js} +3 -3
  103. package/dist/{keyword-F6EIX5KM.js → keyword-XU6Y4XVH.js} +3 -3
  104. package/dist/{logs-B45FXS6E.js → logs-E2DQXWQQ.js} +3 -3
  105. package/dist/{mcp-WPNCNWA5.js → mcp-PQE6WFED.js} +4 -4
  106. package/dist/mcp.js +9 -9
  107. package/dist/{measurement-plan-QT5HJ3KC.js → measurement-plan-NBGLF7BI.js} +5 -5
  108. package/dist/{notify-Z3LIF3SO.js → notify-SHEAQNUJ.js} +3 -3
  109. package/dist/{organic-evidence-R5TM7XUB.js → organic-evidence-LMXCFABG.js} +3 -3
  110. package/dist/{overview-6TZHJLQN.js → overview-MXEBDUCA.js} +4 -4
  111. package/dist/{project-VMPVOQ2A.js → project-MWZGTK4S.js} +3 -3
  112. package/dist/{query-EXSH5QP7.js → query-EZQY45JT.js} +3 -3
  113. package/dist/{research-6R6BEL4X.js → research-PSPDLEQL.js} +3 -3
  114. package/dist/{results-export-ZJRL3ECU.js → results-export-7FLSFC7J.js} +3 -3
  115. package/dist/{run-C4VXAJE3.js → run-MASOXS63.js} +4 -4
  116. package/dist/{schedule-KC6ETWMO.js → schedule-V32G7TBR.js} +21 -5
  117. package/dist/{search-SUR6BEQD.js → search-R2S5CAVK.js} +3 -3
  118. package/dist/{serve-SEHH35PJ.js → serve-PGPQOVGC.js} +26 -26
  119. package/dist/{session-D42KUKCZ.js → session-MNEWM2CT.js} +13 -13
  120. package/dist/{settings-LTCG3D5B.js → settings-KBY4766O.js} +3 -3
  121. package/dist/{sitemaps-YMTZLF5S.js → sitemaps-FLMA6DMY.js} +3 -3
  122. package/dist/{skills-THMG3L64.js → skills-WAHOLVYH.js} +2 -2
  123. package/dist/{snapshot-ZEVB3EE4.js → snapshot-VJYM7UX2.js} +3 -3
  124. package/dist/{src-HPVSTXVF.js → src-XVBKS6UX.js} +2 -2
  125. package/dist/{status-LPCPTWKO.js → status-YFAUBVEA.js} +4 -4
  126. package/dist/{technical-aeo-J6LNT45B.js → technical-aeo-656NEU3A.js} +3 -3
  127. package/dist/{telemetry-BPY34GDV.js → telemetry-CJ7ENZDY.js} +4 -4
  128. package/dist/{tools-K3G43ESI.js → tools-4STRCC7Q.js} +11 -11
  129. package/dist/{traffic-BOIFAPAR.js → traffic-NN6ECPRS.js} +5 -5
  130. package/dist/{trends-A2MDRSQQ.js → trends-HXZVTQ3I.js} +3 -3
  131. package/dist/{users-VJOQODJK.js → users-PELDCAXQ.js} +3 -3
  132. package/dist/{visibility-stats-YNMJYASA.js → visibility-stats-W3AELURT.js} +3 -3
  133. package/dist/{wordpress-BWG6D7VH.js → wordpress-TAVEI2XQ.js} +3 -3
  134. package/package.json +1 -1
  135. package/assets/assets/ToneBadge-B3rD6bDF.js +0 -60
  136. package/dist/intelligence-service-42Q3OVI5.js +0 -16
package/README.md CHANGED
@@ -1,15 +1,94 @@
1
- # Canonry <img src="https://raw.githubusercontent.com/Canonry/canonry/main/apps/web/public/favicon-32.png" alt="Canonry canary icon" width="24" />
1
+ <h1 align="center">
2
+ <a href="https://canonry.ai"><img src="https://raw.githubusercontent.com/Canonry/canonry/main/apps/web/public/apple-touch-icon.png" alt="Canonry" width="64" valign="middle" /></a> Canonry
3
+ </h1>
2
4
 
3
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)
4
- [![npm version](https://img.shields.io/npm/v/@canonry/canonry)](https://www.npmjs.com/package/@canonry/canonry)
5
- [![Discord](https://img.shields.io/badge/Discord-Join-5865F2?logo=discord&logoColor=white)](https://discord.gg/8K9yY7GywJ)
6
- [![Twitter](https://img.shields.io/badge/Twitter-Follow-000000?logo=x&logoColor=white)](https://x.com/canonry_ai)
5
+ <p align="center">
6
+ <a href="https://www.npmjs.com/package/@canonry/canonry"><img src="https://img.shields.io/npm/v/@canonry/canonry?style=flat&amp;label=npm&amp;color=D93F45" alt="npm version" /></a>
7
+ <a href="https://www.npmjs.com/package/@canonry/canonry"><img src="https://img.shields.io/npm/dt/@canonry/canonry?style=flat&amp;label=downloads&amp;color=D93F45" alt="Total npm downloads" /></a>
8
+ <a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-D93F45?style=flat" alt="License: MIT" /></a>
9
+ <a href="https://discord.gg/8K9yY7GywJ"><img src="https://img.shields.io/badge/Discord-5865F2?logo=discord&amp;logoColor=white" alt="Join the Canonry Discord" /></a>
10
+ <a href="https://x.com/canonry_ai"><img src="https://img.shields.io/badge/X-000000?logo=x&amp;logoColor=white" alt="Follow Canonry on X" /></a>
11
+ <img src="https://img.shields.io/badge/API%20%7C%20CLI%20%7C%20MCP%20%7C%20Web-4493F8?style=flat-square" alt="Interfaces: REST API, CLI, MCP, and web dashboard" />
12
+ </p>
13
+
14
+ <p align="center">
15
+ <strong>The agent-first AEO operating platform.</strong><br/>
16
+ Track mentions and citations across ChatGPT, Claude, Gemini, Meta Muse, and more. Join them with Search Console, GA4, ads, AI crawler traffic, site audits, and SEO data. Then let your agent create content, publish fixes, manage ChatGPT ads, get pages indexed, and measure what changed.<br/>
17
+ <sub>Open source · Self-hosted · Bring your own keys</sub>
18
+ </p>
19
+
20
+ <h3 align="center"><a href="https://custom-demo.canonry.ai/projects/summit-roofing"><ins>Try the live demo</ins></a> &nbsp;·&nbsp; <a href="#quick-start"><ins>Quick start</ins></a></h3>
21
+
22
+ <p align="center">
23
+ <a href="https://custom-demo.canonry.ai/projects/summit-roofing"><picture><source media="(max-width: 600px)" srcset="https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/readme/hero-mobile.png" /><img src="https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/readme/hero.png" alt="Canonry dashboard: AI answers over time, with the share of non-brand answers that mention the brand for Claude, Gemini, and OpenAI, with sample data" width="100%" /></picture></a>
24
+ </p>
25
+
26
+ <a id="explore-the-evidence"></a>
27
+ <a id="add-ai-visibility-when-you-need-it"></a>
28
+
29
+ ## Features
30
+
31
+ ### Built for Agents
32
+
33
+ Everything runs on one REST API. The dashboard, CLI, MCP server, and plugins all call it, so your agent can read and do everything you see.
34
+
35
+ - **REST API:** every dashboard number has a typed endpoint under `/api/v1`. The full OpenAPI spec is at `GET /api/v1/openapi.json`. [API →](#self-hosting-and-api)
36
+ - **CLI:** the same calls as `cnry <command> --format json`, for scripts and shell agents. [CLI →](skills/canonry/references/canonry-cli.md)
37
+ - **MCP and plugins:** plugins for Claude Code and Codex, or a hosted MCP server for ChatGPT and Claude Desktop. [MCP →](docs/mcp.md) · [Plugin →](docs/plugins.md)
38
+
39
+ ### AI Visibility
40
+
41
+ Track brand mentions and citations by query and answer engine. See where competitors win, then open the exact answer and sources behind each result. [Docs →](docs/query-visibility.md)
42
+
43
+ <a href="https://custom-demo.canonry.ai/projects/summit-roofing"><picture><source media="(max-width: 600px)" srcset="https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/readme/ai-visibility-mobile.png" /><img src="https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/readme/ai-visibility.png" alt="Where competitors are winning: mention share against two competitors, mention and citation gaps, and mentioned and cited counts by engine, with sample data" width="100%" /></picture></a>
44
+
45
+ ### Server-Side AI Traffic
46
+
47
+ See what AI agents actually do on your site, from your server logs: which pages GPTBot, ClaudeBot, Meta AI, and Grok crawl, which pages ChatGPT, Claude, and Meta AI open for users, and who clicks through. [Docs →](skills/canonry/references/server-side-traffic.md)
48
+
49
+ <a href="skills/canonry/references/server-side-traffic.md"><picture><source media="(max-width: 600px)" srcset="https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/readme/traffic-mobile.png" /><img src="https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/readme/traffic.png" alt="Server traffic for the last 24 hours: 105 AI crawler visits, 35 AI user fetches, and 46 AI referral sessions, with an hourly chart, with sample data" width="100%" /></picture></a>
50
+
51
+ ### Site Health
52
+
53
+ Crawl your site, score each page for AI readiness, and map the internal links between pages. Each finding lists the pages it affects. [Docs →](skills/canonry/references/canonry-cli.md#site-health-technical-aeo-compatibility)
54
+
55
+ <a href="skills/canonry/references/canonry-cli.md#site-health-technical-aeo-compatibility"><picture><source media="(max-width: 600px)" srcset="https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/readme/site-health-mobile.png" /><img src="https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/readme/site-health.png" alt="Site map of 205 crawled pages and the links between them, beside a list of site sections, with sample data" width="100%" /></picture></a>
56
+
57
+ <a id="seo-data"></a>
58
+
59
+ ### SEO Research
60
+
61
+ Keyword volume, difficulty, organic rankings, and search gaps against competitors, powered by DataForSEO Labs. [Docs →](docs/seo-data.md)
62
+
63
+ <a href="docs/seo-data.md"><picture><source media="(max-width: 600px)" srcset="https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/readme/seo-mobile.png" /><img src="https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/readme/seo.png" alt="SEO page with ranked searches, top-10 count, estimated organic traffic, and a table of positions, volume, and difficulty, with sample data" width="100%" /></picture></a>
64
+
65
+ ### Ads
66
+
67
+ Let your agent run ChatGPT Ads: sync campaigns and daily results, create and edit campaigns, and switch them on only after you approve. Track Google Ads spend and conversions, and check each conversion's setup across Google Ads and Tag Manager. Paid results stay separate from organic mentions and citations. [ChatGPT Ads →](skills/canonry/references/canonry-cli.md#openai-ads-chatgpt-ads) · [Google Ads →](docs/google-marketing.md)
7
68
 
8
- Canonry is an **agent-first (CLI, MCP, API), open-source AEO operating platform with a comprehensive web UI.** Track AI visibility over time, investigate changes across search and traffic, and measure progress after you or your agent acts.
69
+ <a href="docs/google-marketing.md"><picture><source media="(max-width: 600px)" srcset="https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/readme/ads-mobile.png" /><img src="https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/readme/ads.png" alt="Google Ads performance: spend, conversions, cost per conversion, a daily chart, and campaign results, with sample data" width="100%" /></picture></a>
9
70
 
10
- **Self-hosted, using SQLite, with your own (BYOK) provider keys.** The web UI and your agent see the same evidence.
71
+ <a id="compare-competitors-and-measure-complex-portfolios"></a>
72
+ <a id="act-on-the-evidence"></a>
73
+
74
+ **Also in the box:**
75
+
76
+ - **[Aero](skills/aero/SKILL.md)** — A built-in analyst in the dashboard that reads your evidence and explains what changed.
77
+ - **[Portfolios](skills/canonry/references/canonry-cli.md#target-measurement-plans)** — Versioned measurement plans for locations, products, or site sections. Branded and non-brand queries stay separate.
78
+ - **[Competitor landscape](docs/competitor-landscape.md)** — Mention share, mentions, and citations for you and your competitors over time.
79
+ - **[Query discovery and research](skills/canonry/references/canonry-cli.md#discovery-tracked-basket-expansion)** — Find new queries to track, or run one-off research without changing what you track.
80
+ - **[Content engine](skills/canonry/references/canonry-cli.md#content-target-dismissals)** — Finds content gaps and the pages to write or improve next, so your agent can draft them and publish to WordPress for review.
81
+ - **[Publish and index](skills/canonry/references/indexing.md)** — WordPress drafts, JSON-LD, and sitemap and URL submissions to Google and Bing.
82
+ - **[Schedules and webhooks](skills/canonry/references/canonry-cli.md#scheduling--notifications)** — Rerun checks on a schedule and send alerts to Discord, Slack, or your systems.
83
+ - **[Backlinks](skills/canonry/references/canonry-cli.md#backlinks-common-crawl)** — Common Crawl link data, queried locally with DuckDB.
84
+ - **[Sentiment](docs/sentiment.md)** — Experimental, off by default. Rates branded answers as favorable, mixed, or unfavorable.
85
+ - **And more** — browse the [documentation index](docs/README.md).
86
+
87
+ **An example job for your connected agent:**
88
+
89
+ > Find where competitors get cited and we don't. Use search data and the site audit to prepare a pull request or WordPress draft for review. After it ships, rerun checks and report what changed.
11
90
 
12
- [Live UI Demo](https://custom-demo.canonry.ai/projects/summit-roofing) · [Quick start](#quick-start) · [Evidence](#explore-the-evidence) · [Actions](#act-on-the-evidence) · [Integrations](#integrations) · [Docs](#documentation) · [Support](#support)
91
+ ## How it works
13
92
 
14
93
  <p align="center">
15
94
  <a href="https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/measure-act.svg">
@@ -17,11 +96,65 @@ Canonry is an **agent-first (CLI, MCP, API), open-source AEO operating platform
17
96
  </a>
18
97
  </p>
19
98
 
99
+ ---
100
+
101
+ ## Supported Engines
102
+
103
+ Compare direct AI engines, OpenRouter models, and search answers from Exa and Parallel. Each keeps its own results and history.
104
+
105
+ <p>
106
+ <a href="https://platform.openai.com/api-keys"><kbd><img src="https://www.google.com/s2/favicons?domain=openai.com&amp;sz=64" alt="OpenAI logo" width="16" valign="middle" /> ChatGPT</kbd></a> &nbsp;
107
+ <a href="https://console.anthropic.com/settings/keys"><kbd><img src="https://www.google.com/s2/favicons?domain=claude.ai&amp;sz=64" alt="Claude logo" width="16" valign="middle" /> Claude</kbd></a> &nbsp;
108
+ <a href="https://aistudio.google.com/apikey"><kbd><img src="https://www.google.com/s2/favicons?domain=gemini.google.com&amp;sz=64" alt="Gemini logo" width="16" valign="middle" /> Gemini</kbd></a> &nbsp;
109
+ <a href="https://www.perplexity.ai/settings/api"><kbd><img src="https://www.google.com/s2/favicons?domain=perplexity.ai&amp;sz=64" alt="Perplexity logo" width="16" valign="middle" /> Perplexity</kbd></a> &nbsp;
110
+ <a href="https://dev.meta.ai/"><kbd><img src="https://www.google.com/s2/favicons?domain=meta.ai&amp;sz=64" alt="Meta logo" width="16" valign="middle" /> Meta (Muse)</kbd></a> &nbsp;
111
+ <a href="docs/providers/openrouter.md"><kbd><img src="https://www.google.com/s2/favicons?domain=openrouter.ai&amp;sz=64" alt="OpenRouter logo" width="16" valign="middle" /> OpenRouter</kbd></a> &nbsp;
112
+ <a href="docs/providers/answer-services/exa.md"><kbd><img src="https://www.google.com/s2/favicons?domain=exa.ai&amp;sz=64" alt="Exa logo" width="16" valign="middle" /> Exa</kbd></a> &nbsp;
113
+ <a href="docs/providers/answer-services/parallel.md"><kbd><img src="https://www.google.com/s2/favicons?domain=parallel.ai&amp;sz=64" alt="Parallel logo" width="16" valign="middle" /> Parallel</kbd></a> &nbsp;
114
+ <kbd>+ any OpenAI-compatible local model</kbd>
115
+ </p>
116
+
117
+ **Works with your agent:**
118
+
119
+ <p>
120
+ <a href="docs/plugins.md#install-for-claude-code"><kbd><img src="https://www.google.com/s2/favicons?domain=claude.ai&amp;sz=64" alt="Claude Code logo" width="16" valign="middle" /> Claude Code</kbd></a> &nbsp;
121
+ <a href="docs/plugins.md#install-for-codex"><kbd><img src="https://www.google.com/s2/favicons?domain=openai.com&amp;sz=64" alt="Codex logo" width="16" valign="middle" /> Codex</kbd></a> &nbsp;
122
+ <a href="docs/mcp.md"><kbd><img src="https://www.google.com/s2/favicons?domain=chatgpt.com&amp;sz=64" alt="ChatGPT logo" width="16" valign="middle" /> ChatGPT</kbd></a> &nbsp;
123
+ <a href="docs/mcp.md"><kbd><img src="https://www.google.com/s2/favicons?domain=claude.ai&amp;sz=64" alt="Claude Desktop logo" width="16" valign="middle" /> Claude Desktop</kbd></a> &nbsp;
124
+ <kbd>+ any MCP client or shell-capable agent</kbd>
125
+ </p>
126
+
127
+ <a id="built-in-integrations-and-workflows"></a>
128
+
129
+ ## Integrations
130
+
131
+ Connect the sources you use. Each one adds evidence or tools to the same project.
132
+
133
+ <p>
134
+ <a href="docs/google-search-console-setup.md"><kbd><img src="https://www.google.com/s2/favicons?domain=search.google.com&amp;sz=64" alt="Google Search Console logo" width="16" valign="middle" /> Search Console</kbd></a> &nbsp;
135
+ <a href="docs/bing-webmaster-setup.md"><kbd><img src="https://www.google.com/s2/favicons?domain=bing.com&amp;sz=64" alt="Bing logo" width="16" valign="middle" /> Bing Webmaster Tools</kbd></a> &nbsp;
136
+ <a href="docs/google-analytics-setup.md"><kbd><img src="https://www.google.com/s2/favicons?domain=analytics.google.com&amp;sz=64" alt="Google Analytics logo" width="16" valign="middle" /> Google Analytics 4</kbd></a> &nbsp;
137
+ <a href="skills/canonry/references/google-business-profile.md"><kbd><img src="https://www.google.com/s2/favicons?domain=business.google.com&amp;sz=64" alt="Google Business Profile logo" width="16" valign="middle" /> Business Profile</kbd></a> &nbsp;
138
+ <a href="docs/google-marketing.md"><kbd><img src="https://www.google.com/s2/favicons?domain=ads.google.com&amp;sz=64" alt="Google Ads logo" width="16" valign="middle" /> Google Ads</kbd></a> &nbsp;
139
+ <a href="docs/google-marketing.md"><kbd><img src="https://www.google.com/s2/favicons?domain=tagmanager.google.com&amp;sz=64" alt="Google Tag Manager logo" width="16" valign="middle" /> Tag Manager</kbd></a> &nbsp;
140
+ <a href="docs/cloudflare-traffic-setup.md"><kbd><img src="https://www.google.com/s2/favicons?domain=cloudflare.com&amp;sz=64" alt="Cloudflare logo" width="16" valign="middle" /> Cloudflare</kbd></a> &nbsp;
141
+ <a href="skills/canonry/references/server-side-traffic.md"><kbd><img src="https://www.google.com/s2/favicons?domain=vercel.com&amp;sz=64" alt="Vercel logo" width="16" valign="middle" /> Vercel</kbd></a> &nbsp;
142
+ <a href="skills/canonry/references/server-side-traffic.md"><kbd><img src="https://www.google.com/s2/favicons?domain=cloud.google.com&amp;sz=64" alt="Google Cloud logo" width="16" valign="middle" /> Cloud Run</kbd></a> &nbsp;
143
+ <a href="docs/wordpress-setup.md"><kbd><img src="https://www.google.com/s2/favicons?domain=wordpress.org&amp;sz=64" alt="WordPress logo" width="16" valign="middle" /> WordPress</kbd></a> &nbsp;
144
+ <a href="docs/seo-data.md"><kbd><img src="https://www.google.com/s2/favicons?domain=dataforseo.com&amp;sz=64" alt="DataForSEO logo" width="16" valign="middle" /> DataForSEO</kbd></a> &nbsp;
145
+ <a href="skills/canonry/references/canonry-cli.md#backlinks-common-crawl"><kbd><img src="https://www.google.com/s2/favicons?domain=commoncrawl.org&amp;sz=64" alt="Common Crawl logo" width="16" valign="middle" /> Common Crawl</kbd></a> &nbsp;
146
+ <a href="skills/canonry/references/canonry-cli.md#openai-ads-chatgpt-ads"><kbd><img src="https://www.google.com/s2/favicons?domain=chatgpt.com&amp;sz=64" alt="ChatGPT logo" width="16" valign="middle" /> ChatGPT Ads</kbd></a>
147
+ </p>
148
+
149
+ ---
150
+
20
151
  ## Quick start
21
152
 
22
153
  <a id="or-use-any-shell-capable-coding-agent"></a>
23
154
 
24
- ### Start with your agent
155
+ ### With your agent
156
+
157
+ Paste this into Claude Code, Codex, or any shell-capable coding agent.
25
158
 
26
159
  <details>
27
160
  <summary>Copy the first-time setup request</summary>
@@ -48,37 +181,31 @@ Pick one path and stay on it. Use connected Canonry tools (plugin or MCP) only i
48
181
 
49
182
  </details>
50
183
 
51
- ### Or Use Official Claude and Codex Plugins
52
-
53
- Connect the [Agent Plugin](docs/plugins.md) or [MCP adapter](docs/mcp.md) to your own agent.
184
+ ### With the plugin
54
185
 
55
- <a id="get-a-page-health-baseline"></a>
56
-
57
- ### Or start it locally yourself
58
-
59
- 1. Install Canonry.
60
-
61
- ```bash
62
- npm install -g @canonry/canonry
63
- ```
64
-
65
- If you previously installed `@ainyc/canonry`, run `npm uninstall -g @ainyc/canonry` first to release the shared command names. Existing configuration and data in `~/.canonry` stay in place. New releases are published only under `@canonry`.
186
+ ```bash
187
+ # Claude Code
188
+ claude plugin marketplace add Canonry/canonry
189
+ claude plugin install canonry@canonry
66
190
 
67
- 2. Initialize Canonry.
191
+ # Codex
192
+ codex plugin marketplace add Canonry/canonry
193
+ codex plugin add canonry@canonry
194
+ ```
68
195
 
69
- ```bash
70
- cnry bootstrap
71
- ```
196
+ Or connect the [MCP adapter](docs/mcp.md) to any MCP client. Full steps: [Agent Plugin guide](docs/plugins.md).
72
197
 
73
- 3. Start Canonry.
198
+ <a id="get-a-page-health-baseline"></a>
74
199
 
75
- ```bash
76
- cnry serve
77
- ```
200
+ ### On your own
78
201
 
79
- 4. Open [http://127.0.0.1:4100/setup](http://127.0.0.1:4100/setup) and follow the setup to scan your site.
202
+ ```bash
203
+ npm install -g @canonry/canonry
204
+ cnry bootstrap
205
+ cnry serve
206
+ ```
80
207
 
81
- The crawl saves a Page Health baseline. AI Visibility is optional and has a separate setup.
208
+ Then open [http://127.0.0.1:4100/setup](http://127.0.0.1:4100/setup) and follow the setup to scan your site. The crawl saves a Page Health baseline. AI Visibility is optional and has a separate setup.
82
209
 
83
210
  <details>
84
211
  <summary>Scan and read results from the terminal</summary>
@@ -102,27 +229,6 @@ If the scan fails or is cancelled, read the error with `cnry run show <run-id> -
102
229
 
103
230
  </details>
104
231
 
105
- ## Explore the evidence
106
-
107
- Your agent and dashboard use the same project API. The dashboard makes trends, exact answers, and site findings available for human review.
108
-
109
- <a id="add-ai-visibility-when-you-need-it"></a>
110
-
111
- ### AI visibility
112
-
113
- Track brand mentions and citations over time, by query and answer engine. Find answers that cite competitors but omit your site.
114
-
115
- Compare direct AI engines, OpenRouter models, and search answers from Exa and Parallel.
116
-
117
- ![Canonry AI Visibility mention share trend across answer engines](https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/ai-visibility-trend.png)
118
-
119
- *Track your share of answer-engine brand mentions over time.*
120
-
121
- ![Canonry AI Visibility citation map across queries and answer engines](https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/ai-visibility-diagnostics.png)
122
-
123
- The citation map shows mention and citation coverage across queries and engines.
124
- [Rank cited domains and pages](skills/canonry/references/canonry-cli.md#cited-source-rankings-cnry-sources), then inspect the exact answers and URLs behind each result.
125
-
126
232
  <details>
127
233
  <summary>Configure a provider and run visibility checks</summary>
128
234
 
@@ -181,67 +287,19 @@ For a configured portfolio destination, select one `--market <key>` or `--proper
181
287
 
182
288
  </details>
183
289
 
184
- <a id="compare-competitors-and-measure-complex-portfolios"></a>
185
-
186
- ### Search, traffic, and site health
187
-
188
- Investigate visibility changes with evidence from search and local performance, crawler visits, AI page fetches, referrals, and conversions.
189
- Site audits show technical findings alongside that evidence.
190
-
191
- ![Canonry Site Map graph](https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/dashboard.png)
192
-
193
- *Map crawlable pages and the internal links that connect them.*
194
-
195
- <a id="seo-data"></a>
196
-
197
- ### SEO data with DataForSEO
198
-
199
- Use **DataForSEO Labs** for Google keyword research, organic rankings, and domain comparisons.
200
- Explore search volume, difficulty, CPC, ranking URLs, and estimated traffic through the dashboard, CLI, MCP, and API.
201
-
202
- [Configure DataForSEO](docs/seo-data.md).
203
-
204
- ![Canonry DataForSEO integration showing domain metrics and ranked keywords](docs/images/seo-data-phase-one/desktop.jpg)
205
-
206
- *SEO dashboard with sample data: organic keyword totals, estimated traffic, and ranked keywords.*
207
-
208
- ### Portfolios and competitors
209
-
210
- Use [versioned measurement plans](docs/mcp.md#tool-surface) for portfolios of locations, products, or site sections.
211
-
212
- - **Properties:** Assign queries, engines, models, and locations to each property. Keep branded and non-brand coverage separate.
213
- - **Market groups:** Organize overlapping sets of properties, each with its own competitors.
214
- - **Comparisons:** Compare properties, groups, engines, and locations. Open the exact answers and cited URLs behind each result.
215
-
216
- ## Act on the evidence
217
-
218
- Your agent coordinates the work through Canonry and its own tools.
219
-
220
- - **Improve the site:** Combine your agent's coding tools with Canonry's content, [WordPress publishing](docs/wordpress-setup.md), JSON-LD, and [indexing workflows](skills/canonry/references/indexing.md).
221
- - **Measure after changes:** Configure [independent schedules](skills/canonry/references/canonry-cli.md#scheduling--notifications) for data syncs and checks. Compare new results with earlier evidence.
222
- - **Export and follow up:** Read [AI visibility evidence](docs/report-retirement.md) and [traffic analytics](docs/report-retirement.md). Send [webhook alerts](skills/canonry/references/canonry-cli.md#agent) to Discord, Slack, or your systems.
223
-
224
- **An example job for your connected agent:**
225
-
226
- > Find where competitors get cited and we don't. Use search data and the site audit to prepare a pull request or WordPress draft for review. After it ships, rerun checks and report what changed.
227
-
228
- <a id="built-in-integrations-and-workflows"></a>
229
-
230
- ## Integrations
290
+ <a id="if-you-get-stuck"></a>
231
291
 
232
- Connect the sources you use. Each integration adds evidence or tools to the same project workflow.
292
+ <details>
293
+ <summary>Troubleshoot installation, scans, and visibility results</summary>
233
294
 
234
- | Area | Supported integrations |
295
+ | Problem | Next step |
235
296
  |---|---|
236
- | **AI answers** | ChatGPT · Claude · Gemini · Meta (Muse) · Perplexity · OpenAI-compatible local models |
237
- | **Search and local** | [Google Search Console](docs/google-search-console-setup.md) · [Bing Webmaster Tools](docs/bing-webmaster-setup.md) · [Google Business Profile](skills/canonry/references/google-business-profile.md) |
238
- | **SEO research** | **[DataForSEO Labs](docs/seo-data.md)**: keyword metrics, ranked keywords, and domain comparison |
239
- | **Analytics** | [Google Analytics 4](docs/google-analytics-setup.md) |
240
- | **Conversion measurement** | [Google Ads + Google Tag Manager](docs/google-marketing.md): read-only snapshots and declared conversion contracts |
241
- | **Server traffic** | [Cloudflare · Vercel · Cloud Run · WordPress](skills/canonry/references/server-side-traffic.md) |
242
- | **Backlinks** | [Common Crawl](skills/canonry/references/canonry-cli.md#backlinks-common-crawl) hyperlink releases, queried locally with DuckDB |
243
- | **Publishing and indexing** | [WordPress](docs/wordpress-setup.md) · JSON-LD · [sitemap and URL submissions](skills/canonry/references/indexing.md) |
244
- | **ChatGPT Ads** | [Campaign measurement, paused campaign editing, approved activation, and operation reconciliation](docs/mcp.md#tool-surface) |
297
+ | Site scan is still active | Read exact counters with `cnry technical-aeo progress <project> --run-id <id> --format json`. |
298
+ | Site scan failed | Read the error with `cnry run show <run-id> --format json`. Read the last phase and counters with the progress command. |
299
+ | No visibility results | Inspect existing work with `cnry runs <project> --format json`, then `cnry run show <run-id> --format json`. This does not start another paid run. |
300
+ | `npm install` fails on `node-gyp` | Install build tools for `better-sqlite3` ([guide](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/troubleshooting.md)). |
301
+
302
+ </details>
245
303
 
246
304
  <a id="deployment-and-trust-boundary"></a>
247
305
  <a id="technical-surface"></a>
@@ -263,23 +321,18 @@ See the [deployment guide](docs/deployment.md) for reverse proxies, daemon mode,
263
321
  - **Install standalone agent skills:** `cnry skills install` ([guide](skills/canonry/SKILL.md))
264
322
  - **Browse all guides:** [Documentation index](docs/README.md)
265
323
 
266
- <a id="if-you-get-stuck"></a>
324
+ ---
267
325
 
268
- <details>
269
- <summary>Troubleshoot installation, scans, and visibility results</summary>
326
+ <a id="support"></a>
270
327
 
271
- | Problem | Next step |
272
- |---|---|
273
- | Site scan is still active | Read exact counters with `cnry technical-aeo progress <project> --run-id <id> --format json`. |
274
- | Site scan failed | Read the error with `cnry run show <run-id> --format json`. Read the last phase and counters with the progress command. |
275
- | No visibility results | Inspect existing work with `cnry runs <project> --format json`, then `cnry run show <run-id> --format json`. This does not start another paid run. |
276
- | `npm install` fails on `node-gyp` | Install build tools for `better-sqlite3` ([guide](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/troubleshooting.md)). |
328
+ ## Community &amp; Support
277
329
 
278
- </details>
279
-
280
- ## Support
330
+ - **Discord:** Join the community on **[Discord](https://discord.gg/8K9yY7GywJ)** for help and questions.
331
+ - **Twitter / X:** Follow **[@canonry_ai](https://x.com/canonry_ai)** for updates.
332
+ - **Feedback &amp; Ideas:** Missing something? [Open an issue](https://github.com/Canonry/canonry/issues).
333
+ - **Show Support:** [Star](https://github.com/Canonry/canonry) this repo to follow along.
281
334
 
282
- Join the [Canonry Discord](https://discord.gg/8K9yY7GywJ) for support and questions about Canonry.
335
+ ---
283
336
 
284
337
  ## Contributing
285
338
 
@@ -291,6 +344,10 @@ pnpm install
291
344
  For local agent work, run tests only after changes are finalized and ready for push or review.
292
345
  `pnpm verify` is optional locally. See [CONTRIBUTING.md](./CONTRIBUTING.md) and the [testing guide](docs/testing.md).
293
346
 
347
+ <a href="https://github.com/Canonry/canonry/graphs/contributors">
348
+ <img src="https://contrib.rocks/image?repo=Canonry/canonry" alt="Canonry contributors" />
349
+ </a>
350
+
294
351
  ## License
295
352
 
296
353
  [MIT](./LICENSE), starting with Canonry 5.0.0. Use, modify, self-host, redistribute, and build commercial products with Canonry. Keep the copyright and permission notice with copies or substantial portions of the software.
@@ -522,10 +522,13 @@ cnry technical-aeo score <project> [--format json] # site score + per-facto
522
522
  cnry technical-aeo pages <project> [--status error] [--sort score-asc|score-desc|url] [--format json|jsonl] # audited-page compatibility view (worst-first by default)
523
523
  cnry technical-aeo trend <project> [--format json|jsonl] # aggregate-score history across past audits
524
524
  cnry schedule set <project> --kind site-audit --preset weekly # keep it fresh
525
+ cnry schedule set <project> --kind site-audit --check-dead-links --include-geo # make every scheduled scan a deep scan (keeps the timing)
525
526
  ```
526
527
 
527
528
  Agent integration checks run by default, inspecting discoverable actions, forms, and tool links. Use `--no-agent-skills` to disable them. Geographic checks are off by default; `--include-geo` adds location and service-area signals. Both selections change scan identity. Lighthouse is not exposed by Canonry.
528
529
 
530
+ A scheduled scan always covers the full site (or the project's saved page budget). Which checks it runs belongs to the schedule: `schedule set --kind site-audit` takes the same `--check-dead-links`, `--include-geo` and `--no-agent-skills` flags as a manual scan, and any of them sets the whole list of checks. `--default-checks` goes back to the defaults (no dead links, no location checks, agent checks on). Without these flags the schedule keeps its checks, so moving, pausing or resuming it never drops them; check flags alone keep the current timing. `schedule show` prints them on a `Checks:` line, and the API returns them as `siteAudit` (PUT `siteAudit`: omit to keep, `null` to reset).
531
+
529
532
  For agent site readiness, begin with `cnry technical-aeo score <project> --format json`. Use `cnry site-health overview <project>` only to add crawl metadata; it never replaces the score. Use `cnry site-health page-audit` (MCP: `canonry_site_health_page_audit`) to tie a selected graph page's audit score to exact findings and fixes. Link score is importance, not an audit verdict. Then request a focused neighborhood, a shortest path, or scan-to-scan changes. Do not ask an agent to materialize the interactive graph: it can exceed the MCP tool-result limit. The matching traversal tools are `canonry_site_health_subgraph`, `canonry_site_health_path`, and `canonry_site_health_changes`; the subgraph tool defaults to a small focused result and should be expanded only when needed.
530
533
 
531
534
  - The score is only available after at least one audit runs — `score` returns `hasData: false` until then.
@@ -719,6 +722,7 @@ cnry schedule set <project> --kind data-refresh --preset daily # refresh all c
719
722
  cnry schedule set <project> --kind backlinks-sync --preset weekly # re-probe Common Crawl; sync only when a newer rolling window is published (no --source/--provider)
720
723
  cnry doctor --project <project> --check site.reachability # probe the homepage now; the server also probes every 10 min and pages health.degraded after two failed passes
721
724
  cnry schedule set <project> --kind site-audit --preset weekly # Technical AEO: bounded full-site crawl and audit (no --source/--provider)
725
+ cnry schedule set <project> --kind site-audit --check-dead-links --include-geo # weekly deep scan: dead links + location checks on every scheduled scan
722
726
  cnry schedule set <project> --kind seo-sync --cron '0 7 2 * *' # paid SEO refresh of every saved profile, monthly (needs seo.run; no --source/--provider)
723
727
  cnry schedule show <project>
724
728
  cnry schedule list <project> --format json # all configured schedule kinds
@@ -1,4 +1,4 @@
1
- import{r as e}from"./rolldown-runtime-XKyaHtl9.js";import{U as t,V as n,Z as r,ct as i}from"./vendor-lucide-Bo69m9KC.js";import{r as a}from"./vendor-markdown-BiRP51B0.js";import{Br as o,On as s,Qt as c,Tt as l,Vr as u,Zt as d,ct as f,r as p,t as m,wt as h,zr as g}from"./ToneBadge-B3rD6bDF.js";import{r as _}from"./account-context-GstYbqpX.js";import{t as v}from"./async-handler-Bj3TnIRw.js";import{et as y,it as b}from"./index-D1ox2bx5.js";var x=e(i(),1),S=a(),C=`https://github.com/Canonry/canonry/blob/main/docs/mcp.md`,w=`https://github.com/Canonry/canonry/blob/main/docs/plugins.md`,T=`https://help.openai.com/en/articles/12584461-connecting-custom-mcp-servers-in-chatgpt`,E=`https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp`,D=`codex plugin marketplace add Canonry/canonry
1
+ import{r as e}from"./rolldown-runtime-XKyaHtl9.js";import{U as t,V as n,Z as r,ct as i}from"./vendor-lucide-Bo69m9KC.js";import{r as a}from"./vendor-markdown-BiRP51B0.js";import{Br as o,On as s,Qt as c,Tt as l,Vr as u,Zt as d,ct as f,r as p,t as m,wt as h,zr as g}from"./ToneBadge-ht0n6RUH.js";import{r as _}from"./account-context-DMLbDVNZ.js";import{t as v}from"./async-handler-Bj3TnIRw.js";import{et as y,it as b}from"./index-3jdPlyTf.js";var x=e(i(),1),S=a(),C=`https://github.com/Canonry/canonry/blob/main/docs/mcp.md`,w=`https://github.com/Canonry/canonry/blob/main/docs/plugins.md`,T=`https://help.openai.com/en/articles/12584461-connecting-custom-mcp-servers-in-chatgpt`,E=`https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp`,D=`codex plugin marketplace add Canonry/canonry
2
2
  codex plugin add canonry-remote@canonry`,O={[o[`public-url-required`]]:`Configure this instance’s public URL before connecting a hosted client.`,[o[`https-required`]]:`Configure an HTTPS public URL for this instance.`,[o[`public-host-required`]]:`Use a public HTTPS address that your client can reach. Localhost and private-network addresses do not work for hosted connectors.`,[o[`public-path-mismatch`]]:`Make the publicUrl path match basePath, and preserve that prefix when the proxy forwards requests for hosted OAuth.`,[o[`oauth-unavailable`]]:`The sign-in routes are unavailable. Check this instance’s OAuth configuration.`,[o[`account-required`]]:`Create a named Canonry account on this instance’s server with canonry user create. A shared dashboard password does not provide an OAuth account.`,[o[`transport-unavailable`]]:`Restore the instance’s HTTP MCP service before connecting a hosted client.`},k={codex:{label:`Codex`,command:`codex plugin marketplace add Canonry/canonry
3
3
  codex plugin add canonry@canonry`},claude:{label:`Claude Code`,command:`claude plugin marketplace add Canonry/canonry
4
4
  claude plugin install canonry@canonry`},other:{label:`Other MCP clients`,command:`canonry-mcp`}};function A({label:e,value:n,showText:i=!1,secondary:a=!1,action:o=`agent_setup.copy`}){let c=(0,x.useId)(),[l,u]=(0,x.useState)(!1),[d,f]=(0,x.useState)(!1);async function m(){try{await navigator.clipboard.writeText(n),s(o),u(!0),f(!1)}catch{u(!1),f(!0)}}return(0,S.jsxs)(`div`,{className:`min-w-0 space-y-3`,children:[i?(0,S.jsx)(`pre`,{className:`overflow-x-auto material-base p-3 text-copy-14 text-heading`,children:(0,S.jsx)(`code`,{children:n})}):null,(0,S.jsxs)(p,{type:`button`,className:`pointer-coarse:min-h-11`,variant:a?`outline`:`default`,onClick:v(m),children:[l?(0,S.jsx)(r,{className:`size-4`,"aria-hidden":`true`}):(0,S.jsx)(t,{className:`size-4`,"aria-hidden":`true`}),e]}),l?(0,S.jsx)(`p`,{role:`status`,className:`text-copy-14 text-secondary`,children:`Copied.`}):null,!i||d?(0,S.jsxs)(`details`,{open:d||void 0,className:`text-label-14 text-secondary`,children:[(0,S.jsx)(`summary`,{className:`min-h-11 w-fit cursor-pointer py-3`,children:d?`Copy unavailable. Select the text below.`:`Preview text`}),(0,S.jsxs)(`label`,{htmlFor:c,className:`sr-only`,children:[e,` text`]}),(0,S.jsx)(`textarea`,{id:c,readOnly:!0,rows:d&&i?3:8,value:n,className:`mt-2 w-full rounded-md border border-default bg-bg p-3 text-copy-14 text-heading`})]}):null]})}function j(){return h()||l()?(0,S.jsxs)(`div`,{className:`page-container`,children:[(0,S.jsx)(`h1`,{className:`page-title`,children:`Agent setup`}),(0,S.jsx)(`p`,{className:`mt-3 text-copy-14 text-secondary`,children:`Open Agent setup in your own Canonry instance to connect your agent.`}),c()?(0,S.jsx)(`a`,{className:`mt-4 inline-block text-label-14 underline`,href:C,target:`_blank`,rel:`noopener noreferrer`,children:`Read the connection guide`}):null]}):(0,S.jsx)(M,{})}function M(){let e=b(),{isAdmin:t}=_(),r=d(),i=c(),a=y(!e,e?.health),o=a.data?.apiStatus,s=o?.state===`ok`?o.mcp:void 0,l=s?.status===u.available&&s.hosted?.status===g.ready?s.hosted:void 0,p=s?.hosted?.status===g.unavailable?s.hosted.reason:void 0,h=`${window.location.origin}${f()}`,v=`This server has not confirmed a public URL and OAuth sign-in. Ask your administrator to check the connection configuration or update Canonry.`;a.isPending?v=`Checking the instance’s hosted connection settings.`:p?v=t&&r?O[p]:`Ask your Canonry administrator to finish hosted connection setup for this instance.`:o?.state===`error`?v=`The instance’s connection settings could not be checked. Try again after the service is available.`:s?.status===u[`not-supported`]&&(v=`This deployment does not provide hosted MCP connections. Ask your administrator for a supported connection.`);let x=l?`Help me connect this existing Canonry instance to ChatGPT or Claude Desktop using its remote MCP URL: ${l.url}
@@ -1 +1 @@
1
- import{r as e}from"./rolldown-runtime-XKyaHtl9.js";import{ct as t}from"./vendor-lucide-Bo69m9KC.js";import{r as n}from"./vendor-markdown-BiRP51B0.js";import{b as r}from"./vendor-tanstack-tBjrfzKu.js";import{mt as i,r as a,t as o}from"./ToneBadge-B3rD6bDF.js";import{fn as s,i as c,nt as l}from"./react-query-B8JD_K7g.js";import{t as u}from"./card-CgdGerYK.js";var d=e(t(),1),f=n(),p=100;function m(e){if(e===`all`)return;let t=Number.parseInt(e,10);return new Date(Date.now()-t*24*60*60*1e3).toISOString()}function h(e){let t=e.userAgent?.toLowerCase()??``;return t.includes(`canonry-mcp`)?`MCP`:t.includes(`canonry-cli`)?`CLI`:t.includes(`mozilla/`)?`Dashboard`:e.actor===`scheduler`?`Scheduler`:e.actor===`agent`?`Agent`:e.actor===`mcp`?`MCP`:e.actor===`cli`?`CLI`:e.actor}function g(e){return/delete|remove|revoke|disconnect|failed|refused/.test(e)?`negative`:/create|add|connect|completed/.test(e)?`positive`:/update|replace|sync|apply/.test(e)?`caution`:`neutral`}function _(e){return[e.action,e.actor,e.entityType,e.entityId,e.actorSession,e.userAgent,e.diff==null?``:JSON.stringify(e.diff)].filter(Boolean).join(` `).toLowerCase()}function v({projectName:e}){let[t,n]=(0,d.useState)(0),[v,y]=(0,d.useState)(`90d`),[b,x]=(0,d.useState)(``),[S,C]=(0,d.useState)(``),w=(0,d.useMemo)(()=>m(v),[v]),T={limit:p,offset:t*p,since:w,actor:b||void 0},E=r({...l({client:i,path:{name:e??``},query:T}),enabled:!!e}),D=r({...c({client:i,query:T}),enabled:!e}),O=r({...s({client:i}),enabled:!e}),k=e?E:D,A=k.data??[],j=(0,d.useMemo)(()=>new Map((O.data??[]).map(e=>[e.id,e.displayName||e.name])),[O.data]),M=S.trim().toLowerCase(),N=(0,d.useMemo)(()=>M?A.filter(e=>_(e).includes(M)):A,[A,M]),P=e=>{n(0),e()};return k.isLoading?(0,f.jsx)(`p`,{className:`supporting-copy mt-6`,role:`status`,children:`Loading change history…`}):(0,f.jsxs)(`section`,{className:`mt-6`,"aria-labelledby":`change-history-heading`,children:[(0,f.jsxs)(`div`,{className:`section-head section-head-inline`,children:[(0,f.jsxs)(`div`,{children:[(0,f.jsx)(`p`,{className:`eyebrow eyebrow-soft`,children:`Audit trail`}),(0,f.jsx)(`h2`,{id:`change-history-heading`,children:`Change history`}),(0,f.jsx)(`p`,{className:`supporting-copy mt-1 max-w-2xl`,children:`Configuration and operator actions. Execution outcomes remain in Runs.`})]}),(0,f.jsx)(a,{type:`button`,variant:`outline`,size:`sm`,onClick:()=>void k.refetch(),disabled:k.isFetching,children:k.isFetching?`Refreshing…`:`Refresh`})]}),(0,f.jsxs)(`div`,{className:`mt-4 grid gap-2 sm:grid-cols-3`,children:[(0,f.jsx)(`input`,{type:`search`,"aria-label":`Search change history`,value:S,onChange:e=>C(e.target.value),placeholder:`Search actions, entities, or details`,className:`min-h-11 rounded-md border border-base bg-bg px-3 text-label-14 text-strong placeholder-mono-400 focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-600`}),(0,f.jsxs)(`select`,{"aria-label":`Filter change history by actor`,value:b,onChange:e=>P(()=>x(e.target.value)),className:`min-h-11 rounded-md border border-base bg-bg px-3 text-label-14 text-strong focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-600`,children:[(0,f.jsx)(`option`,{value:``,children:`All actors`}),(0,f.jsx)(`option`,{value:`api`,children:`API and dashboard`}),(0,f.jsx)(`option`,{value:`cli`,children:`CLI`}),(0,f.jsx)(`option`,{value:`scheduler`,children:`Scheduler`}),(0,f.jsx)(`option`,{value:`agent`,children:`Agent`}),(0,f.jsx)(`option`,{value:`mcp`,children:`MCP`})]}),(0,f.jsxs)(`select`,{"aria-label":`Filter change history by date range`,value:v,onChange:e=>P(()=>y(e.target.value)),className:`min-h-11 rounded-md border border-base bg-bg px-3 text-label-14 text-strong focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-600`,children:[(0,f.jsx)(`option`,{value:`30d`,children:`Last 30 days`}),(0,f.jsx)(`option`,{value:`90d`,children:`Last 90 days`}),(0,f.jsx)(`option`,{value:`365d`,children:`Last year`}),(0,f.jsx)(`option`,{value:`all`,children:`All retained history`})]})]}),k.isError?(0,f.jsxs)(u,{className:`surface-card empty-card mt-4`,children:[(0,f.jsx)(`h3`,{children:`Change history unavailable`}),(0,f.jsx)(`p`,{children:k.error instanceof Error?k.error.message:`Could not load the audit trail.`})]}):N.length===0?(0,f.jsxs)(u,{className:`surface-card empty-card mt-4`,children:[(0,f.jsx)(`h3`,{children:`No changes match these filters`}),(0,f.jsx)(`p`,{children:`Widen the date range or clear the actor and search filters.`})]}):(0,f.jsx)(`div`,{className:`evidence-table-wrap mt-4`,children:(0,f.jsxs)(`table`,{className:`evidence-table`,children:[(0,f.jsx)(`thead`,{children:(0,f.jsxs)(`tr`,{children:[(0,f.jsx)(`th`,{children:`When`}),e?null:(0,f.jsx)(`th`,{children:`Scope`}),(0,f.jsx)(`th`,{children:`Action`}),(0,f.jsx)(`th`,{children:`Entity`}),(0,f.jsx)(`th`,{children:`Origin`}),(0,f.jsx)(`th`,{children:`Details`})]})}),(0,f.jsx)(`tbody`,{children:N.map(t=>(0,f.jsxs)(`tr`,{children:[(0,f.jsx)(`td`,{className:`whitespace-nowrap tabular-nums text-muted`,children:new Date(t.createdAt).toLocaleString()}),e?null:(0,f.jsx)(`td`,{className:`text-secondary`,children:t.projectId?j.get(t.projectId)??`Deleted project · ${t.projectId}`:`Instance settings`}),(0,f.jsx)(`td`,{children:(0,f.jsx)(o,{tone:g(t.action),children:t.action})}),(0,f.jsxs)(`td`,{children:[(0,f.jsx)(`span`,{className:`text-strong`,children:t.entityType}),t.entityId?(0,f.jsx)(`span`,{className:`mt-0.5 block max-w-48 truncate text-label-12-mono text-faint`,title:t.entityId,children:t.entityId}):null]}),(0,f.jsxs)(`td`,{children:[(0,f.jsx)(`span`,{className:`text-secondary`,children:h(t)}),t.actorSession?(0,f.jsxs)(`span`,{className:`mt-0.5 block max-w-40 truncate text-label-12-mono text-faint`,title:t.actorSession,children:[`Session `,t.actorSession]}):null]}),(0,f.jsx)(`td`,{children:t.diff==null?(0,f.jsx)(`span`,{className:`text-faint`,children:`No field diff`}):(0,f.jsxs)(`details`,{children:[(0,f.jsx)(`summary`,{className:`min-h-11 cursor-pointer py-3 text-label-14 text-secondary hover:text-strong`,children:`View diff`}),(0,f.jsx)(`pre`,{className:`max-h-72 max-w-xl overflow-auto whitespace-pre-wrap break-words rounded-md bg-bg-elevated p-3 text-copy-13 text-secondary`,children:JSON.stringify(t.diff,null,2)})]})})]},t.id))})]})}),!k.isError&&(t>0||A.length===p)?(0,f.jsxs)(`div`,{className:`mt-4 flex items-center justify-between gap-3`,children:[(0,f.jsx)(a,{type:`button`,variant:`outline`,size:`sm`,disabled:t===0,onClick:()=>n(e=>Math.max(0,e-1)),children:`Previous`}),(0,f.jsxs)(`span`,{className:`text-label-12 tabular-nums text-muted`,children:[`Page `,t+1]}),(0,f.jsx)(a,{type:`button`,variant:`outline`,size:`sm`,disabled:A.length<p,onClick:()=>n(e=>e+1),children:`Next`})]}):null]})}export{v as t};
1
+ import{r as e}from"./rolldown-runtime-XKyaHtl9.js";import{ct as t}from"./vendor-lucide-Bo69m9KC.js";import{r as n}from"./vendor-markdown-BiRP51B0.js";import{b as r}from"./vendor-tanstack-tBjrfzKu.js";import{mt as i,r as a,t as o}from"./ToneBadge-ht0n6RUH.js";import{fn as s,i as c,nt as l}from"./react-query-DIdeX4Dn.js";import{t as u}from"./card-Be_eN_-Q.js";var d=e(t(),1),f=n(),p=100;function m(e){if(e===`all`)return;let t=Number.parseInt(e,10);return new Date(Date.now()-t*24*60*60*1e3).toISOString()}function h(e){let t=e.userAgent?.toLowerCase()??``;return t.includes(`canonry-mcp`)?`MCP`:t.includes(`canonry-cli`)?`CLI`:t.includes(`mozilla/`)?`Dashboard`:e.actor===`scheduler`?`Scheduler`:e.actor===`agent`?`Agent`:e.actor===`mcp`?`MCP`:e.actor===`cli`?`CLI`:e.actor}function g(e){return/delete|remove|revoke|disconnect|failed|refused/.test(e)?`negative`:/create|add|connect|completed/.test(e)?`positive`:/update|replace|sync|apply/.test(e)?`caution`:`neutral`}function _(e){return[e.action,e.actor,e.entityType,e.entityId,e.actorSession,e.userAgent,e.diff==null?``:JSON.stringify(e.diff)].filter(Boolean).join(` `).toLowerCase()}function v({projectName:e}){let[t,n]=(0,d.useState)(0),[v,y]=(0,d.useState)(`90d`),[b,x]=(0,d.useState)(``),[S,C]=(0,d.useState)(``),w=(0,d.useMemo)(()=>m(v),[v]),T={limit:p,offset:t*p,since:w,actor:b||void 0},E=r({...l({client:i,path:{name:e??``},query:T}),enabled:!!e}),D=r({...c({client:i,query:T}),enabled:!e}),O=r({...s({client:i}),enabled:!e}),k=e?E:D,A=k.data??[],j=(0,d.useMemo)(()=>new Map((O.data??[]).map(e=>[e.id,e.displayName||e.name])),[O.data]),M=S.trim().toLowerCase(),N=(0,d.useMemo)(()=>M?A.filter(e=>_(e).includes(M)):A,[A,M]),P=e=>{n(0),e()};return k.isLoading?(0,f.jsx)(`p`,{className:`supporting-copy mt-6`,role:`status`,children:`Loading change history…`}):(0,f.jsxs)(`section`,{className:`mt-6`,"aria-labelledby":`change-history-heading`,children:[(0,f.jsxs)(`div`,{className:`section-head section-head-inline`,children:[(0,f.jsxs)(`div`,{children:[(0,f.jsx)(`p`,{className:`eyebrow eyebrow-soft`,children:`Audit trail`}),(0,f.jsx)(`h2`,{id:`change-history-heading`,children:`Change history`}),(0,f.jsx)(`p`,{className:`supporting-copy mt-1 max-w-2xl`,children:`Configuration and operator actions. Execution outcomes remain in Runs.`})]}),(0,f.jsx)(a,{type:`button`,variant:`outline`,size:`sm`,onClick:()=>void k.refetch(),disabled:k.isFetching,children:k.isFetching?`Refreshing…`:`Refresh`})]}),(0,f.jsxs)(`div`,{className:`mt-4 grid gap-2 sm:grid-cols-3`,children:[(0,f.jsx)(`input`,{type:`search`,"aria-label":`Search change history`,value:S,onChange:e=>C(e.target.value),placeholder:`Search actions, entities, or details`,className:`min-h-11 rounded-md border border-base bg-bg px-3 text-label-14 text-strong placeholder-mono-400 focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-600`}),(0,f.jsxs)(`select`,{"aria-label":`Filter change history by actor`,value:b,onChange:e=>P(()=>x(e.target.value)),className:`min-h-11 rounded-md border border-base bg-bg px-3 text-label-14 text-strong focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-600`,children:[(0,f.jsx)(`option`,{value:``,children:`All actors`}),(0,f.jsx)(`option`,{value:`api`,children:`API and dashboard`}),(0,f.jsx)(`option`,{value:`cli`,children:`CLI`}),(0,f.jsx)(`option`,{value:`scheduler`,children:`Scheduler`}),(0,f.jsx)(`option`,{value:`agent`,children:`Agent`}),(0,f.jsx)(`option`,{value:`mcp`,children:`MCP`})]}),(0,f.jsxs)(`select`,{"aria-label":`Filter change history by date range`,value:v,onChange:e=>P(()=>y(e.target.value)),className:`min-h-11 rounded-md border border-base bg-bg px-3 text-label-14 text-strong focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-600`,children:[(0,f.jsx)(`option`,{value:`30d`,children:`Last 30 days`}),(0,f.jsx)(`option`,{value:`90d`,children:`Last 90 days`}),(0,f.jsx)(`option`,{value:`365d`,children:`Last year`}),(0,f.jsx)(`option`,{value:`all`,children:`All retained history`})]})]}),k.isError?(0,f.jsxs)(u,{className:`surface-card empty-card mt-4`,children:[(0,f.jsx)(`h3`,{children:`Change history unavailable`}),(0,f.jsx)(`p`,{children:k.error instanceof Error?k.error.message:`Could not load the audit trail.`})]}):N.length===0?(0,f.jsxs)(u,{className:`surface-card empty-card mt-4`,children:[(0,f.jsx)(`h3`,{children:`No changes match these filters`}),(0,f.jsx)(`p`,{children:`Widen the date range or clear the actor and search filters.`})]}):(0,f.jsx)(`div`,{className:`evidence-table-wrap mt-4`,children:(0,f.jsxs)(`table`,{className:`evidence-table`,children:[(0,f.jsx)(`thead`,{children:(0,f.jsxs)(`tr`,{children:[(0,f.jsx)(`th`,{children:`When`}),e?null:(0,f.jsx)(`th`,{children:`Scope`}),(0,f.jsx)(`th`,{children:`Action`}),(0,f.jsx)(`th`,{children:`Entity`}),(0,f.jsx)(`th`,{children:`Origin`}),(0,f.jsx)(`th`,{children:`Details`})]})}),(0,f.jsx)(`tbody`,{children:N.map(t=>(0,f.jsxs)(`tr`,{children:[(0,f.jsx)(`td`,{className:`whitespace-nowrap tabular-nums text-muted`,children:new Date(t.createdAt).toLocaleString()}),e?null:(0,f.jsx)(`td`,{className:`text-secondary`,children:t.projectId?j.get(t.projectId)??`Deleted project · ${t.projectId}`:`Instance settings`}),(0,f.jsx)(`td`,{children:(0,f.jsx)(o,{tone:g(t.action),children:t.action})}),(0,f.jsxs)(`td`,{children:[(0,f.jsx)(`span`,{className:`text-strong`,children:t.entityType}),t.entityId?(0,f.jsx)(`span`,{className:`mt-0.5 block max-w-48 truncate text-label-12-mono text-faint`,title:t.entityId,children:t.entityId}):null]}),(0,f.jsxs)(`td`,{children:[(0,f.jsx)(`span`,{className:`text-secondary`,children:h(t)}),t.actorSession?(0,f.jsxs)(`span`,{className:`mt-0.5 block max-w-40 truncate text-label-12-mono text-faint`,title:t.actorSession,children:[`Session `,t.actorSession]}):null]}),(0,f.jsx)(`td`,{children:t.diff==null?(0,f.jsx)(`span`,{className:`text-faint`,children:`No field diff`}):(0,f.jsxs)(`details`,{children:[(0,f.jsx)(`summary`,{className:`min-h-11 cursor-pointer py-3 text-label-14 text-secondary hover:text-strong`,children:`View diff`}),(0,f.jsx)(`pre`,{className:`max-h-72 max-w-xl overflow-auto whitespace-pre-wrap break-words rounded-md bg-bg-elevated p-3 text-copy-13 text-secondary`,children:JSON.stringify(t.diff,null,2)})]})})]},t.id))})]})}),!k.isError&&(t>0||A.length===p)?(0,f.jsxs)(`div`,{className:`mt-4 flex items-center justify-between gap-3`,children:[(0,f.jsx)(a,{type:`button`,variant:`outline`,size:`sm`,disabled:t===0,onClick:()=>n(e=>Math.max(0,e-1)),children:`Previous`}),(0,f.jsxs)(`span`,{className:`text-label-12 tabular-nums text-muted`,children:[`Page `,t+1]}),(0,f.jsx)(a,{type:`button`,variant:`outline`,size:`sm`,disabled:A.length<p,onClick:()=>n(e=>e+1),children:`Next`})]}):null]})}export{v as t};
@@ -1 +1 @@
1
- import{r as e}from"./rolldown-runtime-XKyaHtl9.js";import{H as t,V as n,c as r,ct as i,s as a,w as o}from"./vendor-lucide-Bo69m9KC.js";import{r as s}from"./vendor-markdown-BiRP51B0.js";import{H as c,L as l,R as u,U as d,a as f,dn as p,jt as m,q as h,r as g,t as _,vt as v,wt as y}from"./ToneBadge-B3rD6bDF.js";import{t as b}from"./async-handler-Bj3TnIRw.js";import{t as x}from"./card-CgdGerYK.js";import{t as S}from"./Hint-uoVNfr0U.js";var C=e(i(),1),w=s(),T=`https://commoncrawl.org/web-graphs`;function E(e){return e==null?`—`:e>=0xe8d4a51000?`${(e/0xe8d4a51000).toFixed(1)} TB`:e>=1e9?`${(e/1e9).toFixed(1)} GB`:e>=1e6?`${(e/1e6).toFixed(1)} MB`:e>=1e3?`${(e/1e3).toFixed(1)} KB`:`${e} B`}function D(e){if(!e)return`—`;let t=Date.now()-new Date(e).getTime(),n=Math.floor(t/6e4);if(n<1)return`just now`;if(n<60)return`${n}m ago`;let r=Math.floor(n/60);return r<24?`${r}h ago`:`${Math.floor(r/24)}d ago`}function O(e){switch(e){case`ready`:return`positive`;case`failed`:return`negative`;case`downloading`:case`querying`:case`queued`:return`caution`}}function k(){let[e,i]=(0,C.useState)(null),[s,k]=(0,C.useState)(null),[A,j]=(0,C.useState)([]),[M,N]=(0,C.useState)([]),[P,F]=(0,C.useState)(null),[I,L]=(0,C.useState)(!0),[R,z]=(0,C.useState)(!1),[B,V]=(0,C.useState)(!1),[H,U]=(0,C.useState)(``),[W,G]=(0,C.useState)(!1),[K,q]=(0,C.useState)(null),[J,Y]=(0,C.useState)(null),X=(0,C.useCallback)(async()=>{L(!0),q(null);try{let[e,t,n,r,a]=await Promise.all([l(),d().catch(()=>null),h().catch(()=>[]),u().catch(()=>[]),c().catch(()=>null)]);i(e),k(t),j(n),N(r),F(a)}catch(e){q(e instanceof Error?e.message:`Failed to load backlinks status`)}finally{L(!1)}},[]);(0,C.useEffect)(()=>{X()},[X]);async function Z(){z(!0),q(null),Y(null);try{let e=await v();Y(e.alreadyPresent?`DuckDB already installed (${e.version}).`:`Installed DuckDB ${e.version}.`),await X()}catch(e){q(e instanceof Error?e.message:`Failed to install DuckDB`)}finally{z(!1)}}async function Q(){let e=H.trim()||void 0;V(!0),q(null),Y(null);try{let t=await p(e);Y(e?`Queued sync for ${t.release}. Download + query runs in the background.`:`Queued sync for auto-discovered release ${t.release}. Download + query runs in the background.`),U(``),G(!1),await X()}catch(e){e instanceof f&&e.code===`MISSING_DEPENDENCY`?q(`DuckDB is not installed. Install it first.`):q(e instanceof Error?e.message:`Failed to trigger sync`)}finally{V(!1)}}async function $(e){q(null),Y(null);try{await m(e),Y(`Pruned cached release ${e}.`),await X()}catch(e){q(e instanceof Error?e.message:`Failed to prune release`)}}let ee=s?.status===`ready`&&M.every(e=>e.release!==s.release);return(0,w.jsxs)(`div`,{className:`page-container`,children:[(0,w.jsx)(`div`,{className:`page-header`,children:(0,w.jsxs)(`div`,{className:`page-header-left`,children:[(0,w.jsx)(`h1`,{className:`page-title`,children:`Backlink data`}),(0,w.jsx)(`p`,{className:`page-subtitle`,children:`Manage the shared backlink source and review its latest data.`})]})}),K&&(0,w.jsx)(x,{className:`surface-card p-4 mb-4 bg-negative-soft`,children:(0,w.jsx)(`p`,{className:`text-copy-14 text-negative`,children:K})}),J&&(0,w.jsx)(x,{className:`surface-card p-4 mb-4 bg-positive-soft`,children:(0,w.jsx)(`p`,{className:`text-copy-14 text-positive`,children:J})}),(0,w.jsxs)(`section`,{className:`page-section-divider`,children:[(0,w.jsxs)(`div`,{className:`section-head section-head-inline`,children:[(0,w.jsxs)(`div`,{children:[(0,w.jsx)(`p`,{className:`eyebrow eyebrow-soft`,children:`Readiness`}),(0,w.jsx)(`h2`,{children:`Backlink source`})]}),e?.duckdbInstalled?(0,w.jsx)(_,{tone:`positive`,children:`Installed`}):(0,w.jsx)(_,{tone:`caution`,children:`Not installed`})]}),(0,w.jsx)(x,{className:`surface-card p-4`,children:I?(0,w.jsx)(`p`,{className:`text-copy-14 text-muted`,children:`Checking…`}):e?.duckdbInstalled?(0,w.jsx)(`p`,{className:`text-copy-14 text-secondary`,children:`Ready to sync a Common Crawl release.`}):(0,w.jsxs)(`div`,{className:`flex items-center justify-between gap-4`,children:[(0,w.jsx)(`p`,{className:`text-copy-14 text-secondary`,children:`Install the local query engine to enable release syncs.`}),!y()&&(0,w.jsxs)(g,{type:`button`,size:`sm`,disabled:R,onClick:b(Z),children:[(0,w.jsx)(t,{className:`h-4 w-4 mr-1.5`,"aria-hidden":!0}),R?`Installing…`:`Install DuckDB`]})]})})]}),(0,w.jsxs)(`section`,{className:`page-section-divider`,children:[(0,w.jsxs)(`div`,{className:`section-head section-head-inline`,children:[(0,w.jsxs)(`div`,{children:[(0,w.jsx)(`p`,{className:`eyebrow eyebrow-soft`,children:`Latest sync`}),(0,w.jsxs)(`h2`,{className:`flex items-center gap-2`,children:[`Release sync`,(0,w.jsx)(S,{label:`What is a release sync?`,children:`A release sync downloads one Common Crawl dump (~16 GB) and extracts backlinks for every project in this workspace in one pass. This is the heavy job — subsequent per-project re-runs skip the download and just re-query the cached files.`})]})]}),s&&(0,w.jsx)(_,{tone:O(s.status),children:s.status})]}),(0,w.jsxs)(x,{className:`surface-card p-5`,children:[s?(0,w.jsxs)(`div`,{className:`space-y-2 text-label-14`,children:[(0,w.jsxs)(`p`,{className:`text-strong`,children:[`Release `,(0,w.jsx)(`code`,{className:`text-neutral`,children:s.release})]}),s.phaseDetail&&(0,w.jsx)(`p`,{className:`text-secondary`,children:s.phaseDetail}),(0,w.jsxs)(`div`,{className:`grid grid-cols-2 md:grid-cols-4 gap-4 text-label-12 text-muted pt-2`,children:[(0,w.jsxs)(`div`,{children:[(0,w.jsx)(`p`,{className:`text-faint normal-case text-label-12`,children:`Projects`}),(0,w.jsx)(`p`,{className:`text-neutral mt-0.5`,children:s.projectsProcessed??`—`})]}),(0,w.jsxs)(`div`,{children:[(0,w.jsxs)(`p`,{className:`text-faint normal-case text-label-12 flex items-center gap-1`,children:[`Rows`,(0,w.jsx)(S,{label:`What are rows?`,children:`Total number of (project, referring domain) pairs persisted in SQLite from this sync, across every project in the workspace.`})]}),(0,w.jsx)(`p`,{className:`text-neutral mt-0.5`,children:s.domainsDiscovered??`—`})]}),(0,w.jsxs)(`div`,{children:[(0,w.jsx)(`p`,{className:`text-faint normal-case text-label-12`,children:`Started`}),(0,w.jsx)(`p`,{className:`text-neutral mt-0.5`,children:D(s.downloadStartedAt??s.createdAt)})]}),(0,w.jsxs)(`div`,{children:[(0,w.jsx)(`p`,{className:`text-faint normal-case text-label-12`,children:`Finished`}),(0,w.jsx)(`p`,{className:`text-neutral mt-0.5`,children:D(s.queryFinishedAt)})]})]}),s.error&&(0,w.jsx)(`p`,{className:`text-copy-14 text-negative-400 pt-2`,children:s.error})]}):(0,w.jsx)(`p`,{className:`text-copy-14 text-muted`,children:`No release sync has run in this workspace yet.`}),ee&&(0,w.jsx)(`div`,{className:`mt-4 rounded border border-caution-800/60 bg-caution-950/20 p-3`,children:(0,w.jsxs)(`div`,{className:`flex items-start gap-2`,children:[(0,w.jsx)(a,{className:`h-4 w-4 text-caution-400 shrink-0 mt-0.5`,"aria-hidden":!0}),(0,w.jsxs)(`div`,{className:`text-label-14 text-neutral`,children:[(0,w.jsx)(`p`,{className:`text-copy-14-strong text-caution-200`,children:`Cached files for this release are missing.`}),(0,w.jsx)(`p`,{className:`mt-1 text-secondary`,children:`Sync this release again, or sync the newest release, before refreshing individual projects.`})]})]})}),(0,w.jsxs)(`div`,{className:`mt-4 material-base p-3`,children:[(0,w.jsxs)(`div`,{className:`flex items-start justify-between gap-3 mb-3`,children:[(0,w.jsxs)(`div`,{children:[(0,w.jsx)(`p`,{className:`text-label-12 normal-case text-muted`,children:`Auto-detected release`}),P?(0,w.jsxs)(`p`,{className:`text-copy-14 text-strong mt-0.5`,children:[(0,w.jsx)(`code`,{className:`text-heading`,children:P.release}),(0,w.jsxs)(`span`,{className:`ml-2 text-label-12 text-muted`,children:[`Vertex `,E(P.vertexBytes),`, edges `,E(P.edgesBytes)]})]}):(0,w.jsx)(`p`,{className:`text-copy-14 text-muted mt-0.5`,children:I?`Checking Common Crawl…`:`Could not find a release. Choose one below.`}),(0,w.jsxs)(`a`,{href:T,target:`_blank`,rel:`noopener noreferrer`,className:`mt-1 inline-flex items-center gap-1 text-label-12 text-secondary hover:text-strong focus:text-strong focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-500 rounded`,children:[`Browse all Common Crawl web-graph releases`,(0,w.jsx)(n,{className:`h-3 w-3`,"aria-hidden":!0})]})]}),!y()&&(0,w.jsxs)(`div`,{className:`flex items-center gap-2 shrink-0`,children:[(0,w.jsxs)(g,{type:`button`,size:`sm`,disabled:B||!e?.duckdbInstalled||!P&&!H.trim(),onClick:b(Q),children:[(0,w.jsx)(o,{className:`h-4 w-4 mr-1.5`,"aria-hidden":!0}),B?`Queuing…`:`Run sync`]}),(0,w.jsxs)(S,{label:`What does Run sync do?`,children:[(0,w.jsxs)(`span`,{className:`block`,children:[`Downloads the auto-detected (or chosen) Common Crawl release (~16 GB) to`,` `,(0,w.jsx)(`code`,{className:`text-neutral`,children:`~/.canonry/cache/commoncrawl/`}),`, then runs a single DuckDB query that extracts referring domains for every project in this workspace.`]}),(0,w.jsxs)(`span`,{className:`mt-2 block text-secondary`,children:[`First time for a release: `,(0,w.jsx)(`span`,{className:`text-strong`,children:`~10–20 min download + ~5 min query`}),`. Re-running the same release later: `,(0,w.jsx)(`span`,{className:`text-strong`,children:`skips download, just re-queries`}),` (~5 min).`]})]})]})]}),y()?null:W?(0,w.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,w.jsx)(`input`,{type:`text`,className:`flex-1 min-w-[240px] rounded border border-strong bg-transparent px-2.5 py-1.5 text-label-14 text-strong placeholder-mono-400 focus:border-mono-500 focus:outline-none`,placeholder:`cc-main-2026-jan-feb-mar`,value:H,onChange:e=>U(e.target.value),disabled:B,autoFocus:!0}),(0,w.jsx)(`button`,{type:`button`,className:`text-button-14 text-secondary hover:text-neutral focus:text-neutral focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-500 rounded`,onClick:()=>{U(``),G(!1)},disabled:B,children:`Cancel`})]}):(0,w.jsx)(`button`,{type:`button`,className:`text-button-14 text-secondary hover:text-neutral focus:text-neutral focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-500 rounded`,onClick:()=>G(!0),disabled:B,children:`Use a different release →`})]}),!e?.duckdbInstalled&&(0,w.jsx)(`p`,{className:`text-copy-14 text-secondary mt-2`,children:`Install DuckDB first to enable sync.`})]})]}),(0,w.jsxs)(`details`,{className:`page-section-divider group`,children:[(0,w.jsx)(`summary`,{className:`cursor-pointer text-label-14-strong text-secondary hover:text-strong focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-500 rounded`,children:`Technical details and cache`}),(0,w.jsxs)(`div`,{className:`mt-4`,children:[(0,w.jsx)(`p`,{className:`text-copy-14 text-secondary mb-3`,children:`Cached releases support project re-extracts. Pruning a release does not remove saved backlink results.`}),(0,w.jsx)(x,{className:`surface-card overflow-hidden`,children:(0,w.jsxs)(`table`,{className:`w-full text-label-14`,children:[(0,w.jsx)(`thead`,{children:(0,w.jsxs)(`tr`,{className:`border-b border-base text-left text-label-12 normal-case text-faint`,children:[(0,w.jsx)(`th`,{className:`px-4 py-2 text-label-12-strong`,children:`Release`}),(0,w.jsx)(`th`,{className:`px-4 py-2 text-label-12-strong`,children:`Sync status`}),(0,w.jsx)(`th`,{className:`px-4 py-2 text-right text-label-12-strong`,children:`Size`}),(0,w.jsx)(`th`,{className:`px-4 py-2 text-label-12-strong`,children:`Last used`}),!y()&&(0,w.jsx)(`th`,{className:`px-4 py-2 text-label-12-strong sr-only`,children:`Actions`})]})}),(0,w.jsxs)(`tbody`,{children:[M.map(e=>(0,w.jsxs)(`tr`,{className:`border-b border-mono-900 last:border-0`,children:[(0,w.jsx)(`td`,{className:`px-4 py-2 text-strong`,children:(0,w.jsx)(`code`,{children:e.release})}),(0,w.jsx)(`td`,{className:`px-4 py-2`,children:e.syncStatus?(0,w.jsx)(_,{tone:O(e.syncStatus),children:e.syncStatus}):(0,w.jsx)(`span`,{className:`text-faint`,children:`—`})}),(0,w.jsx)(`td`,{className:`px-4 py-2 text-right text-secondary tabular-nums`,children:E(e.bytes)}),(0,w.jsx)(`td`,{className:`px-4 py-2 text-secondary`,children:D(e.lastUsedAt)}),!y()&&(0,w.jsx)(`td`,{className:`px-4 py-2 text-right`,children:(0,w.jsxs)(`div`,{className:`inline-flex items-center gap-1`,children:[(0,w.jsxs)(g,{type:`button`,variant:`outline`,size:`sm`,onClick:()=>{$(e.release)},children:[(0,w.jsx)(r,{className:`h-4 w-4 mr-1.5`,"aria-hidden":!0}),`Prune`]}),(0,w.jsx)(S,{label:`What does Prune do?`,placement:`top`,children:`Deletes the ~16 GB cache for this release from disk. Backlink results already in SQLite remain untouched. To re-run extracts against this release, you’d have to sync it again (another ~16 GB download).`})]})})]},e.release)),M.length===0&&(0,w.jsx)(`tr`,{children:(0,w.jsx)(`td`,{className:`px-4 py-4 text-label-14 text-muted`,colSpan:y()?4:5,children:`No cached releases on this machine. If you ran a sync from a different machine (or deleted the cache), the backlink data is still in the database — but you’ll need to re-sync a release to run new extracts.`})})]})]})}),(0,w.jsx)(`p`,{className:`mt-3 text-copy-13 text-muted`,children:`Common Crawl files are stored locally. DuckDB reads them; Canonry stores results in its normal database.`})]})]}),A.length>1&&(0,w.jsxs)(`section`,{className:`page-section-divider`,children:[(0,w.jsx)(`div`,{className:`section-head section-head-inline`,children:(0,w.jsxs)(`div`,{children:[(0,w.jsx)(`p`,{className:`eyebrow eyebrow-soft`,children:`History`}),(0,w.jsx)(`h2`,{children:`Past release syncs`})]})}),(0,w.jsx)(x,{className:`surface-card overflow-hidden`,children:(0,w.jsxs)(`table`,{className:`w-full text-label-14`,children:[(0,w.jsx)(`thead`,{children:(0,w.jsxs)(`tr`,{className:`border-b border-base text-left text-label-12 normal-case text-faint`,children:[(0,w.jsx)(`th`,{className:`px-4 py-2 text-label-12-strong`,children:`Release`}),(0,w.jsx)(`th`,{className:`px-4 py-2 text-label-12-strong`,children:`Status`}),(0,w.jsx)(`th`,{className:`px-4 py-2 text-right text-label-12-strong`,children:`Projects`}),(0,w.jsx)(`th`,{className:`px-4 py-2 text-right text-label-12-strong`,children:`Rows`}),(0,w.jsx)(`th`,{className:`px-4 py-2 text-label-12-strong`,children:`Finished`})]})}),(0,w.jsx)(`tbody`,{children:A.map(e=>(0,w.jsxs)(`tr`,{className:`border-b border-mono-900 last:border-0`,children:[(0,w.jsx)(`td`,{className:`px-4 py-2 text-strong`,children:(0,w.jsx)(`code`,{children:e.release})}),(0,w.jsx)(`td`,{className:`px-4 py-2`,children:(0,w.jsx)(_,{tone:O(e.status),children:e.status})}),(0,w.jsx)(`td`,{className:`px-4 py-2 text-right text-secondary tabular-nums`,children:e.projectsProcessed??`—`}),(0,w.jsx)(`td`,{className:`px-4 py-2 text-right text-secondary tabular-nums`,children:e.domainsDiscovered??`—`}),(0,w.jsx)(`td`,{className:`px-4 py-2 text-secondary`,children:D(e.queryFinishedAt??e.updatedAt)})]},e.id))})]})})]})]})}export{k as BacklinksPage};
1
+ import{r as e}from"./rolldown-runtime-XKyaHtl9.js";import{H as t,V as n,c as r,ct as i,s as a,w as o}from"./vendor-lucide-Bo69m9KC.js";import{r as s}from"./vendor-markdown-BiRP51B0.js";import{H as c,L as l,R as u,U as d,a as f,dn as p,jt as m,q as h,r as g,t as _,vt as v,wt as y}from"./ToneBadge-ht0n6RUH.js";import{t as b}from"./async-handler-Bj3TnIRw.js";import{t as x}from"./card-Be_eN_-Q.js";import{t as S}from"./Hint-uoVNfr0U.js";var C=e(i(),1),w=s(),T=`https://commoncrawl.org/web-graphs`;function E(e){return e==null?`—`:e>=0xe8d4a51000?`${(e/0xe8d4a51000).toFixed(1)} TB`:e>=1e9?`${(e/1e9).toFixed(1)} GB`:e>=1e6?`${(e/1e6).toFixed(1)} MB`:e>=1e3?`${(e/1e3).toFixed(1)} KB`:`${e} B`}function D(e){if(!e)return`—`;let t=Date.now()-new Date(e).getTime(),n=Math.floor(t/6e4);if(n<1)return`just now`;if(n<60)return`${n}m ago`;let r=Math.floor(n/60);return r<24?`${r}h ago`:`${Math.floor(r/24)}d ago`}function O(e){switch(e){case`ready`:return`positive`;case`failed`:return`negative`;case`downloading`:case`querying`:case`queued`:return`caution`}}function k(){let[e,i]=(0,C.useState)(null),[s,k]=(0,C.useState)(null),[A,j]=(0,C.useState)([]),[M,N]=(0,C.useState)([]),[P,F]=(0,C.useState)(null),[I,L]=(0,C.useState)(!0),[R,z]=(0,C.useState)(!1),[B,V]=(0,C.useState)(!1),[H,U]=(0,C.useState)(``),[W,G]=(0,C.useState)(!1),[K,q]=(0,C.useState)(null),[J,Y]=(0,C.useState)(null),X=(0,C.useCallback)(async()=>{L(!0),q(null);try{let[e,t,n,r,a]=await Promise.all([l(),d().catch(()=>null),h().catch(()=>[]),u().catch(()=>[]),c().catch(()=>null)]);i(e),k(t),j(n),N(r),F(a)}catch(e){q(e instanceof Error?e.message:`Failed to load backlinks status`)}finally{L(!1)}},[]);(0,C.useEffect)(()=>{X()},[X]);async function Z(){z(!0),q(null),Y(null);try{let e=await v();Y(e.alreadyPresent?`DuckDB already installed (${e.version}).`:`Installed DuckDB ${e.version}.`),await X()}catch(e){q(e instanceof Error?e.message:`Failed to install DuckDB`)}finally{z(!1)}}async function Q(){let e=H.trim()||void 0;V(!0),q(null),Y(null);try{let t=await p(e);Y(e?`Queued sync for ${t.release}. Download + query runs in the background.`:`Queued sync for auto-discovered release ${t.release}. Download + query runs in the background.`),U(``),G(!1),await X()}catch(e){e instanceof f&&e.code===`MISSING_DEPENDENCY`?q(`DuckDB is not installed. Install it first.`):q(e instanceof Error?e.message:`Failed to trigger sync`)}finally{V(!1)}}async function $(e){q(null),Y(null);try{await m(e),Y(`Pruned cached release ${e}.`),await X()}catch(e){q(e instanceof Error?e.message:`Failed to prune release`)}}let ee=s?.status===`ready`&&M.every(e=>e.release!==s.release);return(0,w.jsxs)(`div`,{className:`page-container`,children:[(0,w.jsx)(`div`,{className:`page-header`,children:(0,w.jsxs)(`div`,{className:`page-header-left`,children:[(0,w.jsx)(`h1`,{className:`page-title`,children:`Backlink data`}),(0,w.jsx)(`p`,{className:`page-subtitle`,children:`Manage the shared backlink source and review its latest data.`})]})}),K&&(0,w.jsx)(x,{className:`surface-card p-4 mb-4 bg-negative-soft`,children:(0,w.jsx)(`p`,{className:`text-copy-14 text-negative`,children:K})}),J&&(0,w.jsx)(x,{className:`surface-card p-4 mb-4 bg-positive-soft`,children:(0,w.jsx)(`p`,{className:`text-copy-14 text-positive`,children:J})}),(0,w.jsxs)(`section`,{className:`page-section-divider`,children:[(0,w.jsxs)(`div`,{className:`section-head section-head-inline`,children:[(0,w.jsxs)(`div`,{children:[(0,w.jsx)(`p`,{className:`eyebrow eyebrow-soft`,children:`Readiness`}),(0,w.jsx)(`h2`,{children:`Backlink source`})]}),e?.duckdbInstalled?(0,w.jsx)(_,{tone:`positive`,children:`Installed`}):(0,w.jsx)(_,{tone:`caution`,children:`Not installed`})]}),(0,w.jsx)(x,{className:`surface-card p-4`,children:I?(0,w.jsx)(`p`,{className:`text-copy-14 text-muted`,children:`Checking…`}):e?.duckdbInstalled?(0,w.jsx)(`p`,{className:`text-copy-14 text-secondary`,children:`Ready to sync a Common Crawl release.`}):(0,w.jsxs)(`div`,{className:`flex items-center justify-between gap-4`,children:[(0,w.jsx)(`p`,{className:`text-copy-14 text-secondary`,children:`Install the local query engine to enable release syncs.`}),!y()&&(0,w.jsxs)(g,{type:`button`,size:`sm`,disabled:R,onClick:b(Z),children:[(0,w.jsx)(t,{className:`h-4 w-4 mr-1.5`,"aria-hidden":!0}),R?`Installing…`:`Install DuckDB`]})]})})]}),(0,w.jsxs)(`section`,{className:`page-section-divider`,children:[(0,w.jsxs)(`div`,{className:`section-head section-head-inline`,children:[(0,w.jsxs)(`div`,{children:[(0,w.jsx)(`p`,{className:`eyebrow eyebrow-soft`,children:`Latest sync`}),(0,w.jsxs)(`h2`,{className:`flex items-center gap-2`,children:[`Release sync`,(0,w.jsx)(S,{label:`What is a release sync?`,children:`A release sync downloads one Common Crawl dump (~16 GB) and extracts backlinks for every project in this workspace in one pass. This is the heavy job — subsequent per-project re-runs skip the download and just re-query the cached files.`})]})]}),s&&(0,w.jsx)(_,{tone:O(s.status),children:s.status})]}),(0,w.jsxs)(x,{className:`surface-card p-5`,children:[s?(0,w.jsxs)(`div`,{className:`space-y-2 text-label-14`,children:[(0,w.jsxs)(`p`,{className:`text-strong`,children:[`Release `,(0,w.jsx)(`code`,{className:`text-neutral`,children:s.release})]}),s.phaseDetail&&(0,w.jsx)(`p`,{className:`text-secondary`,children:s.phaseDetail}),(0,w.jsxs)(`div`,{className:`grid grid-cols-2 md:grid-cols-4 gap-4 text-label-12 text-muted pt-2`,children:[(0,w.jsxs)(`div`,{children:[(0,w.jsx)(`p`,{className:`text-faint normal-case text-label-12`,children:`Projects`}),(0,w.jsx)(`p`,{className:`text-neutral mt-0.5`,children:s.projectsProcessed??`—`})]}),(0,w.jsxs)(`div`,{children:[(0,w.jsxs)(`p`,{className:`text-faint normal-case text-label-12 flex items-center gap-1`,children:[`Rows`,(0,w.jsx)(S,{label:`What are rows?`,children:`Total number of (project, referring domain) pairs persisted in SQLite from this sync, across every project in the workspace.`})]}),(0,w.jsx)(`p`,{className:`text-neutral mt-0.5`,children:s.domainsDiscovered??`—`})]}),(0,w.jsxs)(`div`,{children:[(0,w.jsx)(`p`,{className:`text-faint normal-case text-label-12`,children:`Started`}),(0,w.jsx)(`p`,{className:`text-neutral mt-0.5`,children:D(s.downloadStartedAt??s.createdAt)})]}),(0,w.jsxs)(`div`,{children:[(0,w.jsx)(`p`,{className:`text-faint normal-case text-label-12`,children:`Finished`}),(0,w.jsx)(`p`,{className:`text-neutral mt-0.5`,children:D(s.queryFinishedAt)})]})]}),s.error&&(0,w.jsx)(`p`,{className:`text-copy-14 text-negative-400 pt-2`,children:s.error})]}):(0,w.jsx)(`p`,{className:`text-copy-14 text-muted`,children:`No release sync has run in this workspace yet.`}),ee&&(0,w.jsx)(`div`,{className:`mt-4 rounded border border-caution-800/60 bg-caution-950/20 p-3`,children:(0,w.jsxs)(`div`,{className:`flex items-start gap-2`,children:[(0,w.jsx)(a,{className:`h-4 w-4 text-caution-400 shrink-0 mt-0.5`,"aria-hidden":!0}),(0,w.jsxs)(`div`,{className:`text-label-14 text-neutral`,children:[(0,w.jsx)(`p`,{className:`text-copy-14-strong text-caution-200`,children:`Cached files for this release are missing.`}),(0,w.jsx)(`p`,{className:`mt-1 text-secondary`,children:`Sync this release again, or sync the newest release, before refreshing individual projects.`})]})]})}),(0,w.jsxs)(`div`,{className:`mt-4 material-base p-3`,children:[(0,w.jsxs)(`div`,{className:`flex items-start justify-between gap-3 mb-3`,children:[(0,w.jsxs)(`div`,{children:[(0,w.jsx)(`p`,{className:`text-label-12 normal-case text-muted`,children:`Auto-detected release`}),P?(0,w.jsxs)(`p`,{className:`text-copy-14 text-strong mt-0.5`,children:[(0,w.jsx)(`code`,{className:`text-heading`,children:P.release}),(0,w.jsxs)(`span`,{className:`ml-2 text-label-12 text-muted`,children:[`Vertex `,E(P.vertexBytes),`, edges `,E(P.edgesBytes)]})]}):(0,w.jsx)(`p`,{className:`text-copy-14 text-muted mt-0.5`,children:I?`Checking Common Crawl…`:`Could not find a release. Choose one below.`}),(0,w.jsxs)(`a`,{href:T,target:`_blank`,rel:`noopener noreferrer`,className:`mt-1 inline-flex items-center gap-1 text-label-12 text-secondary hover:text-strong focus:text-strong focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-500 rounded`,children:[`Browse all Common Crawl web-graph releases`,(0,w.jsx)(n,{className:`h-3 w-3`,"aria-hidden":!0})]})]}),!y()&&(0,w.jsxs)(`div`,{className:`flex items-center gap-2 shrink-0`,children:[(0,w.jsxs)(g,{type:`button`,size:`sm`,disabled:B||!e?.duckdbInstalled||!P&&!H.trim(),onClick:b(Q),children:[(0,w.jsx)(o,{className:`h-4 w-4 mr-1.5`,"aria-hidden":!0}),B?`Queuing…`:`Run sync`]}),(0,w.jsxs)(S,{label:`What does Run sync do?`,children:[(0,w.jsxs)(`span`,{className:`block`,children:[`Downloads the auto-detected (or chosen) Common Crawl release (~16 GB) to`,` `,(0,w.jsx)(`code`,{className:`text-neutral`,children:`~/.canonry/cache/commoncrawl/`}),`, then runs a single DuckDB query that extracts referring domains for every project in this workspace.`]}),(0,w.jsxs)(`span`,{className:`mt-2 block text-secondary`,children:[`First time for a release: `,(0,w.jsx)(`span`,{className:`text-strong`,children:`~10–20 min download + ~5 min query`}),`. Re-running the same release later: `,(0,w.jsx)(`span`,{className:`text-strong`,children:`skips download, just re-queries`}),` (~5 min).`]})]})]})]}),y()?null:W?(0,w.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,w.jsx)(`input`,{type:`text`,className:`flex-1 min-w-[240px] rounded border border-strong bg-transparent px-2.5 py-1.5 text-label-14 text-strong placeholder-mono-400 focus:border-mono-500 focus:outline-none`,placeholder:`cc-main-2026-jan-feb-mar`,value:H,onChange:e=>U(e.target.value),disabled:B,autoFocus:!0}),(0,w.jsx)(`button`,{type:`button`,className:`text-button-14 text-secondary hover:text-neutral focus:text-neutral focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-500 rounded`,onClick:()=>{U(``),G(!1)},disabled:B,children:`Cancel`})]}):(0,w.jsx)(`button`,{type:`button`,className:`text-button-14 text-secondary hover:text-neutral focus:text-neutral focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-500 rounded`,onClick:()=>G(!0),disabled:B,children:`Use a different release →`})]}),!e?.duckdbInstalled&&(0,w.jsx)(`p`,{className:`text-copy-14 text-secondary mt-2`,children:`Install DuckDB first to enable sync.`})]})]}),(0,w.jsxs)(`details`,{className:`page-section-divider group`,children:[(0,w.jsx)(`summary`,{className:`cursor-pointer text-label-14-strong text-secondary hover:text-strong focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-500 rounded`,children:`Technical details and cache`}),(0,w.jsxs)(`div`,{className:`mt-4`,children:[(0,w.jsx)(`p`,{className:`text-copy-14 text-secondary mb-3`,children:`Cached releases support project re-extracts. Pruning a release does not remove saved backlink results.`}),(0,w.jsx)(x,{className:`surface-card overflow-hidden`,children:(0,w.jsxs)(`table`,{className:`w-full text-label-14`,children:[(0,w.jsx)(`thead`,{children:(0,w.jsxs)(`tr`,{className:`border-b border-base text-left text-label-12 normal-case text-faint`,children:[(0,w.jsx)(`th`,{className:`px-4 py-2 text-label-12-strong`,children:`Release`}),(0,w.jsx)(`th`,{className:`px-4 py-2 text-label-12-strong`,children:`Sync status`}),(0,w.jsx)(`th`,{className:`px-4 py-2 text-right text-label-12-strong`,children:`Size`}),(0,w.jsx)(`th`,{className:`px-4 py-2 text-label-12-strong`,children:`Last used`}),!y()&&(0,w.jsx)(`th`,{className:`px-4 py-2 text-label-12-strong sr-only`,children:`Actions`})]})}),(0,w.jsxs)(`tbody`,{children:[M.map(e=>(0,w.jsxs)(`tr`,{className:`border-b border-mono-900 last:border-0`,children:[(0,w.jsx)(`td`,{className:`px-4 py-2 text-strong`,children:(0,w.jsx)(`code`,{children:e.release})}),(0,w.jsx)(`td`,{className:`px-4 py-2`,children:e.syncStatus?(0,w.jsx)(_,{tone:O(e.syncStatus),children:e.syncStatus}):(0,w.jsx)(`span`,{className:`text-faint`,children:`—`})}),(0,w.jsx)(`td`,{className:`px-4 py-2 text-right text-secondary tabular-nums`,children:E(e.bytes)}),(0,w.jsx)(`td`,{className:`px-4 py-2 text-secondary`,children:D(e.lastUsedAt)}),!y()&&(0,w.jsx)(`td`,{className:`px-4 py-2 text-right`,children:(0,w.jsxs)(`div`,{className:`inline-flex items-center gap-1`,children:[(0,w.jsxs)(g,{type:`button`,variant:`outline`,size:`sm`,onClick:()=>{$(e.release)},children:[(0,w.jsx)(r,{className:`h-4 w-4 mr-1.5`,"aria-hidden":!0}),`Prune`]}),(0,w.jsx)(S,{label:`What does Prune do?`,placement:`top`,children:`Deletes the ~16 GB cache for this release from disk. Backlink results already in SQLite remain untouched. To re-run extracts against this release, you’d have to sync it again (another ~16 GB download).`})]})})]},e.release)),M.length===0&&(0,w.jsx)(`tr`,{children:(0,w.jsx)(`td`,{className:`px-4 py-4 text-label-14 text-muted`,colSpan:y()?4:5,children:`No cached releases on this machine. If you ran a sync from a different machine (or deleted the cache), the backlink data is still in the database — but you’ll need to re-sync a release to run new extracts.`})})]})]})}),(0,w.jsx)(`p`,{className:`mt-3 text-copy-13 text-muted`,children:`Common Crawl files are stored locally. DuckDB reads them; Canonry stores results in its normal database.`})]})]}),A.length>1&&(0,w.jsxs)(`section`,{className:`page-section-divider`,children:[(0,w.jsx)(`div`,{className:`section-head section-head-inline`,children:(0,w.jsxs)(`div`,{children:[(0,w.jsx)(`p`,{className:`eyebrow eyebrow-soft`,children:`History`}),(0,w.jsx)(`h2`,{children:`Past release syncs`})]})}),(0,w.jsx)(x,{className:`surface-card overflow-hidden`,children:(0,w.jsxs)(`table`,{className:`w-full text-label-14`,children:[(0,w.jsx)(`thead`,{children:(0,w.jsxs)(`tr`,{className:`border-b border-base text-left text-label-12 normal-case text-faint`,children:[(0,w.jsx)(`th`,{className:`px-4 py-2 text-label-12-strong`,children:`Release`}),(0,w.jsx)(`th`,{className:`px-4 py-2 text-label-12-strong`,children:`Status`}),(0,w.jsx)(`th`,{className:`px-4 py-2 text-right text-label-12-strong`,children:`Projects`}),(0,w.jsx)(`th`,{className:`px-4 py-2 text-right text-label-12-strong`,children:`Rows`}),(0,w.jsx)(`th`,{className:`px-4 py-2 text-label-12-strong`,children:`Finished`})]})}),(0,w.jsx)(`tbody`,{children:A.map(e=>(0,w.jsxs)(`tr`,{className:`border-b border-mono-900 last:border-0`,children:[(0,w.jsx)(`td`,{className:`px-4 py-2 text-strong`,children:(0,w.jsx)(`code`,{children:e.release})}),(0,w.jsx)(`td`,{className:`px-4 py-2`,children:(0,w.jsx)(_,{tone:O(e.status),children:e.status})}),(0,w.jsx)(`td`,{className:`px-4 py-2 text-right text-secondary tabular-nums`,children:e.projectsProcessed??`—`}),(0,w.jsx)(`td`,{className:`px-4 py-2 text-right text-secondary tabular-nums`,children:e.domainsDiscovered??`—`}),(0,w.jsx)(`td`,{className:`px-4 py-2 text-secondary`,children:D(e.queryFinishedAt??e.updatedAt)})]},e.id))})]})})]})]})}export{k as BacklinksPage};
@@ -1 +1 @@
1
- import{r as e}from"./vendor-markdown-BiRP51B0.js";import{t}from"./AuditHistoryPanel-YrEggsyA.js";var n=e();function r(){return(0,n.jsxs)(`div`,{className:`page-container`,children:[(0,n.jsx)(`div`,{className:`page-header`,children:(0,n.jsxs)(`div`,{className:`page-header-left`,children:[(0,n.jsx)(`h1`,{className:`page-title`,children:`History`}),(0,n.jsx)(`p`,{className:`page-subtitle`,children:`Changes to projects and settings.`})]})}),(0,n.jsx)(t,{})]})}export{r as HistoryPage};
1
+ import{r as e}from"./vendor-markdown-BiRP51B0.js";import{t}from"./AuditHistoryPanel-DAotR_zD.js";var n=e();function r(){return(0,n.jsxs)(`div`,{className:`page-container`,children:[(0,n.jsx)(`div`,{className:`page-header`,children:(0,n.jsxs)(`div`,{className:`page-header-left`,children:[(0,n.jsx)(`h1`,{className:`page-title`,children:`History`}),(0,n.jsx)(`p`,{className:`page-subtitle`,children:`Changes to projects and settings.`})]})}),(0,n.jsx)(t,{})]})}export{r as HistoryPage};