@ainyc/canonry 4.182.3 → 4.183.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 (28) hide show
  1. package/README.md +139 -104
  2. package/assets/agent-workspace/skills/aero/references/orchestration.md +1 -1
  3. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +1 -1
  4. package/assets/assets/{AuditHistoryPanel-1_FHpzj-.js → AuditHistoryPanel-ClwYugEs.js} +1 -1
  5. package/assets/assets/{BacklinksPage-045za10_.js → BacklinksPage-DaEn1w9_.js} +1 -1
  6. package/assets/assets/{HistoryPage-36K_6asu.js → HistoryPage-B4WWIXmx.js} +1 -1
  7. package/assets/assets/{MeasurementPropertyPage-B4DJGoY6.js → MeasurementPropertyPage-DFvelKfS.js} +1 -1
  8. package/assets/assets/{ProjectPage-CshC68Ne.js → ProjectPage-D3AuGcau.js} +8 -8
  9. package/assets/assets/{RunRow-U2CN755e.js → RunRow-0HuBq5KN.js} +1 -1
  10. package/assets/assets/{RunsPage-D0CtU4d-.js → RunsPage-DhdjoIdK.js} +1 -1
  11. package/assets/assets/{SettingsPage-zMWieTPK.js → SettingsPage-XzHCmYz4.js} +1 -1
  12. package/assets/assets/{SiteHealthSection-TpQPu_Uj.js → SiteHealthSection-BODUx0bX.js} +3 -3
  13. package/assets/assets/{TrafficPage-DfLUt-64.js → TrafficPage-2AKICLH_.js} +1 -1
  14. package/assets/assets/{TrafficSourceDetailPage-DbjNNWSt.js → TrafficSourceDetailPage-CYVG7qZ0.js} +1 -1
  15. package/assets/assets/{extract-error-message-CWqz1QPC.js → extract-error-message-CtGLEBMO.js} +1 -1
  16. package/assets/assets/{index-Brb7M_6G.js → index-B64WXjg8.js} +2 -2
  17. package/assets/assets/{react-sigma_core.esm.min-Drqprr9L.js → react-sigma_core.esm.min-D5Honb13.js} +1 -1
  18. package/assets/index.html +1 -1
  19. package/dist/{chunk-K45YKXE4.js → chunk-ISONU3FA.js} +5 -3
  20. package/dist/{chunk-BDJT7XZL.js → chunk-OF3PKBW4.js} +8 -6
  21. package/dist/{chunk-I3KSOMJA.js → chunk-RQSSKN5F.js} +2 -2
  22. package/dist/{chunk-FVC7PGYQ.js → chunk-VFQPW2VX.js} +3 -3
  23. package/dist/{chunk-DY5ZDKQW.js → chunk-Z2HTLTQV.js} +3 -4
  24. package/dist/cli.js +5 -5
  25. package/dist/index.js +4 -4
  26. package/dist/{intelligence-service-HGA553GT.js → intelligence-service-4XQBUFBU.js} +2 -2
  27. package/dist/mcp.js +3 -3
  28. package/package.json +13 -13
package/README.md CHANGED
@@ -1,70 +1,31 @@
1
1
  # Canonry <img src="https://raw.githubusercontent.com/Canonry/canonry/main/apps/web/public/favicon-32.png" alt="Canonry canary icon" width="24" />
2
2
 
