@ainyc/canonry 4.180.8 → 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.
- package/README.md +166 -100
- package/assets/agent-workspace/skills/aero/references/orchestration.md +1 -1
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +1 -1
- package/assets/assets/{AuditHistoryPanel-qiuqNGvG.js → AuditHistoryPanel-ClwYugEs.js} +1 -1
- package/assets/assets/{BacklinksPage-D-g5f0Xv.js → BacklinksPage-DaEn1w9_.js} +1 -1
- package/assets/assets/{ChartPrimitives-DG35TyhH.js → ChartPrimitives-DLEICrrL.js} +1 -1
- package/assets/assets/{HistoryPage-q8_pCuOs.js → HistoryPage-B4WWIXmx.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-C_wMjUZe.js → MeasurementPropertyPage-DFvelKfS.js} +1 -1
- package/assets/assets/ProjectPage-D3AuGcau.js +9 -0
- package/assets/assets/{RunRow-D5hatdlY.js → RunRow-0HuBq5KN.js} +1 -1
- package/assets/assets/{RunsPage-Bj2snMM3.js → RunsPage-DhdjoIdK.js} +1 -1
- package/assets/assets/{SettingsPage-Cf5YgyJv.js → SettingsPage-XzHCmYz4.js} +1 -1
- package/assets/assets/{SiteHealthSection-D8RLvMAE.js → SiteHealthSection-BODUx0bX.js} +3 -3
- package/assets/assets/{TrafficPage-CotJ14le.js → TrafficPage-2AKICLH_.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-BcxYX-uq.js → TrafficSourceDetailPage-CYVG7qZ0.js} +1 -1
- package/assets/assets/extract-error-message-CtGLEBMO.js +1 -0
- package/assets/assets/index-B64WXjg8.js +86 -0
- package/assets/assets/{index-DXCQf5s1.css → index-DwdK2e4e.css} +1 -1
- package/assets/assets/{react-sigma_core.esm.min-DxZINEmi.js → react-sigma_core.esm.min-D5Honb13.js} +1 -1
- package/assets/assets/{vendor-lucide-nYKv-BB7.js → vendor-lucide-XjCNFK0x.js} +1 -1
- package/assets/assets/{vendor-markdown-Bn7fRwrx.js → vendor-markdown-9tTeP8v7.js} +1 -1
- package/assets/assets/{vendor-radix-4UOaVWza.js → vendor-radix-BpmvLvpp.js} +1 -1
- package/assets/assets/{vendor-recharts-D8rt5lKn.js → vendor-recharts-FA0uySU7.js} +1 -1
- package/assets/assets/{vendor-tanstack-Dock0eH1.js → vendor-tanstack-CYBjbqVr.js} +1 -1
- package/assets/index.html +7 -7
- package/dist/{chunk-ZJ2VGTYU.js → chunk-ISONU3FA.js} +4276 -3389
- package/dist/{chunk-GFVZOVG3.js → chunk-OF3PKBW4.js} +72 -6
- package/dist/{chunk-G7M2KM7Q.js → chunk-RQSSKN5F.js} +2 -2
- package/dist/{chunk-IM4HFNGF.js → chunk-VFQPW2VX.js} +43 -3
- package/dist/{chunk-GN2G7VFK.js → chunk-Z2HTLTQV.js} +162 -4
- package/dist/cli.js +141 -8
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-IURLYRB3.js → intelligence-service-4XQBUFBU.js} +2 -2
- package/dist/mcp.js +3 -3
- package/package.json +8 -8
- package/assets/assets/ProjectPage-33THfDtC.js +0 -9
- package/assets/assets/extract-error-message-Bxt42scd.js +0 -1
- package/assets/assets/index-BlILyZJ5.js +0 -86
package/README.md
CHANGED
|
@@ -1,46 +1,56 @@
|
|
|
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
|
-
[](https://www.npmjs.com/package/@canonry/canonry)
|
|
3
|
+
[](https://www.npmjs.com/package/@canonry/canonry)
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
|
|
7
|
+
**Self-hosted, with your own provider keys.** Your dashboard and agent review the same project evidence.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
[Quick start](#quick-start) · [Evidence](#explore-the-evidence) · [Actions](#act-on-the-evidence) · [Integrations](#integrations) · [Docs](#documentation)
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://raw.githubusercontent.com/Canonry/canonry/main/docs/images/measure-act.svg">
|
|
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%" />
|
|
14
|
+
</a>
|
|
15
|
+
</p>
|
|
12
16
|
|
|
13
|
-
|
|
17
|
+
## Quick start
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
<a id="or-use-any-shell-capable-coding-agent"></a>
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|---|---|
|
|
19
|
-
| **Measure** | Track mentions and citations across Gemini, ChatGPT, Claude, Perplexity, and local models. Join them with GSC, GA4, Bing, server side traffic, Business Profile, and backlink data. |
|
|
20
|
-
| **Diagnose** | Crawl the site, score Page Health, inspect evidence, compare competitors, and explain regressions. |
|
|
21
|
-
| **Act** | Give your agent the context + skills to make surgical on-site technical SEO changes and coordinate content, indexing, analytics, and paid media. |
|
|
22
|
-
| **Operate** | Create automation workflows, schedule checks, sync data, send webhooks, and generate client-ready reports. |
|
|
21
|
+
### Start with your agent
|
|
23
22
|
|
|
24
|
-
|
|
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.
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
- **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).
|
|
28
|
-
- **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).
|
|
29
|
-
- **Server traffic:** Capture events from [Cloudflare, Cloud Run, Vercel, and WordPress](skills/canonry/references/server-side-traffic.md).
|
|
30
|
-
- **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.
|
|
31
|
-
- **Paid media:** Connect [OpenAI Ads Manager](docs/mcp.md#tool-surface) for account, conversion, campaign, and performance workflows.
|
|
32
|
-
- **Conversion measurement:** Audit [Google Ads and Google Tag Manager](docs/google-marketing.md) with read-only snapshots and declared conversion contracts.
|
|
33
|
-
- **Local presence:** Connect [Google Business Profile](skills/canonry/references/google-business-profile.md) for search terms, performance, lodging data, and booking actions.
|
|
34
|
-
- **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).
|
|
35
|
-
- **Client reporting:** Automate [scheduled checks and data syncs](skills/canonry/references/canonry-cli.md#scheduling--notifications), send webhook alerts, and generate [client-ready HTML reports](skills/canonry/references/canonry-cli.md#reports).
|
|
25
|
+
For a new installation, give your agent this setup request:
|
|
36
26
|
|
|
37
|
-
|
|
27
|
+
<details>
|
|
28
|
+
<summary>Copy the first-time setup request</summary>
|
|
38
29
|
|
|
39
|
-
|
|
30
|
+
```text
|
|
31
|
+
Help me set up Canonry for my public site.
|
|
40
32
|
|
|
41
|
-
|
|
33
|
+
Use the official Canonry docs:
|
|
34
|
+
- Agent quickstart: https://github.com/Canonry/canonry#or-use-any-shell-capable-coding-agent
|
|
35
|
+
- CLI reference: https://github.com/Canonry/canonry/blob/main/skills/canonry/references/canonry-cli.md
|
|
36
|
+
- Plugin setup: https://github.com/Canonry/canonry/blob/main/docs/plugins.md
|
|
37
|
+
- MCP setup: https://github.com/Canonry/canonry/blob/main/docs/mcp.md
|
|
38
|
+
|
|
39
|
+
If a Canonry installation or connected plugin/MCP is available, use it. Do not create a duplicate. Choose the connected tools or the shell path, not both. The `cnry` and `canonry` commands are interchangeable.
|
|
42
40
|
|
|
43
|
-
|
|
41
|
+
1. Ask for my public domain, country, and language. Do not create or scan anything yet.
|
|
42
|
+
2. If connected tools are available, use them for the remaining steps. For the shell path, make sure that `cnry` is on PATH. Then run `cnry --version`. If Canonry is missing, propose `npm install -g @canonry/canonry` and wait for approval. If configuration is missing, tell me to run `cnry bootstrap` in my private terminal and wait. Never ask me to paste passwords, API keys, OAuth credentials, or command output.
|
|
43
|
+
3. Make sure that the API or connected tool is reachable. If the shell API is unavailable, propose `cnry start`. Wait for approval. List the projects with the connected project tool or `cnry project list --format json`. Reuse a project with the same domain. Make sure that the proposed name is not assigned to a different domain. If no match exists, show the exact create operation and wait for approval.
|
|
44
|
+
4. Propose a bounded Site Health scan. Include `--max-pages` and the state of dead-link checking. Show the connected operation or exact `cnry technical-aeo run ... --wait --format json` command. Wait for separate approval before scanning.
|
|
45
|
+
5. If the run status is `completed` or `partial`, read its score and worst pages with run-pinned connected tools. For the shell path, use `cnry technical-aeo score <project> --run-id <run-id> --format json` and `cnry technical-aeo pages <project> --run-id <run-id> --sort score-asc --limit 10 --format jsonl`. If the run failed or was cancelled, inspect the run error and stop. Summarize completed evidence and propose AI Visibility setup.
|
|
46
|
+
6. Ask before you add queries, connect providers, start a provider-backed or quota-consuming run, edit files, or publish.
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
</details>
|
|
50
|
+
|
|
51
|
+
<a id="get-a-page-health-baseline"></a>
|
|
52
|
+
|
|
53
|
+
### Start locally
|
|
44
54
|
|
|
45
55
|
1. Install Canonry.
|
|
46
56
|
|
|
@@ -48,79 +58,67 @@ The dashboard, CLI, and agent tools share the same project API.
|
|
|
48
58
|
npm install -g @canonry/canonry
|
|
49
59
|
```
|
|
50
60
|
|
|
51
|
-
2.
|
|
61
|
+
2. Initialize Canonry.
|
|
52
62
|
|
|
53
63
|
```bash
|
|
54
64
|
cnry bootstrap
|
|
55
65
|
```
|
|
56
66
|
|
|
57
|
-
Keep the output private. Provider credentials are optional. Bootstrap imports supported variables that are already in your environment.
|
|
58
|
-
|
|
59
|
-
Canonry stores `config.yaml` and `data.db` in `~/.canonry` by default. Set
|
|
60
|
-
`CANONRY_CONFIG_DIR` before `bootstrap`, `serve`, and later CLI commands to
|
|
61
|
-
keep an install in another private directory; use the same value every time.
|
|
62
|
-
|
|
63
67
|
3. Start Canonry.
|
|
64
68
|
|
|
65
69
|
```bash
|
|
66
70
|
cnry serve
|
|
67
71
|
```
|
|
68
72
|
|
|
69
|
-
4. Open [http://127.0.0.1:4100/setup](http://127.0.0.1:4100/setup)
|
|
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.
|
|
70
74
|
|
|
71
|
-
|
|
75
|
+
The crawl saves a Page Health baseline. AI Visibility is optional and has a separate setup.
|
|
72
76
|
|
|
73
|
-
|
|
77
|
+
<details>
|
|
78
|
+
<summary>Scan and read results from the terminal</summary>
|
|
74
79
|
|
|
75
|
-
|
|
76
|
-
cnry project create my-site --domain example.com --country US --language en
|
|
77
|
-
cnry technical-aeo run my-site --max-pages 100 --wait --format json
|
|
78
|
-
```
|
|
80
|
+
Keep `cnry serve` active. In a second terminal, create a project and start a bounded scan:
|
|
79
81
|
|
|
80
|
-
|
|
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
|
+
```
|
|
81
86
|
|
|
82
|
-
|
|
83
|
-
cnry technical-aeo score my-site --run-id <run-id> --format json
|
|
84
|
-
cnry technical-aeo pages my-site --run-id <run-id> --sort score-asc --limit 10 --format jsonl
|
|
85
|
-
```
|
|
87
|
+
Read the run ID and status from the output. If the status is `completed` or `partial`, read evidence from that run:
|
|
86
88
|
|
|
87
|
-
|
|
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
|
+
```
|
|
88
93
|
|
|
89
|
-
|
|
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`.
|
|
90
96
|
|
|
91
|
-
|
|
97
|
+
</details>
|
|
92
98
|
|
|
93
|
-
|
|
94
|
-
<summary>Copy the Site Health-first setup request</summary>
|
|
99
|
+
## Explore the evidence
|
|
95
100
|
|
|
96
|
-
|
|
101
|
+
Your agent and dashboard use the same project API. The dashboard makes trends, exact answers, and site findings available for human review.
|
|
97
102
|
|
|
98
|
-
|
|
99
|
-
Help me set up Canonry for my public site.
|
|
103
|
+
<a id="add-ai-visibility-when-you-need-it"></a>
|
|
100
104
|
|
|
101
|
-
|
|
102
|
-
- Agent quickstart: https://github.com/Canonry/canonry#or-use-any-shell-capable-coding-agent
|
|
103
|
-
- CLI reference: https://github.com/Canonry/canonry/blob/main/skills/canonry/references/canonry-cli.md
|
|
104
|
-
- Plugin setup: https://github.com/Canonry/canonry/blob/main/docs/plugins.md
|
|
105
|
-
- MCP setup: https://github.com/Canonry/canonry/blob/main/docs/mcp.md
|
|
105
|
+
### AI visibility
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
Track brand mentions and citations over time, by query and answer engine. Find answers that cite competitors but omit your site.
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
2. If connected tools are available, use them for the remaining steps. For the shell path, make sure that `cnry` is on PATH. Then run `cnry --version`. If Canonry is missing, propose `npm install -g @canonry/canonry` and wait for approval. If configuration is missing, tell me to run `cnry bootstrap` in my private terminal and wait. Never ask me to paste passwords, API keys, OAuth credentials, or command output.
|
|
111
|
-
3. Make sure that the API or connected tool is reachable. If the shell API is unavailable, propose `cnry start`. Wait for approval. List the projects with the connected project tool or `cnry project list --format json`. Reuse a project with the same domain. Make sure that the proposed name is not assigned to a different domain. If no match exists, show the exact create operation and wait for approval.
|
|
112
|
-
4. Propose a bounded Site Health scan. Include `--max-pages` and the state of dead-link checking. Show the connected operation or exact `cnry technical-aeo run ... --wait --format json` command. Wait for separate approval before scanning.
|
|
113
|
-
5. If the run status is `completed` or `partial`, read its score and worst pages with run-pinned connected tools. For the shell path, use `cnry technical-aeo score <project> --run-id <run-id> --format json` and `cnry technical-aeo pages <project> --run-id <run-id> --sort score-asc --limit 10 --format jsonl`. If the run failed or was cancelled, inspect the run error and stop. Summarize completed evidence and propose AI Visibility setup.
|
|
114
|
-
6. Ask before you add queries, connect providers, start a provider-backed or quota-consuming run, edit files, or publish.
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
</details>
|
|
109
|
+

|
|
118
110
|
|
|
119
|
-
|
|
111
|
+
*Track your share of answer-engine brand mentions over time.*
|
|
120
112
|
|
|
121
113
|

|
|
122
114
|
|
|
123
|
-
|
|
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.
|
|
124
122
|
|
|
125
123
|
| Provider | Key source | Environment variable |
|
|
126
124
|
|---|---|---|
|
|
@@ -130,7 +128,7 @@ If a Canonry installation or connected plugin/MCP is available, use it. Do not c
|
|
|
130
128
|
| Perplexity | [Perplexity settings](https://www.perplexity.ai/settings/api) | `PERPLEXITY_API_KEY` |
|
|
131
129
|
| Local model | Any OpenAI-compatible endpoint | `LOCAL_BASE_URL` |
|
|
132
130
|
|
|
133
|
-
|
|
131
|
+
Add the queries that matter to your project:
|
|
134
132
|
|
|
135
133
|
```bash
|
|
136
134
|
cnry query add my-site "your first query" "your second query"
|
|
@@ -138,45 +136,113 @@ cnry run my-site --wait
|
|
|
138
136
|
cnry visibility-stats my-site --by-provider
|
|
139
137
|
```
|
|
140
138
|
|
|
141
|
-
|
|
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>
|
|
145
|
+
|
|
146
|
+
Canonry uses direct provider APIs to capture answers, citations, and available
|
|
147
|
+
search details that model routers can omit. This richer data helps us measure
|
|
148
|
+
brand mentions and website citations accurately and explain changes in your
|
|
149
|
+
visibility.
|
|
150
|
+
[Read more about accuracy and why we chose models and direct provider API adapters individually](docs/providers/model-selection.md).
|
|
151
|
+
|
|
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.
|
|
142
170
|
|
|
143
|
-
|
|
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
|
+

|
|
181
|
+
|
|
182
|
+
*Map crawlable pages and the internal links that connect them.*
|
|
183
|
+
|
|
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 |
|
|
144
203
|
|---|---|
|
|
145
|
-
| **
|
|
146
|
-
| **
|
|
147
|
-
| **
|
|
148
|
-
| **
|
|
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
|
|
149
217
|
|
|
150
|
-
|
|
218
|
+
Canonry is single-tenant. Run one instance for one operator or team. Keep unrelated teams on separate instances.
|
|
151
219
|
|
|
152
|
-
|
|
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`.
|
|
153
222
|
|
|
154
223
|
See the [deployment guide](docs/deployment.md) for reverse proxies, daemon mode, Docker, systemd, and Tailscale.
|
|
155
224
|
|
|
156
|
-
##
|
|
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>
|
|
157
234
|
|
|
158
|
-
|
|
235
|
+
<details>
|
|
236
|
+
<summary>Troubleshoot installation, scans, and visibility results</summary>
|
|
237
|
+
|
|
238
|
+
| Problem | Next step |
|
|
159
239
|
|---|---|
|
|
160
|
-
| Site scan is still
|
|
161
|
-
| Site scan failed | Read the error with `cnry run show <run-id> --format json`. Read the last phase and counters with the progress command
|
|
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. |
|
|
162
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. |
|
|
163
|
-
| 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. |
|
|
164
|
-
| Need one-off research | Run `cnry research run <project> "query one" "query two" --wait`. Research does not change the tracked basket. |
|
|
165
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)). |
|
|
166
244
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
| | |
|
|
170
|
-
|---|---|
|
|
171
|
-
| **Architecture & data model** | [docs/architecture.md](docs/architecture.md) · [docs/data-model.md](docs/data-model.md) |
|
|
172
|
-
| **Aero — built-in agent** | [skills/aero/SKILL.md](skills/aero/SKILL.md) |
|
|
173
|
-
| **Agent Plugin — portable core + Codex / Claude adapters** | [docs/plugins.md](docs/plugins.md) |
|
|
174
|
-
| **MCP — Claude Desktop / Cursor / Codex** | [docs/mcp.md](docs/mcp.md) |
|
|
175
|
-
| **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) |
|
|
176
|
-
| **Deployment** — reverse proxies, Docker, systemd, Tailscale | [docs/deployment.md](docs/deployment.md) |
|
|
177
|
-
| **API** | `GET /api/v1/openapi.json` |
|
|
178
|
-
| **Standalone skills bundle** for Claude Code / Codex | `cnry skills install` ([details](skills/canonry/SKILL.md)) |
|
|
179
|
-
| **All docs** | [docs/README.md](docs/README.md) |
|
|
245
|
+
</details>
|
|
180
246
|
|
|
181
247
|
## Contributing
|
|
182
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
|
|
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
|
|
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-
|
|
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-Dock0eH1.js";import{cT as _,ah as H,cU as U,cV as V,cW as Y,f as x,T as j,j as u,B as w,l as F,cX as K,cY as X,ak as Z,cZ as J}from"./index-BlILyZJ5.js";import{D as ee,T as se,E as te,P as ae,v as ne,t as re}from"./vendor-lucide-nYKv-BB7.js";import"./vendor-radix-4UOaVWza.js";import"./vendor-recharts-D8rt5lKn.js";import"./vendor-markdown-Bn7fRwrx.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,$]=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(""),[T,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,O]=await Promise.all([_(),H().catch(()=>null),U().catch(()=>[]),V().catch(()=>[]),Y().catch(()=>null)]);h(s),i(r),y(W),$(z),L(O)}catch(s){l(s instanceof Error?s.message:"Failed to load backlinks status")}finally{D(!1)}},[]);n.useEffect(()=>{f()},[f]);async function M(){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:F(M),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:F(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:T?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 i}from"./vendor-tanstack-
|
|
1
|
+
import{j as i}from"./vendor-tanstack-CYBjbqVr.js";import{R as k,C,a as R,e as A,X as I,Y as _,T as D,d as m}from"./vendor-recharts-FA0uySU7.js";const L={contentStyle:{backgroundColor:"var(--chart-tooltip-bg, #18181b)",border:"1px solid var(--chart-tooltip-border, #3f3f46)",borderRadius:8,fontSize:12},labelStyle:{color:"var(--chart-tooltip-label, #e4e4e7)"},itemStyle:{color:"var(--chart-tooltip-item, #a1a1aa)"}},v={fill:"var(--chart-neutral-text-dim, #8b8b93)",fontSize:11},O="var(--chart-grid, #27272a)",b="var(--chart-axis, #27272a)",p=["var(--chart-series-1, #34d399)","var(--chart-series-2, #60a5fa)","var(--chart-series-3, #f472b6)","var(--chart-series-4, #facc15)","var(--chart-series-5, #a78bfa)","var(--chart-series-6, #fb923c)","var(--chart-series-7, #22d3ee)","var(--chart-series-8, #f87171)"],j={gemini:"#60a5fa",openai:"#34d399",claude:"#fb923c",perplexity:"#22d3ee",local:"#a78bfa"};function $(r,a=0){return j[r]??p[a%p.length]}const K={text:"var(--chart-neutral-text, #a1a1aa)",textDim:"var(--chart-neutral-text-dim, #8b8b93)",textFaint:"var(--chart-neutral-text-faint, #52525b)",surface:"var(--chart-neutral-surface, #27272a)",trackSubtle:"var(--chart-neutral-track-subtle, rgb(255 255 255 / 0.04))"},H={positive:"var(--chart-tone-positive, #34d399)",positiveDeep:"var(--chart-tone-positive-deep, #10b981)",caution:"var(--chart-tone-caution, #fbbf24)",negative:"var(--chart-tone-negative, #fb7185)"};function N(r){return r}function y(r){const a=/^(\d{4})-(\d{2})-(\d{2})/.exec(r);return a?{year:Number(a[1]),month:Number(a[2]),day:Number(a[3])}:null}function M(r){const a=String(r),e=y(a);return e?new Date(Date.UTC(e.year,e.month-1,e.day)).toLocaleDateString(void 0,{timeZone:"UTC",month:"short",day:"numeric",year:"numeric"}):a}function F(r){const a=y(String(r));return a?`${a.month}/${a.day}`:String(r)}function G(r){return new Date(r).toLocaleDateString(void 0,{month:"short",day:"numeric",year:"numeric"})}function P(r){const a=new Date(r);return`${a.getMonth()+1}/${a.getDate()}`}function U({data:r,xKey:a,series:e,height:x=260,xTickFormatter:g,labelFormatter:f,onSelectX:d,selectedX:u}){const h=[...new Set(e.map(t=>t.axisId))],S=new Map(e.map(t=>[t.label,t.formatValue])),T=r.map((t,s)=>{const n={...t};for(const o of e){if(!o.trend)continue;const c=o.trend.startIndex??0,l=o.trend.endIndex??r.length-1;if(s<c||s>l){n[`${o.dataKey}__trend`]=null;continue}n[`${o.dataKey}__trend`]=l<=c?o.trend.start:o.trend.start+(o.trend.end-o.trend.start)*(s-c)/(l-c)}return n});return i.jsx(k,{width:"100%",height:x,children:i.jsxs(C,{data:T,margin:{top:8,right:8,bottom:4,left:0},onClick:d?t=>{t?.activeLabel!==void 0&&d(String(t.activeLabel))}:void 0,style:d?{cursor:"pointer"}:void 0,children:[i.jsx(R,{stroke:O,strokeDasharray:"3 3",vertical:!1}),u!=null&&i.jsx(A,{x:u,yAxisId:h[0],stroke:K.text,strokeDasharray:"2 3",strokeWidth:1}),i.jsx(I,{dataKey:a,tick:v,stroke:b,tickFormatter:g,minTickGap:24}),h.map((t,s)=>{const n=e.find(o=>o.axisId===t);return i.jsx(_,{yAxisId:t,orientation:s===1?"right":"left",hide:s>1,reversed:n.inverted??!1,domain:n.inverted?[1,"auto"]:[0,"auto"],tick:v,stroke:b,width:48,tickFormatter:n.formatValue},t)}),i.jsx(D,{...L,labelFormatter:t=>f?f(String(t)):String(t),formatter:(t,s)=>{const n=String(s??"");return typeof t!="number"?[String(t??""),n]:[S.get(n)?.(t)??t.toLocaleString(),n]}}),e.filter(t=>t.trend).map(t=>i.jsx(m,{yAxisId:t.axisId,dataKey:`${t.dataKey}__trend`,name:`${t.label} trend`,stroke:t.color,strokeWidth:1.5,strokeDasharray:"5 4",strokeOpacity:.55,dot:!1,activeDot:!1,isAnimationActive:!1,legendType:"none",tooltipType:"none"},`${t.dataKey}-trend`)),e.map(t=>i.jsx(m,{yAxisId:t.axisId,dataKey:t.dataKey,name:t.label,stroke:t.color,strokeWidth:2,dot:!1,activeDot:{r:3},connectNulls:!1,isAnimationActive:!1},t.dataKey))]})})}export{O as C,U as M,b as a,v as b,M as c,L as d,p as e,F as f,K as g,H as h,P as i,G as j,N as o,$ as p};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as e}from"./vendor-tanstack-
|
|
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};
|