3
- [![npm version](https://img.shields.io/npm/v/@canonry/canonry)](https://www.npmjs.com/package/@canonry/canonry) [![Node.js >= 22.14](https://img.shields.io/badge/node-%3E%3D22.14-brightgreen)](https://nodejs.org)
3
+ [![npm version](https://img.shields.io/npm/v/@canonry/canonry)](https://www.npmjs.com/package/@canonry/canonry)
4
4
 
5
- **Give your agent the evidence, tools, and skills to run AEO.**
5
+ Canonry is an **agent-first, open-source AEO operating platform.** Track AI visibility over time, investigate changes across search and traffic, and measure progress after your agent acts.
6
6
 
7
- Canonry is an **agent-first, open-source AEO operating platform.**
7
+ **Self-hosted, with your own provider keys.** Your dashboard and agent review the same project evidence.
8
+
9
+ [Quick start](#quick-start) · [Evidence](#explore-the-evidence) · [Actions](#act-on-the-evidence) · [Integrations](#integrations) · [Docs](#documentation)
8
10
 
9
11
  <p align="center">
10
12
  <a href="https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/measure-act.svg">
11
- <img src="https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/measure-act.svg" alt="Supported external sources feed Canonry. Canonry stores project evidence in SQLite for your agent, dashboard, and client reports. Your agent combines its own execution tools with Canonry's WordPress and indexing workflows. Webhooks connect Discord and other systems." width="94%" />
13
+ <img src="https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/measure-act.svg" alt="AI, search, analytics, and traffic sources feed Canonry on schedules or on demand. Historical evidence across multiple domains and portfolios runs from baseline through the latest checks. Your agent reads evidence and sends commands to Canonry tools, while using its own tools for code and content. Canonry publishes to WordPress, submits to Google and Bing, and supplies the dashboard, reports, and webhooks. Site changes loop back into measurement." width="100%" />
12
14
  </a>
13
15
  </p>
14
16
 
15
- Connect the sources you use. Schedule syncs and checks, or run them on demand.
16
-
17
- **Connect your agent:** [Agent Plugin](docs/plugins.md) · [MCP](docs/mcp.md) · CLI · REST API
18
-
19
- - **Track AI visibility:** Brand mentions and citations across answer engines.
20
- - **Compare competitors:** Citation gaps and the domains and pages getting cited.
21
- - **Measure complex portfolios:** Properties and market groups, with questions, engines, models, and locations tailored per property.
22
- - **Investigate what changed:** Combine search and local performance, server-side crawler visits, AI page fetches, referrals, conversion evidence, and technical audits.
23
- - **Improve your site:** Your agent uses its own coding tools alongside Canonry's content, WordPress publishing, schema, and indexing workflows.
24
- - **Report and follow up:** Generate client reports, schedule data syncs and checks, and send alerts to Discord or your own systems.
25
-
26
- **Self-hosted, with your own provider keys.** Use the dashboard to review the same evidence and recommendations as your agent.
27
-
28
- `npm install -g @canonry/canonry`
29
-
30
- **Give your agent a job:**
31
-
32
- > Review what changed across search, local visibility, AI citations, and server-side traffic. Find where competitors get cited and we don't. Use that evidence and the site audit to propose the next changes. Prepare a pull request or WordPress draft for review, then report the results and next steps to the client.
33
-
34
- Your agent coordinates the work through Canonry and its own tools. After changes ship, rerun the relevant checks to measure progress.
35
-
36
- ### Compare competitors and measure complex portfolios
37
-
38
- - **Competitor comparisons:** Compare brand mentions and citations across tracked questions and answer engines. Find questions where competitors are cited and your site is absent.
39
- - **Who gets cited:** [Rank cited domains](skills/canonry/references/canonry-cli.md#cited-source-rankings-cnry-sources) and inspect exact URLs, including your pages, competitors, aggregators, and publishers.
40
- - **Advanced Measurement:** Build [versioned measurement plans](docs/mcp.md#tool-surface) for portfolios of locations, products, or site sections. Assign questions, engines, models, and locations per property, separate branded and non-brand coverage, and organize overlapping market groups with their own competitors. Compare results across properties, groups, answer engines, and locations, then drill down to exact answers and cited sources.
41
-
42
- ### Built-in integrations and workflows
43
-
44
- - **Answer engines:** Canonry measures Gemini, ChatGPT, Claude, Perplexity, and OpenAI-compatible local models.
45
- - **Agent workflows:** Use the [MCP adapter](docs/mcp.md), [Agent Plugin](docs/plugins.md), [external webhooks](skills/canonry/references/canonry-cli.md#agent), or built-in [Aero](skills/aero/SKILL.md).
46
- - **Search and analytics:** Connect [Google Search Console](docs/google-search-console-setup.md), [Google Analytics 4](docs/google-analytics-setup.md), and [Bing Webmaster Tools](docs/bing-webmaster-setup.md).
47
- - **Server traffic:** Inspect recorded crawler visits, AI page fetches, and referral arrivals from [Cloudflare, Cloud Run, Vercel, and WordPress](skills/canonry/references/server-side-traffic.md).
48
- - **Backlinks:** Query [Common Crawl hyperlink releases](skills/canonry/references/canonry-cli.md#backlinks-common-crawl) locally with DuckDB, and sync new releases on a schedule.
49
- - **Conversion measurement:** Audit [Google Ads and Google Tag Manager](docs/google-marketing.md) with read-only snapshots and declared conversion contracts.
50
- - **Local presence:** Connect [Google Business Profile](skills/canonry/references/google-business-profile.md) for search terms, performance, lodging data, and booking actions.
51
- - **Publishing and indexing:** Publish through [WordPress](docs/wordpress-setup.md), generate JSON-LD, and [submit sitemaps or URLs for indexing](skills/canonry/references/indexing.md).
52
- - **Client reporting:** Generate [client-ready HTML reports or JSON evidence](skills/canonry/references/canonry-cli.md#reports), use the report API/MCP tools, or build your own reporting pipeline.
53
- - **Schedules and alerts:** Configure [data syncs and checks](skills/canonry/references/canonry-cli.md#scheduling--notifications) independently; send webhook alerts to Discord, Slack, or your own systems.
54
- - **ChatGPT Ads:** Use the [ads tools](docs/mcp.md#tool-surface) to measure campaigns, create and update paused campaign trees, activate approved trees, and reconcile operation receipts.
55
-
56
- The dashboard, CLI, and agent tools share the same project API.
17
+ ## Quick start
57
18
 
58
19
  <a id="or-use-any-shell-capable-coding-agent"></a>
59
20
 
60
- ## Start with your agent
21
+ ### Start with your agent
61
22
 
62
- Connect the [Agent Plugin](docs/plugins.md) or [MCP adapter](docs/mcp.md) to your agent, or use built-in [Aero](skills/aero/SKILL.md). For any shell-capable agent, copy the setup request below.
23
+ Connect the [Agent Plugin](docs/plugins.md) or [MCP adapter](docs/mcp.md) to your agent. When enabled, built-in [Aero](skills/aero/SKILL.md) reviews evidence and wakes after completed runs.
63
24
 
64
- <details>
65
- <summary>Copy the Site Health-first setup request</summary>
25
+ For a new installation, give your agent this setup request:
66
26
 
67
- <br />
27
+ <details>
28
+ <summary>Copy the first-time setup request</summary>
68
29
 
69
30
  ```text
70
31
  Help me set up Canonry for my public site.
@@ -87,11 +48,9 @@ If a Canonry installation or connected plugin/MCP is available, use it. Do not c
87
48
 
88
49
  </details>
89
50
 
90
- ## Get a Page Health baseline
51
+ <a id="get-a-page-health-baseline"></a>
91
52
 
92
- ![Canonry Site Map graph](https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/dashboard.png)
93
-
94
- *Map crawlable pages and the internal links connecting them.*
53
+ ### Start locally
95
54
 
96
55
  1. Install Canonry.
97
56
 
@@ -99,43 +58,53 @@ If a Canonry installation or connected plugin/MCP is available, use it. Do not c
99
58
  npm install -g @canonry/canonry
100
59
  ```
101
60
 
102
- 2. Create the local configuration, SQLite database, and full-instance API key.
61
+ 2. Initialize Canonry.
103
62
 
104
63
  ```bash
105
64
  cnry bootstrap
106
65
  ```
107
66
 
108
- Canonry stores `config.yaml` and `data.db` in `~/.canonry` by default. Set
109
- `CANONRY_CONFIG_DIR` before `bootstrap`, `serve`, and later CLI commands to
110
- keep an install in another private directory; use the same value every time.
111
-
112
67
  3. Start Canonry.
113
68
 
114
69
  ```bash
115
70
  cnry serve
116
71
  ```
117
72
 
118
- 4. Open [http://127.0.0.1:4100/setup](http://127.0.0.1:4100/setup). If prompted, create a dashboard password.
73
+ 4. Open [http://127.0.0.1:4100/setup](http://127.0.0.1:4100/setup) and follow the setup to scan your site.
74
+
75
+ The crawl saves a Page Health baseline. AI Visibility is optional and has a separate setup.
119
76
 
120
- 5. Enter your domain and approve the public-site crawl. This crawl creates a persisted Page Health baseline. AI Visibility is optional.
77
+ <details>
78
+ <summary>Scan and read results from the terminal</summary>
121
79
 
122
- 6. To use the terminal instead, keep `cnry serve` running. Open a second terminal and run:
80
+ Keep `cnry serve` active. In a second terminal, create a project and start a bounded scan:
123
81
 
124
- ```bash
125
- cnry project create my-site --domain example.com --country US --language en
126
- cnry technical-aeo run my-site --max-pages 100 --wait --format json
127
- ```
82
+ ```bash
83
+ cnry project create my-site --domain example.com --country US --language en
84
+ cnry technical-aeo run my-site --max-pages 100 --wait --format json
85
+ ```
128
86
 
129
- 7. Read the run ID and status from the output. If the status is `completed` or `partial`, read evidence from that run:
87
+ Read the run ID and status from the output. If the status is `completed` or `partial`, read evidence from that run:
130
88
 
131
- ```bash
132
- cnry technical-aeo score my-site --run-id <run-id> --format json
133
- cnry technical-aeo pages my-site --run-id <run-id> --sort score-asc --limit 10 --format jsonl
134
- ```
89
+ ```bash
90
+ cnry technical-aeo score my-site --run-id <run-id> --format json
91
+ cnry technical-aeo pages my-site --run-id <run-id> --sort score-asc --limit 10 --format jsonl
92
+ ```
135
93
 
136
- `--wait` polls for up to 15 minutes. If the scan remains active, use the progress command below. If it fails or is cancelled, inspect it with `cnry run show <run-id> --format json`.
94
+ `--wait` polls for up to 15 minutes. If the scan remains active, use `cnry technical-aeo progress <project> --run-id <id> --format json`.
95
+ If the scan fails or is cancelled, read the error with `cnry run show <run-id> --format json`.
137
96
 
138
- ## Add AI Visibility when you need it
97
+ </details>
98
+
99
+ ## Explore the evidence
100
+
101
+ Your agent and dashboard use the same project API. The dashboard makes trends, exact answers, and site findings available for human review.
102
+
103
+ <a id="add-ai-visibility-when-you-need-it"></a>
104
+
105
+ ### AI visibility
106
+
107
+ Track brand mentions and citations over time, by query and answer engine. Find answers that cite competitors but omit your site.
139
108
 
140
109
  ![Canonry AI Visibility mention share trend across answer engines](https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/ai-visibility-trend.png)
141
110
 
@@ -143,7 +112,13 @@ If a Canonry installation or connected plugin/MCP is available, use it. Do not c
143
112
 
144
113
  ![Canonry AI Visibility citation map across queries and answer engines](https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/ai-visibility-diagnostics.png)
145
114
 
146
- *Map citation and answer-mention coverage across every tracked query and engine.*
115
+ The citation map shows mention and citation coverage across queries and engines.
116
+ [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.
117
+
118
+ <details>
119
+ <summary>Configure a provider and run visibility checks</summary>
120
+
121
+ For an existing project, configure a provider with your own key.
147
122
 
148
123
  | Provider | Key source | Environment variable |
149
124
  |---|---|---|
@@ -153,7 +128,7 @@ If a Canonry installation or connected plugin/MCP is available, use it. Do not c
153
128
  | Perplexity | [Perplexity settings](https://www.perplexity.ai/settings/api) | `PERPLEXITY_API_KEY` |
154
129
  | Local model | Any OpenAI-compatible endpoint | `LOCAL_BASE_URL` |
155
130
 
156
- Then add the queries that matter and run a measured sweep:
131
+ Add the queries that matter to your project:
157
132
 
158
133
  ```bash
159
134
  cnry query add my-site "your first query" "your second query"
@@ -161,7 +136,12 @@ cnry run my-site --wait
161
136
  cnry visibility-stats my-site --by-provider
162
137
  ```
163
138
 
164
- ### Why we use model providers' APIs directly (not a router)
139
+ </details>
140
+
141
+ <a id="why-we-use-model-providers-apis-directly-not-a-router"></a>
142
+
143
+ <details>
144
+ <summary>Why Canonry uses direct provider APIs</summary>
165
145
 
166
146
  Canonry uses direct provider APIs to capture answers, citations, and available
167
147
  search details that model routers can omit. This richer data helps us measure
@@ -169,45 +149,100 @@ brand mentions and website citations accurately and explain changes in your
169
149
  visibility.
170
150
  [Read more about accuracy and why we chose models and direct provider API adapters individually](docs/providers/model-selection.md).
171
151
 
172
- ## Technical surface
152
+ </details>
153
+
154
+ <details>
155
+ <summary>Discover questions or run one-off research</summary>
156
+
157
+ **Expand your tracked questions.** Run `cnry discover run <project> --icp "..."`. This does not change the tracked questions.
158
+ Preview a completed session with `cnry discover promote preview <project> <session-id>`. Promote only after approval.
159
+
160
+ **Research without changing tracking.** Run `cnry research run <project> "query one" "query two" --wait`.
161
+ Research saves the answers without adding queries to the tracked questions.
162
+
163
+ </details>
164
+
165
+ <a id="compare-competitors-and-measure-complex-portfolios"></a>
166
+
167
+ ### Portfolios and competitors
168
+
169
+ Use [versioned measurement plans](docs/mcp.md#tool-surface) for portfolios of locations, products, or site sections.
170
+
171
+ - **Properties:** Assign queries, engines, models, and locations to each property. Keep branded and non-brand coverage separate.
172
+ - **Market groups:** Organize overlapping sets of properties, each with its own competitors.
173
+ - **Comparisons:** Compare properties, groups, engines, and locations. Open the exact answers and cited URLs behind each result.
174
+
175
+ ### Search, traffic, and site health
176
+
177
+ Investigate visibility changes with evidence from search and local performance, crawler visits, AI page fetches, referrals, and conversions.
178
+ Site audits show technical findings alongside that evidence.
179
+
180
+ ![Canonry Site Map graph](https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/dashboard.png)
181
+
182
+ *Map crawlable pages and the internal links that connect them.*
173
183
 
174
- | Surface | Use it for |
184
+ ## Act on the evidence
185
+
186
+ Your agent coordinates the work through Canonry and its own tools.
187
+
188
+ - **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).
189
+ - **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.
190
+ - **Report and follow up:** Generate [HTML reports or JSON evidence](skills/canonry/references/canonry-cli.md#reports). Send [webhook alerts](skills/canonry/references/canonry-cli.md#agent) to Discord, Slack, or your systems.
191
+
192
+ **An example job for your connected agent:**
193
+
194
+ > 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.
195
+
196
+ <a id="built-in-integrations-and-workflows"></a>
197
+
198
+ ## Integrations
199
+
200
+ Connect the sources you use. Each integration adds evidence or tools to the same project workflow.
201
+
202
+ | Area | Supported integrations |
175
203
  |---|---|
176
- | **CLI and REST API** | Script project measurements, diagnoses, actions, reports, and schedules. OpenAPI is available at `GET /api/v1/openapi.json`. |
177
- | **MCP and Agent Plugin** | Give Codex, Claude, Cursor, or a custom agent a typed, task-shaped tool surface. |
178
- | **Aero** | When enabled and configured, use the built-in analyst that reviews evidence and wakes after completed runs. |
179
- | **Dashboard** | Review recommendations, inspect evidence, and observe the same project record used by agents. |
204
+ | **AI answers** | ChatGPT · Claude · Gemini · Perplexity · OpenAI-compatible local models |
205
+ | **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) |
206
+ | **Analytics** | [Google Analytics 4](docs/google-analytics-setup.md) |
207
+ | **Conversion measurement** | [Google Ads + Google Tag Manager](docs/google-marketing.md): read-only snapshots and declared conversion contracts |
208
+ | **Server traffic** | [Cloudflare · Vercel · Cloud Run · WordPress](skills/canonry/references/server-side-traffic.md) |
209
+ | **Backlinks** | [Common Crawl](skills/canonry/references/canonry-cli.md#backlinks-common-crawl) hyperlink releases, queried locally with DuckDB |
210
+ | **Publishing and indexing** | [WordPress](docs/wordpress-setup.md) · JSON-LD · [sitemap and URL submissions](skills/canonry/references/indexing.md) |
211
+ | **ChatGPT Ads** | [Campaign measurement, paused campaign editing, approved activation, and operation reconciliation](docs/mcp.md#tool-surface) |
212
+
213
+ <a id="deployment-and-trust-boundary"></a>
214
+ <a id="technical-surface"></a>
215
+
216
+ ## Self-hosting and API
180
217
 
181
- ## Deployment and trust boundary
218
+ Canonry is single-tenant. Run one instance for one operator or team. Keep unrelated teams on separate instances.
182
219
 
183
- Canonry is self-hosted and single-tenant. Run one instance for one operator or team, and isolate unrelated teams on separate instances.
220
+ The CLI and REST API are the primary interfaces. They expose measurements, diagnoses, actions, reports, and schedules.
221
+ OpenAPI is available at `GET /api/v1/openapi.json`.
184
222
 
185
223
  See the [deployment guide](docs/deployment.md) for reverse proxies, daemon mode, Docker, systemd, and Tailscale.
186
224
 
187
- ## If you get stuck
225
+ ## Documentation
226
+
227
+ - **Operate Canonry:** [CLI reference](skills/canonry/references/canonry-cli.md) · [Agent Plugin](docs/plugins.md) · [MCP](docs/mcp.md)
228
+ - **Use built-in Aero:** [Setup and analysis workflows](skills/aero/SKILL.md)
229
+ - **Understand the system:** [Architecture](docs/architecture.md) · [Data model](docs/data-model.md)
230
+ - **Install standalone agent skills:** `cnry skills install` ([guide](skills/canonry/SKILL.md))
231
+ - **Browse all guides:** [Documentation index](docs/README.md)
232
+
233
+ <a id="if-you-get-stuck"></a>
234
+
235
+ <details>
236
+ <summary>Troubleshoot installation, scans, and visibility results</summary>
188
237
 
189
- | Problem | Fix |
238
+ | Problem | Next step |
190
239
  |---|---|
191
- | Site scan is still running | Read exact counters with `cnry technical-aeo progress <project> --run-id <id> --format json`. |
192
- | Site scan failed | Read the error with `cnry run show <run-id> --format json`. Read the last phase and counters with the progress command above. |
240
+ | Site scan is still active | Read exact counters with `cnry technical-aeo progress <project> --run-id <id> --format json`. |
241
+ | Site scan failed | Read the error with `cnry run show <run-id> --format json`. Read the last phase and counters with the progress command. |
193
242
  | 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. |
194
- | Need more query candidates | Run `cnry discover run <project> --icp "..."`. This does not change the basket. Preview a completed session with `cnry discover promote preview <project> <session-id>`. Promote only after approval. |
195
- | Need one-off research | Run `cnry research run <project> "query one" "query two" --wait`. Research does not change the tracked basket. |
196
243
  | `npm install` fails on `node-gyp` | Install build tools for `better-sqlite3` ([guide](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/troubleshooting.md)). |
197
244
 
198
- ## Documentation
199
-
200
- | | |
201
- |---|---|
202
- | **Architecture & data model** | [docs/architecture.md](docs/architecture.md) · [docs/data-model.md](docs/data-model.md) |
203
- | **Aero — built-in agent** | [skills/aero/SKILL.md](skills/aero/SKILL.md) |
204
- | **Agent Plugin — portable core + Codex / Claude adapters** | [docs/plugins.md](docs/plugins.md) |
205
- | **MCP — Claude Desktop / Cursor / Codex** | [docs/mcp.md](docs/mcp.md) |
206
- | **Integrations** | [GSC](docs/google-search-console-setup.md) · [GA4](docs/google-analytics-setup.md) · [Google Ads + GTM](docs/google-marketing.md) · [Bing](docs/bing-webmaster-setup.md) · [Google Business Profile](skills/canonry/references/google-business-profile.md) · [WordPress](docs/wordpress-setup.md) · [Server-side traffic (Cloudflare direct push or Queue pull, Cloud Run, Vercel, WordPress)](skills/canonry/references/server-side-traffic.md) |
207
- | **Deployment** — reverse proxies, Docker, systemd, Tailscale | [docs/deployment.md](docs/deployment.md) |
208
- | **API** | `GET /api/v1/openapi.json` |
209
- | **Standalone skills bundle** for Claude Code / Codex | `cnry skills install` ([details](skills/canonry/SKILL.md)) |
210
- | **All docs** | [docs/README.md](docs/README.md) |
245
+ </details>
211
246
 
212
247
  ## Contributing
213
248
 
@@ -14,7 +14,7 @@ Trigger: First sweep completes for a new project
14
14
  Steps:
15
15
  1. `cnry overview <project> --format json` → mention read first: `queryCounts.mentionRate`, `scores.mention`, `scores.mentionShare`. Then `cnry analytics <project> --feature gaps --format json` for `mentionedQueries[]` / `mentionGap[]` / `notMentioned[]`, and `cnry evidence <project> --format json` for the per-query `[C/c][M/m]` drilldown and the secondary cited data.
16
16
  2. Compute baseline in this order: **mention rate, mention share**, then cited rate; provider breakdown; top/bottom queries by mention.
17
- 3. With explicit operator approval for the crawl and persisted run, `cnry technical-aeo run <project> --wait`, then `cnry technical-aeo score <project> --format json` for site readiness. Use `cnry site-health overview <project> --format json` only to add crawl metadata (root, completeness, budgets, and termination); it never replaces the score. The crawl discovers the in-scope URL inventory from the root, sitemaps, and internal links. The default budgets are 1,000 pages and 100,000 edges; use `--max-pages`, `--max-edges`, or `--max-depth` to tighten them. Dead-link checks remain off unless `--check-dead-links` is explicit. For architecture investigation, use bounded Site Health subgraph/path/changes reads rather than attempting to load the visualization graph. Treat `countAccuracy: "lower-bound"` subgraph counts as minimums, and qualify an incomplete path's unreachable/truncated result with `complete: false` plus `termination`; neither is a site-wide conclusion. Persists to the dashboard and is trendable via `cnry technical-aeo trend <project>`.
17
+ 3. With explicit operator approval for the crawl and persisted run, `cnry technical-aeo run <project> --wait`, then `cnry technical-aeo score <project> --format json` for site readiness. Use `cnry site-health overview <project> --format json` only to add crawl metadata (root, completeness, budgets, and termination); it never replaces the score. The crawl discovers the in-scope URL inventory from the root, sitemaps, and internal links. The default page budget is 1,000; the edge budget is unset by default and the crawl engine derives it from the page count, so `--max-edges` sets a ceiling rather than lifting one. Use `--max-pages`, `--max-edges`, or `--max-depth` to tighten them. Dead-link checks remain off unless `--check-dead-links` is explicit. For architecture investigation, use bounded Site Health subgraph/path/changes reads rather than attempting to load the visualization graph. Treat `countAccuracy: "lower-bound"` subgraph counts as minimums, and qualify an incomplete path's unreachable/truncated result with `complete: false` plus `termination`; neither is a site-wide conclusion. Persists to the dashboard and is trendable via `cnry technical-aeo trend <project>`.
18
18
  4. Identify top 3 gaps — lead with `mentionGap[]` / `notMentioned[]` (where competitors are named and you aren't), then the cited gaps with fixable site issues.
19
19
  5. Generate onboarding report with baseline + action plan
20
20
  6. Store baseline metrics in memory (include mention rate + mention share, not just cited rate)
@@ -257,7 +257,7 @@ cnry visibility-compare <project> --from 2026-05 --to 2026-06 --format json
257
257
  Site-wide technical audit (structured data, AI-readable content, AI-crawler access, content depth/freshness/extractability, …) powered by `@canonry/aeo-audit`'s `runSiteCrawl`. Runs as the `site-audit` run kind — discovers in-scope URLs from the project root, sitemaps, and internal links; stores the URL/link graph; audits eligible HTML pages; and rolls the results into one 0–100 site score. Pure HTTP, no LLM cost; a large site can take minutes, so it runs in the background. `site-health` is the operator-facing CLI name; `technical-aeo` remains compatible.
258
258
 
259
259
  ```bash
260
- cnry technical-aeo run <project> --wait # full crawl + audit; defaults to 1,000 pages / 100,000 edges; waits for terminal state
260
+ cnry technical-aeo run <project> --wait # full crawl + audit; defaults to 1,000 pages, edges derived from the page count unless --max-edges is set; waits for terminal state
261
261
  cnry technical-aeo run <project> --sitemap-url <url> --max-pages 5000 --max-edges 250000 --max-depth 12 # optional crawl seeds and custom budgets; hard caps are 50,000 pages / 1,000,000 edges
262
262
  cnry technical-aeo run <project> --check-dead-links --wait # opt in to dead-link checks; they are off by default
263
263
  cnry technical-aeo progress <project> --run-id <id> [--format json] # exact durable phase and pages found / checked / failed counters; never a synthesized percentage
@@ -1 +1 @@
1
- import{r as a,u as h,j as e}from"./vendor-tanstack-CYBjbqVr.js";import{cG as L,cH as P,bW as E,f as u,B as x,e as v,T as M}from"./index-Brb7M_6G.js";const o=100;function k(s){if(s==="all")return;const n=Number.parseInt(s,10);return new Date(Date.now()-n*24*60*60*1e3).toISOString()}function O(s){const n=s.userAgent?.toLowerCase()??"";return n.includes("canonry-mcp")?"MCP":n.includes("canonry-cli")?"CLI":n.includes("mozilla/")?"Dashboard":s.actor==="scheduler"?"Scheduler":s.actor==="agent"?"Agent":s.actor==="mcp"?"MCP":s.actor==="cli"?"CLI":s.actor}function B(s){return/delete|remove|revoke|disconnect|failed/.test(s)?"negative":/create|add|connect|completed/.test(s)?"positive":/update|replace|sync|apply/.test(s)?"caution":"neutral"}function D(s){return[s.action,s.actor,s.entityType,s.entityId,s.actorSession,s.userAgent,s.diff==null?"":JSON.stringify(s.diff)].filter(Boolean).join(" ").toLowerCase()}function F({projectName:s}){const[n,l]=a.useState(0),[c,y]=a.useState("90d"),[m,N]=a.useState(""),[p,w]=a.useState(""),S=a.useMemo(()=>k(c),[c]),g={limit:o,offset:n*o,since:S,actor:m||void 0},C=h({...L({client:u,path:{name:s??""},query:g}),enabled:!!s}),A=h({...P({client:u,query:g}),enabled:!s}),f=h({...E({client:u}),enabled:!s}),i=s?C:A,r=i.data??[],I=a.useMemo(()=>new Map((f.data??[]).map(t=>[t.id,t.displayName||t.name])),[f.data]),d=p.trim().toLowerCase(),j=a.useMemo(()=>d?r.filter(t=>D(t).includes(d)):r,[r,d]),b=t=>{l(0),t()};return i.isLoading?e.jsx("p",{className:"supporting-copy mt-6",role:"status",children:"Loading change history…"}):e.jsxs("section",{className:"mt-6","aria-labelledby":"change-history-heading",children:[e.jsxs("div",{className:"section-head section-head-inline",children:[e.jsxs("div",{children:[e.jsx("p",{className:"eyebrow eyebrow-soft",children:"Audit trail"}),e.jsx("h2",{id:"change-history-heading",children:"Change history"}),e.jsx("p",{className:"supporting-copy mt-1 max-w-2xl",children:"Configuration and operator actions. Execution outcomes remain in Runs."})]}),e.jsx(x,{type:"button",variant:"outline",size:"sm",onClick:()=>{i.refetch()},disabled:i.isFetching,children:i.isFetching?"Refreshing…":"Refresh"})]}),e.jsxs("div",{className:"mt-4 grid gap-2 sm:grid-cols-3",children:[e.jsx("input",{type:"search","aria-label":"Search change history",value:p,onChange:t=>w(t.target.value),placeholder:"Search actions, entities, or details",className:"min-h-11 rounded-md border border-base bg-bg px-3 text-sm text-strong placeholder-mono-600 focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-600"}),e.jsxs("select",{"aria-label":"Filter change history by actor",value:m,onChange:t=>b(()=>N(t.target.value)),className:"min-h-11 rounded-md border border-base bg-bg px-3 text-sm text-strong focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-600",children:[e.jsx("option",{value:"",children:"All actors"}),e.jsx("option",{value:"api",children:"API and dashboard"}),e.jsx("option",{value:"cli",children:"CLI"}),e.jsx("option",{value:"scheduler",children:"Scheduler"}),e.jsx("option",{value:"agent",children:"Agent"}),e.jsx("option",{value:"mcp",children:"MCP"})]}),e.jsxs("select",{"aria-label":"Filter change history by date range",value:c,onChange:t=>b(()=>y(t.target.value)),className:"min-h-11 rounded-md border border-base bg-bg px-3 text-sm text-strong focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-600",children:[e.jsx("option",{value:"30d",children:"Last 30 days"}),e.jsx("option",{value:"90d",children:"Last 90 days"}),e.jsx("option",{value:"365d",children:"Last year"}),e.jsx("option",{value:"all",children:"All retained history"})]})]}),i.isError?e.jsxs(v,{className:"surface-card empty-card mt-4",children:[e.jsx("h3",{children:"Change history unavailable"}),e.jsx("p",{children:i.error instanceof Error?i.error.message:"Could not load the audit trail."})]}):j.length===0?e.jsxs(v,{className:"surface-card empty-card mt-4",children:[e.jsx("h3",{children:"No changes match these filters"}),e.jsx("p",{children:"Widen the date range or clear the actor and search filters."})]}):e.jsx("div",{className:"evidence-table-wrap mt-4",children:e.jsxs("table",{className:"evidence-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"When"}),s?null:e.jsx("th",{children:"Scope"}),e.jsx("th",{children:"Action"}),e.jsx("th",{children:"Entity"}),e.jsx("th",{children:"Origin"}),e.jsx("th",{children:"Details"})]})}),e.jsx("tbody",{children:j.map(t=>e.jsxs("tr",{children:[e.jsx("td",{className:"whitespace-nowrap tabular-nums text-muted",children:new Date(t.createdAt).toLocaleString()}),s?null:e.jsx("td",{className:"text-secondary",children:t.projectId?I.get(t.projectId)??`Deleted project · ${t.projectId}`:"Instance settings"}),e.jsx("td",{children:e.jsx(M,{tone:B(t.action),children:t.action})}),e.jsxs("td",{children:[e.jsx("span",{className:"text-strong",children:t.entityType}),t.entityId?e.jsx("span",{className:"mt-0.5 block max-w-48 truncate font-mono text-[11px] text-faint",title:t.entityId,children:t.entityId}):null]}),e.jsxs("td",{children:[e.jsx("span",{className:"text-secondary",children:O(t)}),t.actorSession?e.jsxs("span",{className:"mt-0.5 block max-w-40 truncate font-mono text-[11px] text-faint",title:t.actorSession,children:["Session ",t.actorSession]}):null]}),e.jsx("td",{children:t.diff==null?e.jsx("span",{className:"text-faint",children:"No field diff"}):e.jsxs("details",{children:[e.jsx("summary",{className:"min-h-11 cursor-pointer py-3 text-sm text-secondary hover:text-strong",children:"View diff"}),e.jsx("pre",{className:"max-h-72 max-w-xl overflow-auto whitespace-pre-wrap break-words rounded-md bg-bg-elevated p-3 text-xs text-secondary",children:JSON.stringify(t.diff,null,2)})]})})]},t.id))})]})}),!i.isError&&(n>0||r.length===o)?e.jsxs("div",{className:"mt-4 flex items-center justify-between gap-3",children:[e.jsx(x,{type:"button",variant:"outline",size:"sm",disabled:n===0,onClick:()=>l(t=>Math.max(0,t-1)),children:"Previous"}),e.jsxs("span",{className:"text-xs tabular-nums text-muted",children:["Page ",n+1]}),e.jsx(x,{type:"button",variant:"outline",size:"sm",disabled:r.length<o,onClick:()=>l(t=>t+1),children:"Next"})]}):null]})}export{F as A};
1
+ import{r as a,u as h,j as e}from"./vendor-tanstack-CYBjbqVr.js";import{cG as L,cH as P,bW as E,f as u,B as x,e as v,T as M}from"./index-B64WXjg8.js";const o=100;function k(s){if(s==="all")return;const n=Number.parseInt(s,10);return new Date(Date.now()-n*24*60*60*1e3).toISOString()}function O(s){const n=s.userAgent?.toLowerCase()??"";return n.includes("canonry-mcp")?"MCP":n.includes("canonry-cli")?"CLI":n.includes("mozilla/")?"Dashboard":s.actor==="scheduler"?"Scheduler":s.actor==="agent"?"Agent":s.actor==="mcp"?"MCP":s.actor==="cli"?"CLI":s.actor}function B(s){return/delete|remove|revoke|disconnect|failed/.test(s)?"negative":/create|add|connect|completed/.test(s)?"positive":/update|replace|sync|apply/.test(s)?"caution":"neutral"}function D(s){return[s.action,s.actor,s.entityType,s.entityId,s.actorSession,s.userAgent,s.diff==null?"":JSON.stringify(s.diff)].filter(Boolean).join(" ").toLowerCase()}function F({projectName:s}){const[n,l]=a.useState(0),[c,y]=a.useState("90d"),[m,N]=a.useState(""),[p,w]=a.useState(""),S=a.useMemo(()=>k(c),[c]),g={limit:o,offset:n*o,since:S,actor:m||void 0},C=h({...L({client:u,path:{name:s??""},query:g}),enabled:!!s}),A=h({...P({client:u,query:g}),enabled:!s}),f=h({...E({client:u}),enabled:!s}),i=s?C:A,r=i.data??[],I=a.useMemo(()=>new Map((f.data??[]).map(t=>[t.id,t.displayName||t.name])),[f.data]),d=p.trim().toLowerCase(),j=a.useMemo(()=>d?r.filter(t=>D(t).includes(d)):r,[r,d]),b=t=>{l(0),t()};return i.isLoading?e.jsx("p",{className:"supporting-copy mt-6",role:"status",children:"Loading change history…"}):e.jsxs("section",{className:"mt-6","aria-labelledby":"change-history-heading",children:[e.jsxs("div",{className:"section-head section-head-inline",children:[e.jsxs("div",{children:[e.jsx("p",{className:"eyebrow eyebrow-soft",children:"Audit trail"}),e.jsx("h2",{id:"change-history-heading",children:"Change history"}),e.jsx("p",{className:"supporting-copy mt-1 max-w-2xl",children:"Configuration and operator actions. Execution outcomes remain in Runs."})]}),e.jsx(x,{type:"button",variant:"outline",size:"sm",onClick:()=>{i.refetch()},disabled:i.isFetching,children:i.isFetching?"Refreshing…":"Refresh"})]}),e.jsxs("div",{className:"mt-4 grid gap-2 sm:grid-cols-3",children:[e.jsx("input",{type:"search","aria-label":"Search change history",value:p,onChange:t=>w(t.target.value),placeholder:"Search actions, entities, or details",className:"min-h-11 rounded-md border border-base bg-bg px-3 text-sm text-strong placeholder-mono-600 focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-600"}),e.jsxs("select",{"aria-label":"Filter change history by actor",value:m,onChange:t=>b(()=>N(t.target.value)),className:"min-h-11 rounded-md border border-base bg-bg px-3 text-sm text-strong focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-600",children:[e.jsx("option",{value:"",children:"All actors"}),e.jsx("option",{value:"api",children:"API and dashboard"}),e.jsx("option",{value:"cli",children:"CLI"}),e.jsx("option",{value:"scheduler",children:"Scheduler"}),e.jsx("option",{value:"agent",children:"Agent"}),e.jsx("option",{value:"mcp",children:"MCP"})]}),e.jsxs("select",{"aria-label":"Filter change history by date range",value:c,onChange:t=>b(()=>y(t.target.value)),className:"min-h-11 rounded-md border border-base bg-bg px-3 text-sm text-strong focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-600",children:[e.jsx("option",{value:"30d",children:"Last 30 days"}),e.jsx("option",{value:"90d",children:"Last 90 days"}),e.jsx("option",{value:"365d",children:"Last year"}),e.jsx("option",{value:"all",children:"All retained history"})]})]}),i.isError?e.jsxs(v,{className:"surface-card empty-card mt-4",children:[e.jsx("h3",{children:"Change history unavailable"}),e.jsx("p",{children:i.error instanceof Error?i.error.message:"Could not load the audit trail."})]}):j.length===0?e.jsxs(v,{className:"surface-card empty-card mt-4",children:[e.jsx("h3",{children:"No changes match these filters"}),e.jsx("p",{children:"Widen the date range or clear the actor and search filters."})]}):e.jsx("div",{className:"evidence-table-wrap mt-4",children:e.jsxs("table",{className:"evidence-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"When"}),s?null:e.jsx("th",{children:"Scope"}),e.jsx("th",{children:"Action"}),e.jsx("th",{children:"Entity"}),e.jsx("th",{children:"Origin"}),e.jsx("th",{children:"Details"})]})}),e.jsx("tbody",{children:j.map(t=>e.jsxs("tr",{children:[e.jsx("td",{className:"whitespace-nowrap tabular-nums text-muted",children:new Date(t.createdAt).toLocaleString()}),s?null:e.jsx("td",{className:"text-secondary",children:t.projectId?I.get(t.projectId)??`Deleted project · ${t.projectId}`:"Instance settings"}),e.jsx("td",{children:e.jsx(M,{tone:B(t.action),children:t.action})}),e.jsxs("td",{children:[e.jsx("span",{className:"text-strong",children:t.entityType}),t.entityId?e.jsx("span",{className:"mt-0.5 block max-w-48 truncate font-mono text-[11px] text-faint",title:t.entityId,children:t.entityId}):null]}),e.jsxs("td",{children:[e.jsx("span",{className:"text-secondary",children:O(t)}),t.actorSession?e.jsxs("span",{className:"mt-0.5 block max-w-40 truncate font-mono text-[11px] text-faint",title:t.actorSession,children:["Session ",t.actorSession]}):null]}),e.jsx("td",{children:t.diff==null?e.jsx("span",{className:"text-faint",children:"No field diff"}):e.jsxs("details",{children:[e.jsx("summary",{className:"min-h-11 cursor-pointer py-3 text-sm text-secondary hover:text-strong",children:"View diff"}),e.jsx("pre",{className:"max-h-72 max-w-xl overflow-auto whitespace-pre-wrap break-words rounded-md bg-bg-elevated p-3 text-xs text-secondary",children:JSON.stringify(t.diff,null,2)})]})})]},t.id))})]})}),!i.isError&&(n>0||r.length===o)?e.jsxs("div",{className:"mt-4 flex items-center justify-between gap-3",children:[e.jsx(x,{type:"button",variant:"outline",size:"sm",disabled:n===0,onClick:()=>l(t=>Math.max(0,t-1)),children:"Previous"}),e.jsxs("span",{className:"text-xs tabular-nums text-muted",children:["Page ",n+1]}),e.jsx(x,{type:"button",variant:"outline",size:"sm",disabled:r.length<o,onClick:()=>l(t=>t+1),children:"Next"})]}):null]})}export{F as A};
@@ -1 +1 @@
1
- import{r as n,j as e}from"./vendor-tanstack-CYBjbqVr.js";import{cV as O,ag as H,cW as U,cX as V,cY as Y,e as x,T as j,i as u,B as w,k as $,cZ as K,c_ as X,aj as Z,c$ as J}from"./index-Brb7M_6G.js";import{D as ee,T as se,E as te,P as ae,v as ne,t as re}from"./vendor-lucide-XjCNFK0x.js";import"./vendor-radix-BpmvLvpp.js";import"./vendor-recharts-FA0uySU7.js";import"./vendor-markdown-9tTeP8v7.js";import"./vendor-yaml-GWJz5H2G.js";function g({children:t,label:h="More info",placement:a="top",className:i}){const d=n.useId(),[y,c]=n.useState(!1);return e.jsxs("span",{className:`relative inline-flex ${i??""}`,children:[e.jsx("button",{type:"button","aria-label":h,"aria-describedby":y?d:void 0,className:"inline-flex h-4 w-4 items-center justify-center rounded-full text-muted hover:text-strong focus:text-strong focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-500",onMouseEnter:()=>c(!0),onMouseLeave:()=>c(!1),onFocus:()=>c(!0),onBlur:()=>c(!1),children:e.jsx(re,{className:"h-3.5 w-3.5","aria-hidden":!0})}),y&&e.jsx("span",{id:d,role:"tooltip",className:`absolute z-50 w-64 rounded border border-strong bg-bg-elevated px-3 py-2 text-xs font-normal leading-relaxed text-strong shadow-lg ${a==="top"?"bottom-full mb-2":"top-full mt-2"} left-1/2 -translate-x-1/2 whitespace-normal`,children:t})]})}const le="https://commoncrawl.org/web-graphs";function S(t){return t==null?"—":t>=1e12?`${(t/1e12).toFixed(1)} TB`:t>=1e9?`${(t/1e9).toFixed(1)} GB`:t>=1e6?`${(t/1e6).toFixed(1)} MB`:t>=1e3?`${(t/1e3).toFixed(1)} KB`:`${t} B`}function b(t){if(!t)return"—";const h=Date.now()-new Date(t).getTime(),a=Math.floor(h/6e4);if(a<1)return"just now";if(a<60)return`${a}m ago`;const i=Math.floor(a/60);return i<24?`${i}h ago`:`${Math.floor(i/24)}d ago`}function C(t){switch(t){case"ready":return"positive";case"failed":return"negative";case"downloading":case"querying":case"queued":return"caution"}}function me(){const[t,h]=n.useState(null),[a,i]=n.useState(null),[d,y]=n.useState([]),[c,F]=n.useState([]),[m,L]=n.useState(null),[B,D]=n.useState(!0),[R,E]=n.useState(!1),[p,I]=n.useState(!1),[N,v]=n.useState(""),[M,k]=n.useState(!1),[A,l]=n.useState(null),[P,o]=n.useState(null),f=n.useCallback(async()=>{D(!0),l(null);try{const[s,r,W,_,z]=await Promise.all([O(),H().catch(()=>null),U().catch(()=>[]),V().catch(()=>[]),Y().catch(()=>null)]);h(s),i(r),y(W),F(_),L(z)}catch(s){l(s instanceof Error?s.message:"Failed to load backlinks status")}finally{D(!1)}},[]);n.useEffect(()=>{f()},[f]);async function T(){E(!0),l(null),o(null);try{const s=await K();o(s.alreadyPresent?`DuckDB already installed (${s.version}).`:`Installed DuckDB ${s.version}.`),await f()}catch(s){l(s instanceof Error?s.message:"Failed to install DuckDB")}finally{E(!1)}}async function q(){const s=N.trim()||void 0;I(!0),l(null),o(null);try{const r=await X(s);o(s?`Queued sync for ${r.release}. Download + query runs in the background.`:`Queued sync for auto-discovered release ${r.release}. Download + query runs in the background.`),v(""),k(!1),await f()}catch(r){r instanceof Z&&r.code==="MISSING_DEPENDENCY"?l("DuckDB is not installed. Install it first."):l(r instanceof Error?r.message:"Failed to trigger sync")}finally{I(!1)}}async function G(s){l(null),o(null);try{await J(s),o(`Pruned cached release ${s}.`),await f()}catch(r){l(r instanceof Error?r.message:"Failed to prune release")}}const Q=a?.status==="ready"&&c.every(s=>s.release!==a.release);return e.jsxs("div",{className:"page-container",children:[e.jsx("div",{className:"page-header",children:e.jsxs("div",{className:"page-header-left",children:[e.jsx("h1",{className:"page-title",children:"Backlink data"}),e.jsx("p",{className:"page-subtitle",children:"Manage the shared backlink source and review its latest data."})]})}),A&&e.jsx(x,{className:"surface-card p-4 mb-4 border-negative-800/60",children:e.jsx("p",{className:"text-sm text-negative",children:A})}),P&&e.jsx(x,{className:"surface-card p-4 mb-4 border-positive-800/60",children:e.jsx("p",{className:"text-sm text-positive",children:P})}),e.jsxs("section",{className:"page-section-divider",children:[e.jsxs("div",{className:"section-head section-head-inline",children:[e.jsxs("div",{children:[e.jsx("p",{className:"eyebrow eyebrow-soft",children:"Readiness"}),e.jsx("h2",{children:"Backlink source"})]}),t?.duckdbInstalled?e.jsx(j,{tone:"positive",children:"Installed"}):e.jsx(j,{tone:"caution",children:"Not installed"})]}),e.jsx(x,{className:"surface-card p-4",children:B?e.jsx("p",{className:"text-sm text-muted",children:"Checking…"}):t?.duckdbInstalled?e.jsx("p",{className:"text-sm text-secondary",children:"Ready to sync a Common Crawl release."}):e.jsxs("div",{className:"flex items-center justify-between gap-4",children:[e.jsx("p",{className:"text-sm text-secondary",children:"Install the local query engine to enable release syncs."}),!u()&&e.jsxs(w,{type:"button",size:"sm",disabled:R,onClick:$(T),children:[e.jsx(ee,{className:"h-4 w-4 mr-1.5","aria-hidden":!0}),R?"Installing…":"Install DuckDB"]})]})})]}),e.jsxs("section",{className:"page-section-divider",children:[e.jsxs("div",{className:"section-head section-head-inline",children:[e.jsxs("div",{children:[e.jsx("p",{className:"eyebrow eyebrow-soft",children:"Latest sync"}),e.jsxs("h2",{className:"flex items-center gap-2",children:["Release sync",e.jsx(g,{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."})]})]}),a&&e.jsx(j,{tone:C(a.status),children:a.status})]}),e.jsxs(x,{className:"surface-card p-5",children:[a?e.jsxs("div",{className:"space-y-2 text-sm",children:[e.jsxs("p",{className:"text-strong",children:["Release ",e.jsx("code",{className:"text-neutral",children:a.release})]}),a.phaseDetail&&e.jsx("p",{className:"text-secondary",children:a.phaseDetail}),e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-4 text-xs text-muted pt-2",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-faint uppercase tracking-wide",children:"Projects"}),e.jsx("p",{className:"text-neutral mt-0.5",children:a.projectsProcessed??"—"})]}),e.jsxs("div",{children:[e.jsxs("p",{className:"text-faint uppercase tracking-wide flex items-center gap-1",children:["Rows",e.jsx(g,{label:"What are rows?",children:"Total number of (project, referring domain) pairs persisted in SQLite from this sync, across every project in the workspace."})]}),e.jsx("p",{className:"text-neutral mt-0.5",children:a.domainsDiscovered??"—"})]}),e.jsxs("div",{children:[e.jsx("p",{className:"text-faint uppercase tracking-wide",children:"Started"}),e.jsx("p",{className:"text-neutral mt-0.5",children:b(a.downloadStartedAt??a.createdAt)})]}),e.jsxs("div",{children:[e.jsx("p",{className:"text-faint uppercase tracking-wide",children:"Finished"}),e.jsx("p",{className:"text-neutral mt-0.5",children:b(a.queryFinishedAt)})]})]}),a.error&&e.jsx("p",{className:"text-sm text-negative-400 pt-2",children:a.error})]}):e.jsx("p",{className:"text-sm text-muted",children:"No release sync has run in this workspace yet."}),Q&&e.jsx("div",{className:"mt-4 rounded border border-caution-800/60 bg-caution-950/20 p-3",children:e.jsxs("div",{className:"flex items-start gap-2",children:[e.jsx(se,{className:"h-4 w-4 text-caution-400 shrink-0 mt-0.5","aria-hidden":!0}),e.jsxs("div",{className:"text-sm text-neutral leading-relaxed",children:[e.jsx("p",{className:"font-medium text-caution-200",children:"Cached files for this release are missing."}),e.jsx("p",{className:"mt-1 text-secondary",children:"Sync this release again, or sync the newest release, before refreshing individual projects."})]})]})}),e.jsxs("div",{className:"mt-4 rounded border border-base bg-bg-elevated/40 p-3",children:[e.jsxs("div",{className:"flex items-start justify-between gap-3 mb-3",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-[10px] uppercase tracking-wide text-muted",children:"Auto-detected release"}),m?e.jsxs("p",{className:"text-sm text-strong mt-0.5",children:[e.jsx("code",{className:"text-heading",children:m.release}),e.jsxs("span",{className:"ml-2 text-xs text-muted",children:["Vertex ",S(m.vertexBytes),", edges ",S(m.edgesBytes)]})]}):e.jsx("p",{className:"text-sm text-muted mt-0.5",children:B?"Checking Common Crawl…":"Could not find a release. Choose one below."}),e.jsxs("a",{href:le,target:"_blank",rel:"noopener noreferrer",className:"mt-1 inline-flex items-center gap-1 text-xs 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",e.jsx(te,{className:"h-3 w-3","aria-hidden":!0})]})]}),!u()&&e.jsxs("div",{className:"flex items-center gap-2 shrink-0",children:[e.jsxs(w,{type:"button",size:"sm",disabled:p||!t?.duckdbInstalled||!m&&!N.trim(),onClick:$(q),children:[e.jsx(ae,{className:"h-4 w-4 mr-1.5","aria-hidden":!0}),p?"Queuing…":"Run sync"]}),e.jsxs(g,{label:"What does Run sync do?",children:[e.jsxs("span",{className:"block",children:["Downloads the auto-detected (or chosen) Common Crawl release (~16 GB) to"," ",e.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."]}),e.jsxs("span",{className:"mt-2 block text-secondary",children:["First time for a release: ",e.jsx("span",{className:"text-strong",children:"~10–20 min download + ~5 min query"}),". Re-running the same release later: ",e.jsx("span",{className:"text-strong",children:"skips download, just re-queries"})," (~5 min)."]})]})]})]}),u()?null:M?e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("input",{type:"text",className:"flex-1 min-w-[240px] rounded border border-strong bg-transparent px-2.5 py-1.5 text-sm text-strong placeholder-mono-600 focus:border-mono-500 focus:outline-none",placeholder:"cc-main-2026-jan-feb-mar",value:N,onChange:s=>v(s.target.value),disabled:p,autoFocus:!0}),e.jsx("button",{type:"button",className:"text-sm text-secondary hover:text-neutral focus:text-neutral focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-500 rounded",onClick:()=>{v(""),k(!1)},disabled:p,children:"Cancel"})]}):e.jsx("button",{type:"button",className:"text-sm text-secondary hover:text-neutral focus:text-neutral focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-500 rounded",onClick:()=>k(!0),disabled:p,children:"Use a different release →"})]}),!t?.duckdbInstalled&&e.jsx("p",{className:"text-sm text-secondary mt-2",children:"Install DuckDB first to enable sync."})]})]}),e.jsxs("details",{className:"page-section-divider group",children:[e.jsx("summary",{className:"cursor-pointer text-sm font-medium text-secondary hover:text-strong focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-500 rounded",children:"Technical details and cache"}),e.jsxs("div",{className:"mt-4",children:[e.jsx("p",{className:"text-sm text-secondary mb-3",children:"Cached releases support project re-extracts. Pruning a release does not remove saved backlink results."}),e.jsx(x,{className:"surface-card overflow-hidden",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-base text-left text-xs uppercase tracking-wide text-faint",children:[e.jsx("th",{className:"px-4 py-2 font-medium",children:"Release"}),e.jsx("th",{className:"px-4 py-2 font-medium",children:"Sync status"}),e.jsx("th",{className:"px-4 py-2 text-right font-medium",children:"Size"}),e.jsx("th",{className:"px-4 py-2 font-medium",children:"Last used"}),!u()&&e.jsx("th",{className:"px-4 py-2 font-medium sr-only",children:"Actions"})]})}),e.jsxs("tbody",{children:[c.map(s=>e.jsxs("tr",{className:"border-b border-mono-900 last:border-0",children:[e.jsx("td",{className:"px-4 py-2 text-strong",children:e.jsx("code",{children:s.release})}),e.jsx("td",{className:"px-4 py-2",children:s.syncStatus?e.jsx(j,{tone:C(s.syncStatus),children:s.syncStatus}):e.jsx("span",{className:"text-faint",children:"—"})}),e.jsx("td",{className:"px-4 py-2 text-right text-secondary tabular-nums",children:S(s.bytes)}),e.jsx("td",{className:"px-4 py-2 text-secondary",children:b(s.lastUsedAt)}),!u()&&e.jsx("td",{className:"px-4 py-2 text-right",children:e.jsxs("div",{className:"inline-flex items-center gap-1",children:[e.jsxs(w,{type:"button",variant:"outline",size:"sm",onClick:()=>{G(s.release)},children:[e.jsx(ne,{className:"h-4 w-4 mr-1.5","aria-hidden":!0}),"Prune"]}),e.jsx(g,{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)."})]})})]},s.release)),c.length===0&&e.jsx("tr",{children:e.jsx("td",{className:"px-4 py-4 text-sm text-muted",colSpan:u()?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."})})]})]})}),e.jsx("p",{className:"mt-3 text-xs text-muted",children:"Common Crawl files are stored locally. DuckDB reads them; Canonry stores results in its normal database."})]})]}),d.length>1&&e.jsxs("section",{className:"page-section-divider",children:[e.jsx("div",{className:"section-head section-head-inline",children:e.jsxs("div",{children:[e.jsx("p",{className:"eyebrow eyebrow-soft",children:"History"}),e.jsx("h2",{children:"Past release syncs"})]})}),e.jsx(x,{className:"surface-card overflow-hidden",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-base text-left text-xs uppercase tracking-wide text-faint",children:[e.jsx("th",{className:"px-4 py-2 font-medium",children:"Release"}),e.jsx("th",{className:"px-4 py-2 font-medium",children:"Status"}),e.jsx("th",{className:"px-4 py-2 text-right font-medium",children:"Projects"}),e.jsx("th",{className:"px-4 py-2 text-right font-medium",children:"Rows"}),e.jsx("th",{className:"px-4 py-2 font-medium",children:"Finished"})]})}),e.jsx("tbody",{children:d.map(s=>e.jsxs("tr",{className:"border-b border-mono-900 last:border-0",children:[e.jsx("td",{className:"px-4 py-2 text-strong",children:e.jsx("code",{children:s.release})}),e.jsx("td",{className:"px-4 py-2",children:e.jsx(j,{tone:C(s.status),children:s.status})}),e.jsx("td",{className:"px-4 py-2 text-right text-secondary tabular-nums",children:s.projectsProcessed??"—"}),e.jsx("td",{className:"px-4 py-2 text-right text-secondary tabular-nums",children:s.domainsDiscovered??"—"}),e.jsx("td",{className:"px-4 py-2 text-secondary",children:b(s.queryFinishedAt??s.updatedAt)})]},s.id))})]})})]})]})}export{me as BacklinksPage};
1
+ import{r as n,j as e}from"./vendor-tanstack-CYBjbqVr.js";import{cV as O,ag as H,cW as U,cX as V,cY as Y,e as x,T as j,i as u,B as w,k as $,cZ as K,c_ as X,aj as Z,c$ as J}from"./index-B64WXjg8.js";import{D as ee,T as se,E as te,P as ae,v as ne,t as re}from"./vendor-lucide-XjCNFK0x.js";import"./vendor-radix-BpmvLvpp.js";import"./vendor-recharts-FA0uySU7.js";import"./vendor-markdown-9tTeP8v7.js";import"./vendor-yaml-GWJz5H2G.js";function g({children:t,label:h="More info",placement:a="top",className:i}){const d=n.useId(),[y,c]=n.useState(!1);return e.jsxs("span",{className:`relative inline-flex ${i??""}`,children:[e.jsx("button",{type:"button","aria-label":h,"aria-describedby":y?d:void 0,className:"inline-flex h-4 w-4 items-center justify-center rounded-full text-muted hover:text-strong focus:text-strong focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-500",onMouseEnter:()=>c(!0),onMouseLeave:()=>c(!1),onFocus:()=>c(!0),onBlur:()=>c(!1),children:e.jsx(re,{className:"h-3.5 w-3.5","aria-hidden":!0})}),y&&e.jsx("span",{id:d,role:"tooltip",className:`absolute z-50 w-64 rounded border border-strong bg-bg-elevated px-3 py-2 text-xs font-normal leading-relaxed text-strong shadow-lg ${a==="top"?"bottom-full mb-2":"top-full mt-2"} left-1/2 -translate-x-1/2 whitespace-normal`,children:t})]})}const le="https://commoncrawl.org/web-graphs";function S(t){return t==null?"—":t>=1e12?`${(t/1e12).toFixed(1)} TB`:t>=1e9?`${(t/1e9).toFixed(1)} GB`:t>=1e6?`${(t/1e6).toFixed(1)} MB`:t>=1e3?`${(t/1e3).toFixed(1)} KB`:`${t} B`}function b(t){if(!t)return"—";const h=Date.now()-new Date(t).getTime(),a=Math.floor(h/6e4);if(a<1)return"just now";if(a<60)return`${a}m ago`;const i=Math.floor(a/60);return i<24?`${i}h ago`:`${Math.floor(i/24)}d ago`}function C(t){switch(t){case"ready":return"positive";case"failed":return"negative";case"downloading":case"querying":case"queued":return"caution"}}function me(){const[t,h]=n.useState(null),[a,i]=n.useState(null),[d,y]=n.useState([]),[c,F]=n.useState([]),[m,L]=n.useState(null),[B,D]=n.useState(!0),[R,E]=n.useState(!1),[p,I]=n.useState(!1),[N,v]=n.useState(""),[M,k]=n.useState(!1),[A,l]=n.useState(null),[P,o]=n.useState(null),f=n.useCallback(async()=>{D(!0),l(null);try{const[s,r,W,_,z]=await Promise.all([O(),H().catch(()=>null),U().catch(()=>[]),V().catch(()=>[]),Y().catch(()=>null)]);h(s),i(r),y(W),F(_),L(z)}catch(s){l(s instanceof Error?s.message:"Failed to load backlinks status")}finally{D(!1)}},[]);n.useEffect(()=>{f()},[f]);async function T(){E(!0),l(null),o(null);try{const s=await K();o(s.alreadyPresent?`DuckDB already installed (${s.version}).`:`Installed DuckDB ${s.version}.`),await f()}catch(s){l(s instanceof Error?s.message:"Failed to install DuckDB")}finally{E(!1)}}async function q(){const s=N.trim()||void 0;I(!0),l(null),o(null);try{const r=await X(s);o(s?`Queued sync for ${r.release}. Download + query runs in the background.`:`Queued sync for auto-discovered release ${r.release}. Download + query runs in the background.`),v(""),k(!1),await f()}catch(r){r instanceof Z&&r.code==="MISSING_DEPENDENCY"?l("DuckDB is not installed. Install it first."):l(r instanceof Error?r.message:"Failed to trigger sync")}finally{I(!1)}}async function G(s){l(null),o(null);try{await J(s),o(`Pruned cached release ${s}.`),await f()}catch(r){l(r instanceof Error?r.message:"Failed to prune release")}}const Q=a?.status==="ready"&&c.every(s=>s.release!==a.release);return e.jsxs("div",{className:"page-container",children:[e.jsx("div",{className:"page-header",children:e.jsxs("div",{className:"page-header-left",children:[e.jsx("h1",{className:"page-title",children:"Backlink data"}),e.jsx("p",{className:"page-subtitle",children:"Manage the shared backlink source and review its latest data."})]})}),A&&e.jsx(x,{className:"surface-card p-4 mb-4 border-negative-800/60",children:e.jsx("p",{className:"text-sm text-negative",children:A})}),P&&e.jsx(x,{className:"surface-card p-4 mb-4 border-positive-800/60",children:e.jsx("p",{className:"text-sm text-positive",children:P})}),e.jsxs("section",{className:"page-section-divider",children:[e.jsxs("div",{className:"section-head section-head-inline",children:[e.jsxs("div",{children:[e.jsx("p",{className:"eyebrow eyebrow-soft",children:"Readiness"}),e.jsx("h2",{children:"Backlink source"})]}),t?.duckdbInstalled?e.jsx(j,{tone:"positive",children:"Installed"}):e.jsx(j,{tone:"caution",children:"Not installed"})]}),e.jsx(x,{className:"surface-card p-4",children:B?e.jsx("p",{className:"text-sm text-muted",children:"Checking…"}):t?.duckdbInstalled?e.jsx("p",{className:"text-sm text-secondary",children:"Ready to sync a Common Crawl release."}):e.jsxs("div",{className:"flex items-center justify-between gap-4",children:[e.jsx("p",{className:"text-sm text-secondary",children:"Install the local query engine to enable release syncs."}),!u()&&e.jsxs(w,{type:"button",size:"sm",disabled:R,onClick:$(T),children:[e.jsx(ee,{className:"h-4 w-4 mr-1.5","aria-hidden":!0}),R?"Installing…":"Install DuckDB"]})]})})]}),e.jsxs("section",{className:"page-section-divider",children:[e.jsxs("div",{className:"section-head section-head-inline",children:[e.jsxs("div",{children:[e.jsx("p",{className:"eyebrow eyebrow-soft",children:"Latest sync"}),e.jsxs("h2",{className:"flex items-center gap-2",children:["Release sync",e.jsx(g,{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."})]})]}),a&&e.jsx(j,{tone:C(a.status),children:a.status})]}),e.jsxs(x,{className:"surface-card p-5",children:[a?e.jsxs("div",{className:"space-y-2 text-sm",children:[e.jsxs("p",{className:"text-strong",children:["Release ",e.jsx("code",{className:"text-neutral",children:a.release})]}),a.phaseDetail&&e.jsx("p",{className:"text-secondary",children:a.phaseDetail}),e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-4 text-xs text-muted pt-2",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-faint uppercase tracking-wide",children:"Projects"}),e.jsx("p",{className:"text-neutral mt-0.5",children:a.projectsProcessed??"—"})]}),e.jsxs("div",{children:[e.jsxs("p",{className:"text-faint uppercase tracking-wide flex items-center gap-1",children:["Rows",e.jsx(g,{label:"What are rows?",children:"Total number of (project, referring domain) pairs persisted in SQLite from this sync, across every project in the workspace."})]}),e.jsx("p",{className:"text-neutral mt-0.5",children:a.domainsDiscovered??"—"})]}),e.jsxs("div",{children:[e.jsx("p",{className:"text-faint uppercase tracking-wide",children:"Started"}),e.jsx("p",{className:"text-neutral mt-0.5",children:b(a.downloadStartedAt??a.createdAt)})]}),e.jsxs("div",{children:[e.jsx("p",{className:"text-faint uppercase tracking-wide",children:"Finished"}),e.jsx("p",{className:"text-neutral mt-0.5",children:b(a.queryFinishedAt)})]})]}),a.error&&e.jsx("p",{className:"text-sm text-negative-400 pt-2",children:a.error})]}):e.jsx("p",{className:"text-sm text-muted",children:"No release sync has run in this workspace yet."}),Q&&e.jsx("div",{className:"mt-4 rounded border border-caution-800/60 bg-caution-950/20 p-3",children:e.jsxs("div",{className:"flex items-start gap-2",children:[e.jsx(se,{className:"h-4 w-4 text-caution-400 shrink-0 mt-0.5","aria-hidden":!0}),e.jsxs("div",{className:"text-sm text-neutral leading-relaxed",children:[e.jsx("p",{className:"font-medium text-caution-200",children:"Cached files for this release are missing."}),e.jsx("p",{className:"mt-1 text-secondary",children:"Sync this release again, or sync the newest release, before refreshing individual projects."})]})]})}),e.jsxs("div",{className:"mt-4 rounded border border-base bg-bg-elevated/40 p-3",children:[e.jsxs("div",{className:"flex items-start justify-between gap-3 mb-3",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-[10px] uppercase tracking-wide text-muted",children:"Auto-detected release"}),m?e.jsxs("p",{className:"text-sm text-strong mt-0.5",children:[e.jsx("code",{className:"text-heading",children:m.release}),e.jsxs("span",{className:"ml-2 text-xs text-muted",children:["Vertex ",S(m.vertexBytes),", edges ",S(m.edgesBytes)]})]}):e.jsx("p",{className:"text-sm text-muted mt-0.5",children:B?"Checking Common Crawl…":"Could not find a release. Choose one below."}),e.jsxs("a",{href:le,target:"_blank",rel:"noopener noreferrer",className:"mt-1 inline-flex items-center gap-1 text-xs 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",e.jsx(te,{className:"h-3 w-3","aria-hidden":!0})]})]}),!u()&&e.jsxs("div",{className:"flex items-center gap-2 shrink-0",children:[e.jsxs(w,{type:"button",size:"sm",disabled:p||!t?.duckdbInstalled||!m&&!N.trim(),onClick:$(q),children:[e.jsx(ae,{className:"h-4 w-4 mr-1.5","aria-hidden":!0}),p?"Queuing…":"Run sync"]}),e.jsxs(g,{label:"What does Run sync do?",children:[e.jsxs("span",{className:"block",children:["Downloads the auto-detected (or chosen) Common Crawl release (~16 GB) to"," ",e.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."]}),e.jsxs("span",{className:"mt-2 block text-secondary",children:["First time for a release: ",e.jsx("span",{className:"text-strong",children:"~10–20 min download + ~5 min query"}),". Re-running the same release later: ",e.jsx("span",{className:"text-strong",children:"skips download, just re-queries"})," (~5 min)."]})]})]})]}),u()?null:M?e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("input",{type:"text",className:"flex-1 min-w-[240px] rounded border border-strong bg-transparent px-2.5 py-1.5 text-sm text-strong placeholder-mono-600 focus:border-mono-500 focus:outline-none",placeholder:"cc-main-2026-jan-feb-mar",value:N,onChange:s=>v(s.target.value),disabled:p,autoFocus:!0}),e.jsx("button",{type:"button",className:"text-sm text-secondary hover:text-neutral focus:text-neutral focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-500 rounded",onClick:()=>{v(""),k(!1)},disabled:p,children:"Cancel"})]}):e.jsx("button",{type:"button",className:"text-sm text-secondary hover:text-neutral focus:text-neutral focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-500 rounded",onClick:()=>k(!0),disabled:p,children:"Use a different release →"})]}),!t?.duckdbInstalled&&e.jsx("p",{className:"text-sm text-secondary mt-2",children:"Install DuckDB first to enable sync."})]})]}),e.jsxs("details",{className:"page-section-divider group",children:[e.jsx("summary",{className:"cursor-pointer text-sm font-medium text-secondary hover:text-strong focus:outline-none focus-visible:ring-1 focus-visible:ring-mono-500 rounded",children:"Technical details and cache"}),e.jsxs("div",{className:"mt-4",children:[e.jsx("p",{className:"text-sm text-secondary mb-3",children:"Cached releases support project re-extracts. Pruning a release does not remove saved backlink results."}),e.jsx(x,{className:"surface-card overflow-hidden",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-base text-left text-xs uppercase tracking-wide text-faint",children:[e.jsx("th",{className:"px-4 py-2 font-medium",children:"Release"}),e.jsx("th",{className:"px-4 py-2 font-medium",children:"Sync status"}),e.jsx("th",{className:"px-4 py-2 text-right font-medium",children:"Size"}),e.jsx("th",{className:"px-4 py-2 font-medium",children:"Last used"}),!u()&&e.jsx("th",{className:"px-4 py-2 font-medium sr-only",children:"Actions"})]})}),e.jsxs("tbody",{children:[c.map(s=>e.jsxs("tr",{className:"border-b border-mono-900 last:border-0",children:[e.jsx("td",{className:"px-4 py-2 text-strong",children:e.jsx("code",{children:s.release})}),e.jsx("td",{className:"px-4 py-2",children:s.syncStatus?e.jsx(j,{tone:C(s.syncStatus),children:s.syncStatus}):e.jsx("span",{className:"text-faint",children:"—"})}),e.jsx("td",{className:"px-4 py-2 text-right text-secondary tabular-nums",children:S(s.bytes)}),e.jsx("td",{className:"px-4 py-2 text-secondary",children:b(s.lastUsedAt)}),!u()&&e.jsx("td",{className:"px-4 py-2 text-right",children:e.jsxs("div",{className:"inline-flex items-center gap-1",children:[e.jsxs(w,{type:"button",variant:"outline",size:"sm",onClick:()=>{G(s.release)},children:[e.jsx(ne,{className:"h-4 w-4 mr-1.5","aria-hidden":!0}),"Prune"]}),e.jsx(g,{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)."})]})})]},s.release)),c.length===0&&e.jsx("tr",{children:e.jsx("td",{className:"px-4 py-4 text-sm text-muted",colSpan:u()?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."})})]})]})}),e.jsx("p",{className:"mt-3 text-xs text-muted",children:"Common Crawl files are stored locally. DuckDB reads them; Canonry stores results in its normal database."})]})]}),d.length>1&&e.jsxs("section",{className:"page-section-divider",children:[e.jsx("div",{className:"section-head section-head-inline",children:e.jsxs("div",{children:[e.jsx("p",{className:"eyebrow eyebrow-soft",children:"History"}),e.jsx("h2",{children:"Past release syncs"})]})}),e.jsx(x,{className:"surface-card overflow-hidden",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"border-b border-base text-left text-xs uppercase tracking-wide text-faint",children:[e.jsx("th",{className:"px-4 py-2 font-medium",children:"Release"}),e.jsx("th",{className:"px-4 py-2 font-medium",children:"Status"}),e.jsx("th",{className:"px-4 py-2 text-right font-medium",children:"Projects"}),e.jsx("th",{className:"px-4 py-2 text-right font-medium",children:"Rows"}),e.jsx("th",{className:"px-4 py-2 font-medium",children:"Finished"})]})}),e.jsx("tbody",{children:d.map(s=>e.jsxs("tr",{className:"border-b border-mono-900 last:border-0",children:[e.jsx("td",{className:"px-4 py-2 text-strong",children:e.jsx("code",{children:s.release})}),e.jsx("td",{className:"px-4 py-2",children:e.jsx(j,{tone:C(s.status),children:s.status})}),e.jsx("td",{className:"px-4 py-2 text-right text-secondary tabular-nums",children:s.projectsProcessed??"—"}),e.jsx("td",{className:"px-4 py-2 text-right text-secondary tabular-nums",children:s.domainsDiscovered??"—"}),e.jsx("td",{className:"px-4 py-2 text-secondary",children:b(s.queryFinishedAt??s.updatedAt)})]},s.id))})]})})]})]})}export{me as BacklinksPage};
@@ -1 +1 @@
1
- import{j as e}from"./vendor-tanstack-CYBjbqVr.js";import{A as s}from"./AuditHistoryPanel-1_FHpzj-.js";import"./index-Brb7M_6G.js";import"./vendor-radix-BpmvLvpp.js";import"./vendor-recharts-FA0uySU7.js";import"./vendor-lucide-XjCNFK0x.js";import"./vendor-markdown-9tTeP8v7.js";import"./vendor-yaml-GWJz5H2G.js";function l(){return e.jsxs("div",{className:"page-container",children:[e.jsx("div",{className:"page-header",children:e.jsxs("div",{className:"page-header-left",children:[e.jsx("h1",{className:"page-title",children:"History"}),e.jsx("p",{className:"page-subtitle",children:"Changes to projects and settings."})]})}),e.jsx(s,{})]})}export{l as HistoryPage};
1
+ import{j as e}from"./vendor-tanstack-CYBjbqVr.js";import{A as s}from"./AuditHistoryPanel-ClwYugEs.js";import"./index-B64WXjg8.js";import"./vendor-radix-BpmvLvpp.js";import"./vendor-recharts-FA0uySU7.js";import"./vendor-lucide-XjCNFK0x.js";import"./vendor-markdown-9tTeP8v7.js";import"./vendor-yaml-GWJz5H2G.js";function l(){return e.jsxs("div",{className:"page-container",children:[e.jsx("div",{className:"page-header",children:e.jsxs("div",{className:"page-header-left",children:[e.jsx("h1",{className:"page-title",children:"History"}),e.jsx("p",{className:"page-subtitle",children:"Changes to projects and settings."})]})}),e.jsx(s,{})]})}export{l as HistoryPage};