@canonry/canonry 4.159.0 → 4.161.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 +1 -1
- package/assets/agent-workspace/skills/aero/SKILL.md +16 -1
- package/assets/agent-workspace/skills/canonry/SKILL.md +17 -5
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +66 -0
- package/assets/agent-workspace/skills/canonry/references/server-side-traffic.md +231 -62
- package/assets/assets/{AuditHistoryPanel-BiloGcQA.js → AuditHistoryPanel-C_1twF00.js} +1 -1
- package/assets/assets/{BacklinksPage-CPM1O2bC.js → BacklinksPage-CfAuMCaN.js} +1 -1
- package/assets/assets/{HistoryPage-1YOjtdJy.js → HistoryPage-DrRxOL3j.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-CVDTY_gC.js → MeasurementPropertyPage-BFtxx5Jw.js} +1 -1
- package/assets/assets/{ProjectPage-DnGXMx1n.js → ProjectPage-Djvhx4HD.js} +9 -9
- package/assets/assets/{RunRow-fIaadH4h.js → RunRow-DVZXibR2.js} +1 -1
- package/assets/assets/{RunsPage-DfifdM5z.js → RunsPage-CAUMy7By.js} +1 -1
- package/assets/assets/{SettingsPage-efA2EEmS.js → SettingsPage-CcSOmQAl.js} +1 -1
- package/assets/assets/{SiteHealthSection-BROtQGfu.js → SiteHealthSection-DYjEWd1I.js} +3 -3
- package/assets/assets/{TrafficPage-Wk43Bm_q.js → TrafficPage-BWM33jnJ.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-BhBvoLWu.js → TrafficSourceDetailPage-DvEZ_JGY.js} +1 -1
- package/assets/assets/{extract-error-message-YGzZFvWs.js → extract-error-message-CnKOJkYB.js} +1 -1
- package/assets/assets/{index-DmsIZQAm.js → index-CQGAqmDx.js} +22 -22
- package/assets/assets/{react-sigma_core.esm.min-DM9XptbM.js → react-sigma_core.esm.min-CHCAIMBW.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-ZL3VY435.js → chunk-EXDO7EPL.js} +104 -9
- package/dist/{chunk-ZEG43TY2.js → chunk-HCGTKQCG.js} +21 -1
- package/dist/{chunk-5AFWHKKI.js → chunk-J27J2DF6.js} +2095 -703
- package/dist/{chunk-BZPBVOAM.js → chunk-W6OWEPZG.js} +70 -30
- package/dist/cli.js +308 -124
- package/dist/index.d.ts +28 -14
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-DHY5CYWX.js → intelligence-service-HKDIVTZB.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -94,7 +94,7 @@ Configure during `cnry init`, in the dashboard `/settings`, or as env vars.
|
|
|
94
94
|
| **Aero — built-in agent** | [skills/aero/SKILL.md](skills/aero/SKILL.md) |
|
|
95
95
|
| **Agent Plugin — portable core + Codex / Claude adapters** | [docs/plugins.md](docs/plugins.md) |
|
|
96
96
|
| **MCP — Claude Desktop / Cursor / Codex** | [docs/mcp.md](docs/mcp.md) |
|
|
97
|
-
| **Integrations** | [GSC](docs/google-search-console-setup.md) · [GA4](docs/google-analytics-setup.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, Cloud Run, Vercel, WordPress)](skills/canonry/references/server-side-traffic.md) |
|
|
97
|
+
| **Integrations** | [GSC](docs/google-search-console-setup.md) · [GA4](docs/google-analytics-setup.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) |
|
|
98
98
|
| **Deployment** — Docker, Railway, Render, systemd, Tailscale | [docs/deployment.md](docs/deployment.md) |
|
|
99
99
|
| **API** — 118+ endpoints | `GET /api/v1/openapi.json` (no auth) |
|
|
100
100
|
| **Standalone skills bundle** for Claude Code / Codex | `cnry skills install` ([details](skills/canonry/SKILL.md)) |
|
|
@@ -16,7 +16,22 @@ Persist only *user-scoped* context (operator preferences, communication style) i
|
|
|
16
16
|
|
|
17
17
|
**Two signals, not one.** Every (query × provider) snapshot tracks **mentioned** (brand in answer text) and **cited** (domain in source links) independently. Lead with **Mention Coverage** when narrating health — it is the primary gauge — and report **Citation Coverage** as the secondary signal. Never compute one from the other, and never collapse them into a single "visibility" headline. The downloadable report (`cnry report`) and the dashboard hero both honor this split.
|
|
18
18
|
|
|
19
|
-
When a project has GA4 connected, traffic is a first-class signal alongside
|
|
19
|
+
When a project has GA4 connected, traffic is a first-class signal alongside
|
|
20
|
+
mentions and citations. Use `cnry ga traffic` and `cnry ga attribution --trend`
|
|
21
|
+
for the current snapshot. Use the GA referral-history commands for daily series.
|
|
22
|
+
Before you quote GA4 data, make sure that `cnry ga status` has a recent
|
|
23
|
+
`lastSyncedAt`. If it is stale, get approval before you run `cnry ga sync`.
|
|
24
|
+
|
|
25
|
+
For Cloud Run, WordPress, Vercel, or Cloudflare, use `cnry traffic status` and
|
|
26
|
+
`cnry traffic events` for crawler and AI-referral evidence. Read the Cloudflare
|
|
27
|
+
`deliveryMode` before you recommend an action. Direct push does not use
|
|
28
|
+
`traffic sync`. Queue pull freshness requires an enabled `traffic-sync`
|
|
29
|
+
schedule. Run the `traffic.source.*` doctor checks. Inspect
|
|
30
|
+
`traffic.source.queue-backlog` before you quote current Queue data. If more than
|
|
31
|
+
1,000 messages remain, report that one default tick cannot drain the backlog.
|
|
32
|
+
Get approval before you run a manual sync or change the schedule. The full
|
|
33
|
+
command reference is in the co-installed
|
|
34
|
+
`canonry/references/canonry-cli.md`.
|
|
20
35
|
|
|
21
36
|
**Diagnosing a stuck Vercel/Cloud Run source:** if `cnry traffic status` shows `status=error` with a recent `lastError` of `refusing to advance` or `ExceedsBillingLimitError`, the source's `lastSyncedAt` has aged past the upstream retention boundary and every sync now throws. Recovery: `cnry traffic reset <project> --source <id> --advance-to-now`. This advances `lastSyncedAt` to NOW and resumes going-forward syncs — historical events in the gap are unrecoverable from the sync path; run `cnry traffic backfill --days N` separately if any of that history is needed (capped at retention).
|
|
22
37
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: canonry
|
|
3
|
-
description: "Set up and operate Canonry AEO projects
|
|
3
|
+
description: "Set up and operate Canonry AEO projects. Inspect mention and citation coverage, diagnose regressions, and run technical audits. Connect Cloudflare direct-push or queue-pull traffic. Act through the Canonry CLI or MCP tools. Examples: inspect project acme coverage (run + report), diagnose query drift via attribution (report.html + visibility-attribution), or submit GSC sitemaps (gsc-sitemap-submission batched)."
|
|
4
4
|
compatibility: Requires Node.js 22.14+ and globally installed @canonry/canonry; canonry-mcp must be on PATH.
|
|
5
5
|
metadata:
|
|
6
6
|
agent: >-
|
|
@@ -131,10 +131,22 @@ GA4 is a first-class signal alongside citation tracking. Connect once with `cnry
|
|
|
131
131
|
|
|
132
132
|
When the project ships behind a server you control, connect Cloud Run, WordPress,
|
|
133
133
|
or Vercel. `cnry traffic sync` pulls and classifies their logs into hourly
|
|
134
|
-
buckets. For
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
134
|
+
buckets. For Cloudflare, choose `direct-push` or `queue-pull` on the local,
|
|
135
|
+
credential-owning host. Direct push requires a public Canonry HTTPS receiver.
|
|
136
|
+
It does not use `traffic sync`. Queue pull sends events to a Cloudflare Queue,
|
|
137
|
+
then Canonry drains it through `traffic sync` and the `traffic-sync` schedule.
|
|
138
|
+
|
|
139
|
+
If the connect response reports `activationRequired`, attach the Worker route
|
|
140
|
+
first. Then run `cnry traffic activate <project> --source <id>`. Activation
|
|
141
|
+
pauses all sibling sources. It also moves the one `traffic-sync` schedule to
|
|
142
|
+
Queue pull. A first source becomes active automatically.
|
|
143
|
+
|
|
144
|
+
Each default Queue tick drains at most 1,000 messages. Use `cnry traffic status`
|
|
145
|
+
and the `traffic.source.queue-backlog` doctor check to read the backlog. If more
|
|
146
|
+
than 1,000 messages remain, get approval before a manual sync or a shorter
|
|
147
|
+
schedule interval. Use `cnry traffic events`, `cnry traffic sources`, and
|
|
148
|
+
`cnry traffic status` for every adapter. See
|
|
149
|
+
`references/server-side-traffic.md` for setup and smoke tests.
|
|
138
150
|
|
|
139
151
|
**Vercel gotcha:** a freshly connected Vercel source captures only going-forward traffic — `lastSyncedAt` is seeded to NOW to avoid the 30-day default window exceeding Vercel's ~14-day request-logs retention (which would otherwise throw on every first sync). Use `cnry traffic backfill <project> --source <id> --days N` for historical recovery. If an idle Vercel/Cloud Run source has been failing long enough that `lastSyncedAt` aged past retention, unstick it with `cnry traffic reset <project> --source <id> --advance-to-now`.
|
|
140
152
|
|
|
@@ -564,6 +564,72 @@ cnry wordpress onboard <project> --url <url> --user <user> # full onboarding wo
|
|
|
564
564
|
|
|
565
565
|
**Onboard** runs: connect → audit → set-meta → schema deploy → Google submit → Bing submit. Use `--skip-schema` or `--skip-submit` to skip steps. `--profile <file>` provides business data and page-to-schema mapping for schema deployment.
|
|
566
566
|
|
|
567
|
+
## Server-Side Traffic
|
|
568
|
+
|
|
569
|
+
Cloudflare supports direct push and Queue pull. Run Cloudflare connect only from
|
|
570
|
+
the local host that owns the Canonry configuration and Wrangler profile. This
|
|
571
|
+
operation is not available through MCP because it uses local credentials.
|
|
572
|
+
|
|
573
|
+
```bash
|
|
574
|
+
# Direct push to a stable public Canonry HTTPS receiver:
|
|
575
|
+
cnry traffic connect cloudflare <project> \
|
|
576
|
+
--delivery-mode direct-push \
|
|
577
|
+
--zone-id <zone-id> --account-id <account-id> \
|
|
578
|
+
--deploy --confirm-route --confirm-fail-open
|
|
579
|
+
|
|
580
|
+
# Create the Queue and enable its HTTP pull consumer first:
|
|
581
|
+
wrangler queues create canonry-traffic-<project>
|
|
582
|
+
# Workers Paid only: use this command to change the four-day default.
|
|
583
|
+
wrangler queues update canonry-traffic-<project> \
|
|
584
|
+
--message-retention-period-secs <seconds>
|
|
585
|
+
wrangler queues info canonry-traffic-<project>
|
|
586
|
+
wrangler queues consumer http add canonry-traffic-<project>
|
|
587
|
+
|
|
588
|
+
# Queue pull keeps the API token in the local Canonry credential store:
|
|
589
|
+
cnry traffic connect cloudflare <project> \
|
|
590
|
+
--delivery-mode queue-pull \
|
|
591
|
+
--zone-id <zone-id> --account-id <account-id> \
|
|
592
|
+
--queue-id <queue-id> --queue-name canonry-traffic-<project> \
|
|
593
|
+
--api-token-file <mode-0600-token-file> \
|
|
594
|
+
--retention-seconds <actual-queue-retention-seconds> \
|
|
595
|
+
--deploy --confirm-route --confirm-fail-open
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
Workers Free retention is fixed at `86400` seconds. Workers Paid defaults to
|
|
599
|
+
`345600` seconds. If you change paid retention with `wrangler queues update`,
|
|
600
|
+
pass the same value to Canonry. The Canonry flag does not change the Queue.
|
|
601
|
+
|
|
602
|
+
Both commands deploy an unattached Worker. Attach the exact site route in the
|
|
603
|
+
Cloudflare Dashboard. Then set its request-limit failure mode to **Fail open**.
|
|
604
|
+
Do not put the Queue API token on the command line or in an agent transcript.
|
|
605
|
+
|
|
606
|
+
```bash
|
|
607
|
+
# If connect reports activationRequired, activate after the route is live:
|
|
608
|
+
cnry traffic activate <project> --source <source-id>
|
|
609
|
+
cnry traffic sync <project> --source <source-id> # pull adapters, including Cloudflare Queue pull
|
|
610
|
+
cnry traffic sources <project> --format json
|
|
611
|
+
cnry traffic status <project> --format json
|
|
612
|
+
cnry traffic events <project> --source <source-id> --format json
|
|
613
|
+
|
|
614
|
+
cnry doctor --project <project> --check 'traffic.source.*' --format json
|
|
615
|
+
cnry schedule show <project> --kind traffic-sync --format json
|
|
616
|
+
cnry schedule set <project> --kind traffic-sync \
|
|
617
|
+
--source <source-id> --cron "*/10 * * * *"
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
A first source becomes active automatically. A staged source stays paused until
|
|
621
|
+
the explicit activation command. Activation pauses sibling sources and moves the
|
|
622
|
+
one `traffic-sync` schedule for the project. Direct push rejects `traffic sync` and
|
|
623
|
+
does not use this schedule.
|
|
624
|
+
|
|
625
|
+
Queue pull drains at most 1,000 messages in one default sync. The doctor warns
|
|
626
|
+
when the remaining backlog is more than 1,000 messages. If the operator approves
|
|
627
|
+
a manual drain, run a manual sync. If the backlog recurs, get approval to shorten
|
|
628
|
+
the schedule interval.
|
|
629
|
+
|
|
630
|
+
Read the [server-side traffic guide](server-side-traffic.md) for token safety,
|
|
631
|
+
route checks, activation order, smoke tests, rollback, and troubleshooting.
|
|
632
|
+
|
|
567
633
|
## Google Analytics 4
|
|
568
634
|
|
|
569
635
|
GA4 integration uses service account authentication (no OAuth). The service account must have Viewer access on the GA4 property. `ga sync` writes to four DB tables (`gaTrafficSnapshots`, `gaAiReferrals`, `gaSocialReferrals`, `gaTrafficSummaries`); every subsequent read command queries the local store rather than re-fetching from GA4, so reads are fast and quotaless. AI-referral rows are tracked across 10 known providers (chatgpt, perplexity, claude, gemini, openai, anthropic, copilot, phind, you.com, meta.ai), three GA4 attribution dimensions (`session` / `first_user` / `manual_utm`), and joined to landing pages. Social referrals are split Organic vs Paid via GA4's `sessionDefaultChannelGroup`. All commands support `--format json`.
|
|
@@ -39,11 +39,12 @@ Adapters today:
|
|
|
39
39
|
| [`cloud-run`](#connecting-a-cloud-run-source) | Pull | GCP Cloud Run request logs via Logging API | Any service running on Cloud Run |
|
|
40
40
|
| [`wordpress`](#connecting-a-wordpress-source) | Pull | Canonry Traffic Logger REST endpoint | WordPress sites where you control wp-admin |
|
|
41
41
|
| [`vercel`](#connecting-a-vercel-source) | Pull | Vercel project logs via the Vercel API | Sites deployed on Vercel |
|
|
42
|
-
| [`cloudflare`](#connecting-a-cloudflare-source
|
|
42
|
+
| [`cloudflare`](#connecting-a-cloudflare-source) | Direct push or Queue pull | A zone Worker selects edge events; Canonry either receives them directly or pulls them from Cloudflare Queues | Sites whose public traffic passes through Cloudflare |
|
|
43
43
|
|
|
44
44
|
Future adapters slot in by implementing the same contract.
|
|
45
|
-
Cloud Run, WordPress, and
|
|
46
|
-
Cloudflare sends selected events from its Worker
|
|
45
|
+
Cloud Run, WordPress, Vercel, and Cloudflare Queue sources pull during
|
|
46
|
+
`traffic sync`. Cloudflare direct push sends selected events from its Worker
|
|
47
|
+
and does not use `traffic sync`.
|
|
47
48
|
|
|
48
49
|
## Connecting a Cloud Run source
|
|
49
50
|
|
|
@@ -195,31 +196,38 @@ Run `cnry traffic backfill <project> --source <id> --days N` (capped at
|
|
|
195
196
|
history. It's an explicit operator action; the connect flow never pulls
|
|
196
197
|
it implicitly.
|
|
197
198
|
|
|
198
|
-
## Connecting a Cloudflare source
|
|
199
|
+
## Connecting a Cloudflare source
|
|
199
200
|
|
|
200
|
-
Cloudflare
|
|
201
|
-
zone route. It applies a broad AI filter and
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
201
|
+
Both Cloudflare modes run the same small ES-module Worker on the site's exact
|
|
202
|
+
zone route. It applies a broad AI filter and delivers selected edge events in
|
|
203
|
+
`ctx.waitUntil()`. The origin response remains independent from filtering,
|
|
204
|
+
scheduling, and delivery errors. Both modes use the same
|
|
205
|
+
`CloudflareEdgeEventBatch`, normalizer, classifier, receipts, and rollups.
|
|
205
206
|
|
|
206
|
-
|
|
207
|
-
|
|
207
|
+
- `direct-push` sends a signed batch to a public Canonry HTTPS receiver.
|
|
208
|
+
- `queue-pull` sends the batch to a Cloudflare Queue. The single-team Canonry
|
|
209
|
+
server pulls and acknowledges it through the Cloudflare Queues HTTP API.
|
|
210
|
+
|
|
211
|
+
The adapter does not pull Cloudflare analytics or request logs.
|
|
208
212
|
|
|
209
213
|
```mermaid
|
|
210
214
|
flowchart LR
|
|
211
215
|
request["Site request"] --> worker["Canonry Cloudflare Worker"]
|
|
212
216
|
worker --> origin["Existing origin or Pages site"]
|
|
213
217
|
origin --> response["Unchanged site response"]
|
|
214
|
-
worker -. "
|
|
215
|
-
|
|
218
|
+
worker -. "direct-push" .-> ingest["Canonry public HTTPS ingest"]
|
|
219
|
+
worker -. "queue-pull" .-> queue["Cloudflare Queue"]
|
|
220
|
+
queue --> pull["Canonry scheduled pull"]
|
|
221
|
+
ingest --> classify["Shared receipt and traffic pipeline"]
|
|
222
|
+
pull --> classify
|
|
216
223
|
classify --> rollups["Hourly crawler, user-fetch, and referral rollups"]
|
|
217
224
|
```
|
|
218
225
|
|
|
219
226
|
### Setup flow
|
|
220
227
|
|
|
221
228
|
1. Create or select a Canonry project with one exact canonical hostname.
|
|
222
|
-
2.
|
|
229
|
+
2. Choose delivery: direct push needs a stable public Canonry HTTPS URL outside
|
|
230
|
+
the site route; Queue pull needs a pre-created Queue and HTTP pull consumer.
|
|
223
231
|
3. Authenticate Wrangler with the Cloudflare account that owns the zone.
|
|
224
232
|
4. Inspect existing Worker routes for the exact canonical hostname.
|
|
225
233
|
5. Run the local Cloudflare connect command with `--deploy`.
|
|
@@ -228,20 +236,23 @@ flowchart LR
|
|
|
228
236
|
|
|
229
237
|
### Current support boundary
|
|
230
238
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
239
|
+
Both modes currently target one local, single-team `canonry serve` instance.
|
|
240
|
+
The Queue consumer is not a multi-tenant Cloudflare control plane. The CLI and
|
|
241
|
+
server must read the same `~/.canonry/config.yaml` credential store. Direct
|
|
242
|
+
push requires a stable public HTTPS receiver; Queue pull does not.
|
|
234
243
|
|
|
235
244
|
The Cloud Run `apps/api` service has no Cloudflare credential store.
|
|
236
|
-
Therefore, `apps/api` cannot retain
|
|
237
|
-
|
|
245
|
+
Therefore, `apps/api` cannot retain direct-push bearer/HMAC values or the
|
|
246
|
+
Queue API token.
|
|
238
247
|
|
|
239
|
-
CAUTION: Use one authoritative server-traffic source for the canonical
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
248
|
+
CAUTION: Use one authoritative server-traffic source for the canonical site.
|
|
249
|
+
Connecting any adapter while a sibling source is active creates or keeps a
|
|
250
|
+
paused staged source. `canonry traffic activate` validates the target's local
|
|
251
|
+
credential, atomically pauses every sibling, and moves or removes the one
|
|
252
|
+
traffic-sync schedule. Overlapping adapters outside this cutover can still
|
|
253
|
+
double-count project totals.
|
|
243
254
|
|
|
244
|
-
### Expose the Canonry receiver
|
|
255
|
+
### Expose the Canonry receiver (direct push only)
|
|
245
256
|
|
|
246
257
|
Complete the local dashboard setup and set its password before you expose
|
|
247
258
|
Canonry. A tunnel makes the loopback server reachable from the public Internet.
|
|
@@ -342,18 +353,20 @@ Worker and keeps the origin available. See [Cloudflare Workers limits](https://d
|
|
|
342
353
|
Free-plan classification uses user-agent, referer, and UTM evidence. Granular
|
|
343
354
|
`request.cf.botManagement` scores require the Enterprise Bot Management add-on.
|
|
344
355
|
|
|
345
|
-
### Generate or deploy
|
|
356
|
+
### Generate or deploy direct push
|
|
346
357
|
|
|
347
358
|
```bash
|
|
348
359
|
# Generate secret-free worker.js and wrangler.toml in
|
|
349
360
|
# ./canonry-cloudflare-<project-slug>:
|
|
350
361
|
cnry traffic connect cloudflare <project> \
|
|
362
|
+
--delivery-mode direct-push \
|
|
351
363
|
--zone-id <cloudflare-zone-id> \
|
|
352
364
|
--account-id <cloudflare-account-id>
|
|
353
365
|
|
|
354
366
|
# Deploy only the Worker after the route preflight.
|
|
355
367
|
# Both safety acknowledgements are mandatory:
|
|
356
368
|
cnry traffic connect cloudflare <project> \
|
|
369
|
+
--delivery-mode direct-push \
|
|
357
370
|
--zone-id <cloudflare-zone-id> \
|
|
358
371
|
--account-id <cloudflare-account-id> \
|
|
359
372
|
--deploy --confirm-route --confirm-fail-open
|
|
@@ -413,6 +426,112 @@ Cloudflare connect is absent from MCP because deployment reads local
|
|
|
413
426
|
secrets. An agent can guide the CLI command. It must not request, print,
|
|
414
427
|
or send either secret through chat.
|
|
415
428
|
|
|
429
|
+
### Generate or deploy Queue pull
|
|
430
|
+
|
|
431
|
+
Queue pull reuses the same edge filter without requiring a public Canonry
|
|
432
|
+
receiver. Create the Queue first. Then use `wrangler queues info` to get its ID.
|
|
433
|
+
Create a Cloudflare API token with **Account Queues Edit** for the owning
|
|
434
|
+
account. Put only the token in a mode-0600 file. Keep that file off shell argv.
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
wrangler queues create canonry-traffic-<project>
|
|
438
|
+
|
|
439
|
+
# Workers Paid only: use this command to change the four-day default.
|
|
440
|
+
wrangler queues update canonry-traffic-<project> \
|
|
441
|
+
--message-retention-period-secs <seconds>
|
|
442
|
+
|
|
443
|
+
wrangler queues info canonry-traffic-<project>
|
|
444
|
+
wrangler queues consumer http add canonry-traffic-<project>
|
|
445
|
+
|
|
446
|
+
cnry traffic connect cloudflare <project> \
|
|
447
|
+
--delivery-mode queue-pull \
|
|
448
|
+
--zone-id <cloudflare-zone-id> \
|
|
449
|
+
--account-id <cloudflare-account-id> \
|
|
450
|
+
--queue-id <cloudflare-queue-id> \
|
|
451
|
+
--queue-name canonry-traffic-<project> \
|
|
452
|
+
--api-token-file <path-to-mode-0600-token-file> \
|
|
453
|
+
--retention-seconds <actual-queue-retention-seconds>
|
|
454
|
+
|
|
455
|
+
cnry traffic connect cloudflare <project> \
|
|
456
|
+
--delivery-mode queue-pull \
|
|
457
|
+
--zone-id <cloudflare-zone-id> \
|
|
458
|
+
--account-id <cloudflare-account-id> \
|
|
459
|
+
--queue-id <cloudflare-queue-id> \
|
|
460
|
+
--queue-name canonry-traffic-<project> \
|
|
461
|
+
--api-token-file <path-to-token-file> \
|
|
462
|
+
--retention-seconds <actual-queue-retention-seconds> \
|
|
463
|
+
--deploy --confirm-route --confirm-fail-open
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
Read the Queue ID from the `wrangler queues info` output. Pass that exact value
|
|
467
|
+
to `--queue-id`.
|
|
468
|
+
|
|
469
|
+
The `queues update` command sets Cloudflare retention on Workers Paid. The
|
|
470
|
+
Canonry `--retention-seconds` option only records the actual value. It does not
|
|
471
|
+
change the Queue. Use `86400` for Workers Free, where retention is fixed at one
|
|
472
|
+
day. Workers Paid defaults to `345600` (four days). Paid accounts accept values
|
|
473
|
+
from 60 seconds through 14 days. If you change the paid Queue, use the same
|
|
474
|
+
value in both commands. See Cloudflare's [Queue limits](https://developers.cloudflare.com/queues/platform/limits/).
|
|
475
|
+
|
|
476
|
+
The HTTP-consumer command is required. The producer binding does not enable
|
|
477
|
+
HTTP pull. Cloudflare does not support an HTTP pull consumer in
|
|
478
|
+
`wrangler.toml`. Remove any existing Worker consumer before you enable HTTP
|
|
479
|
+
pull. One Queue cannot use both consumer types at the same time. See
|
|
480
|
+
Cloudflare's [pull-consumer setup](https://developers.cloudflare.com/queues/configuration/pull-consumers/).
|
|
481
|
+
|
|
482
|
+
The token stays only in Canonry's local credential store. It is not written to
|
|
483
|
+
the source row, Worker, TOML, command output, MCP, or Worker bindings. Wrangler
|
|
484
|
+
authentication remains the operator's active Wrangler profile. Canonry does
|
|
485
|
+
not reuse the Queue pull token for deployment.
|
|
486
|
+
|
|
487
|
+
The generated TOML contains one `[[queues.producers]]` binding named
|
|
488
|
+
`CANONRY_TRAFFIC_QUEUE` and no direct-push ingest URL or Worker secrets. After
|
|
489
|
+
the Worker is deployed and the route is ready, activate a staged source:
|
|
490
|
+
|
|
491
|
+
```bash
|
|
492
|
+
cnry traffic activate <project> --source <source-id>
|
|
493
|
+
cnry traffic sync <project> --source <source-id>
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
A new project with no other active traffic source activates immediately. A
|
|
497
|
+
mode change stays paused until the explicit activation command. Activation
|
|
498
|
+
pauses the prior source and points the project's one `traffic-sync` schedule at
|
|
499
|
+
Queue pull; activating direct push removes that Queue schedule.
|
|
500
|
+
|
|
501
|
+
For Queue-to-direct rollback, drain Queue pull until its backlog is empty,
|
|
502
|
+
switch the Cloudflare route, then activate direct push immediately. The route
|
|
503
|
+
change and Canonry activation are separate operator actions, so keep the
|
|
504
|
+
cutover window short.
|
|
505
|
+
|
|
506
|
+
### Queue-pull acceptance test
|
|
507
|
+
|
|
508
|
+
Use this test after the Queue-pull source is active and the Worker route is
|
|
509
|
+
attached. The UTM request provides a deterministic classification signal.
|
|
510
|
+
If connect reports `activationRequired`, activate the source before this test:
|
|
511
|
+
|
|
512
|
+
```bash
|
|
513
|
+
cnry traffic activate <project> --source <source-id>
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
```bash
|
|
517
|
+
CANONRY_QUEUE_SMOKE_PATH="/canonry-cloudflare-queue-smoke/$(date -u +%Y%m%dT%H%M%SZ)"
|
|
518
|
+
|
|
519
|
+
curl -sS -o /dev/null -w '%{http_code}\n' \
|
|
520
|
+
"https://www.example.com${CANONRY_QUEUE_SMOKE_PATH}?utm_source=chatgpt"
|
|
521
|
+
|
|
522
|
+
sleep 2
|
|
523
|
+
cnry traffic sync <project> --source <source-id>
|
|
524
|
+
cnry traffic events <project> --source <source-id> \
|
|
525
|
+
--kind all --since-minutes 120 --limit 5000 --format json
|
|
526
|
+
cnry traffic status <project> --format json
|
|
527
|
+
cnry doctor --project <project> --check 'traffic.source.*' --format json
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
The manual sync must complete. The events output must contain the unique smoke
|
|
531
|
+
path as an AI referral. The status output shows `queueBacklogCount` and
|
|
532
|
+
`queueBacklogObservedAt`. The doctor must not report a credential failure. If
|
|
533
|
+
the path is absent, wait two seconds and repeat the sync up to ten times.
|
|
534
|
+
|
|
416
535
|
### Direct-push behavior and smoke test
|
|
417
536
|
|
|
418
537
|
The Worker forwards a request candidate when at least one condition is true:
|
|
@@ -434,7 +553,7 @@ Direct push has bounded retries for network failures and HTTP 408, 425,
|
|
|
434
553
|
429, and 5xx responses (250 ms, then 1 s). Delivery is asynchronous and
|
|
435
554
|
never changes the site's response. If Canonry remains unavailable after
|
|
436
555
|
the retry budget, the selected event is lost. Direct push has no durable
|
|
437
|
-
edge buffer.
|
|
556
|
+
edge buffer. Use Queue pull when delivery needs a durable edge buffer.
|
|
438
557
|
|
|
439
558
|
The receiver accepts at most 256 KiB per request. Its default budgets are
|
|
440
559
|
6,000 requests per minute for each source and each caller IP. A bearer token
|
|
@@ -456,7 +575,7 @@ Raw samples retain the user-agent and normalized path. Do not put personal
|
|
|
456
575
|
data in URL paths. Canonry replaces numeric, UUID, and long hexadecimal path
|
|
457
576
|
segments with `:id`, but it retains other path segments.
|
|
458
577
|
|
|
459
|
-
###
|
|
578
|
+
### Direct-push acceptance test
|
|
460
579
|
|
|
461
580
|
1. Choose a stable, harmless origin path and create a unique smoke-run name.
|
|
462
581
|
2. Before route attachment, capture the origin response and source rollups.
|
|
@@ -570,11 +689,26 @@ For a dedicated Canonry-generated Worker:
|
|
|
570
689
|
1. Detach the exact site route from the Worker.
|
|
571
690
|
2. Request an ordinary site page. Make sure that the origin status,
|
|
572
691
|
headers, and body remain healthy.
|
|
573
|
-
3.
|
|
574
|
-
4.
|
|
575
|
-
5.
|
|
692
|
+
3. If this source uses Queue pull, run manual syncs until its backlog is zero.
|
|
693
|
+
4. If a replacement source exists, activate it after the Queue is empty.
|
|
694
|
+
5. If no replacement source exists, run
|
|
695
|
+
`cnry schedule remove <project> --kind traffic-sync`.
|
|
696
|
+
6. If this source uses Queue pull, run
|
|
697
|
+
`wrangler queues consumer http remove <queue-name>`.
|
|
698
|
+
7. Run `wrangler delete --config <artifact-directory>/wrangler.toml`.
|
|
699
|
+
8. If this source uses direct push, stop its unused tunnel.
|
|
700
|
+
9. Run the doctor. Expect source-health warnings until Canonry supports
|
|
576
701
|
Cloudflare disconnect.
|
|
577
702
|
|
|
703
|
+
CAUTION: If no other producer uses the Queue, delete it only after the drain:
|
|
704
|
+
|
|
705
|
+
```bash
|
|
706
|
+
wrangler queues delete <queue-name>
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
If the API token is dedicated to Canonry, revoke it after you remove the HTTP
|
|
710
|
+
consumer.
|
|
711
|
+
|
|
578
712
|
If Canonry code shares an existing Worker, remove only the Canonry code and
|
|
579
713
|
bindings. Redeploy the prior Worker version. Do not delete the shared Worker.
|
|
580
714
|
|
|
@@ -589,30 +723,43 @@ source archival and local credential cleanup.
|
|
|
589
723
|
| Wrangler preflight fails | Install the latest Wrangler. Run `wrangler whoami`. Then rerun connect. |
|
|
590
724
|
| Worker deploys to the wrong account | Make the correct Wrangler profile active. Pass the matching `--account-id`. |
|
|
591
725
|
| Route cannot attach | Make sure that the zone is active, DNS is proxied, and the exact route is unclaimed. Inspect every overlapping route. |
|
|
592
|
-
|
|
|
593
|
-
| Worker log shows ingest `401` | Rerun connect from the credential-owning host. Make sure that system clocks differ by less than five minutes. |
|
|
594
|
-
| No events arrive | Make sure that the route matches the exact host
|
|
726
|
+
| Direct-push receiver returns `403` | Remove the Access or WAF challenge from the exact ingest path. |
|
|
727
|
+
| Direct-push Worker log shows ingest `401` | Rerun connect from the credential-owning host. Make sure that system clocks differ by less than five minutes. |
|
|
728
|
+
| No direct-push events arrive | Make sure that the route matches the exact host. Make sure that `publicUrl` is reachable. Inspect AI Crawl Control, Access, WAF, and route precedence. Then send the UTM smoke request. |
|
|
729
|
+
| No Queue-pull events arrive | Run `wrangler queues info <queue-name>` and `wrangler queues consumer http list <queue-name>`. Make sure that the producer binding uses that Queue. Make sure that the account ID, Queue ID, and token scope match. Then run the Queue acceptance test. |
|
|
730
|
+
| Queue pull returns `403` | Create a token with **Account Queues Edit** for the Queue account. Reconnect with the token file. |
|
|
731
|
+
| Queue backlog remains above 1,000 | Run a manual sync. If the backlog grows again, shorten the 30-minute schedule interval. |
|
|
595
732
|
| Some paths are absent | Inspect more-specific routes and terminating security rules. Integrate Canonry into those routes or document the excluded coverage. |
|
|
596
|
-
|
|
|
733
|
+
| Direct-push ingest returns `429` | Wait for the rate window. The Worker retries twice, then drops the event. |
|
|
597
734
|
| Site returns Cloudflare error `1027` | Set the route to **Fail open**, or detach it. The account exhausted its daily Worker requests. |
|
|
598
735
|
| Doctor reports stale or empty direct-push data | Do not run `traffic sync`. Inspect the route, receiver, and Worker logs. Then repeat the smoke request. |
|
|
599
736
|
|
|
600
|
-
### Queue pull
|
|
601
|
-
|
|
602
|
-
Queue
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
737
|
+
### Queue pull delivery guarantees
|
|
738
|
+
|
|
739
|
+
Queue delivery is at least once. Canonry claims a durable source-scoped sync
|
|
740
|
+
lease, short-polls bounded batches, validates the transport-neutral event
|
|
741
|
+
schema and canonical host, and commits event receipts plus rollups in one DB
|
|
742
|
+
transaction. It acknowledges the Cloudflare lease only after that commit.
|
|
743
|
+
|
|
744
|
+
If acknowledgement fails after commit, Cloudflare can redeliver the batch.
|
|
745
|
+
The durable receipt makes the redelivery a no-op before Canonry acknowledges it
|
|
746
|
+
again. Unsupported V8 messages and malformed message bodies are acknowledged
|
|
747
|
+
as poison after their safe lease metadata is read; raw bodies and the API token
|
|
748
|
+
never enter errors or logs. A process crash leaves unacknowledged messages for
|
|
749
|
+
Cloudflare to redeliver after the visibility timeout.
|
|
750
|
+
|
|
751
|
+
The source lease rejects concurrent scheduler and operator drains. A stale
|
|
752
|
+
lease can be recovered after its expiry, and only its owner can release it.
|
|
753
|
+
Receipts remain for Cloudflare's current 14-day platform maximum plus a replay
|
|
754
|
+
margin. This stays safe even when the locally recorded retention is stale or
|
|
755
|
+
lower than the Queue's actual setting.
|
|
756
|
+
|
|
757
|
+
Each default Queue sync drains at most 10 batches of 100 messages: **1,000
|
|
758
|
+
messages per tick**. Doctor reports a residual of 1–1,000 messages as within
|
|
759
|
+
that budget. If no new messages arrive, the next scheduled sync can clear it.
|
|
760
|
+
Doctor warns when more than 1,000 messages remain because one default tick
|
|
761
|
+
cannot drain the residual. Run a manual sync to accelerate the drain. If the
|
|
762
|
+
backlog remains above 1,000, shorten the `traffic-sync` schedule interval.
|
|
616
763
|
|
|
617
764
|
## Syncing data
|
|
618
765
|
|
|
@@ -628,6 +775,12 @@ cnry traffic sync <project> --source <id>
|
|
|
628
775
|
cnry traffic sync <project> --source <id> --since-minutes 4320 # 3 days
|
|
629
776
|
```
|
|
630
777
|
|
|
778
|
+
Cloudflare Queue pull is cursorless and ignores `--since-minutes`; each call
|
|
779
|
+
drains a bounded set of leased Queue messages. Repeat the command when you need
|
|
780
|
+
to accelerate a backlog, or let the auto-created 30-minute schedule continue
|
|
781
|
+
draining. Direct push still rejects `traffic sync` because it has no pull
|
|
782
|
+
transport.
|
|
783
|
+
|
|
631
784
|
### Unsticking a stuck source
|
|
632
785
|
|
|
633
786
|
If a Vercel (or Cloud Run) source has been failing for so long that
|
|
@@ -654,9 +807,9 @@ above; clearing `lastError` for a transient WordPress failure also
|
|
|
654
807
|
works. Archived sources are rejected — re-connect them with
|
|
655
808
|
`cnry traffic connect ...` instead.
|
|
656
809
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
810
|
+
Time-window and cursor adapters use the `last_event_ids` overlap ring. Queue
|
|
811
|
+
pull uses durable event receipts sized to Queue retention. Both paths make a
|
|
812
|
+
repeated pull safe to schedule or trigger from CI.
|
|
660
813
|
|
|
661
814
|
## Inspecting source state
|
|
662
815
|
|
|
@@ -706,7 +859,7 @@ schema change, the stored rollups are untouched.
|
|
|
706
859
|
| Project dashboard `/projects/:name/activity` | Live source table + 24h totals + GA4 referrals (combined view) |
|
|
707
860
|
| Top-level `/traffic` route | Cross-project source admin (connect, sync, archive) |
|
|
708
861
|
| `cnry report <project>` (HTML + SPA) | "AI Visibility — Server-Side" section, ranked above Indexing Health |
|
|
709
|
-
| `cnry doctor --project <name>` | Source health checks, including
|
|
862
|
+
| `cnry doctor --project <name>` | Source health checks, including direct-push Worker drift and Queue-pull local credential/sync state |
|
|
710
863
|
| MCP toolkit `traffic` | Read/status tools plus pull-source setup/sync tools. Cloudflare connect is local-CLI-only so Worker secrets cannot enter an MCP transcript. |
|
|
711
864
|
|
|
712
865
|
## Doctor signals
|
|
@@ -719,7 +872,9 @@ The doctor checks are adapter-agnostic. When they fail or warn:
|
|
|
719
872
|
| `traffic.source.connected` | `traffic.source.all-errored` | Re-connect the source. The check's `details.lastError` shows the underlying reason. |
|
|
720
873
|
| `traffic.source.recent-data` | `traffic.recent-data.stale` | For pull sources, run `cnry traffic sync …`. For Cloudflare direct push, inspect the route and receiver. |
|
|
721
874
|
| `traffic.source.recent-data` | `traffic.recent-data.empty` | Inspect source configuration. For Cloudflare direct push, send the UTM smoke request and inspect Worker logs. |
|
|
722
|
-
| `traffic.source.
|
|
875
|
+
| `traffic.source.queue-backlog` | `traffic.queue-backlog.within-drain-budget` | 1–1,000 Queue messages remain. If no new messages arrive, the next scheduled sync can drain them. Run a manual sync to accelerate. |
|
|
876
|
+
| `traffic.source.queue-backlog` | `traffic.queue-backlog.remaining` | More than 1,000 Queue messages remain. Run a manual sync. If the backlog persists, shorten the traffic-sync schedule. |
|
|
877
|
+
| `traffic.source.credentials` | `traffic.credentials.resolve-failed` | Reconnect from the host that owns the source credentials. Queue pull requires a non-empty Account Queues Edit token paired by source ID. |
|
|
723
878
|
| `traffic.source.cache-blindspot` | `traffic.cache-blindspot.wordpress-plugin` | A WordPress source is connected, so the plugin cannot see cache-served page views. Exclude AI user-agents from the page cache and any CDN, or switch to a log/edge source. Warns only, not a failure. |
|
|
724
879
|
| `traffic.source.worker-version` | `traffic.worker-version.waiting-for-first-event` | Send a smoke-test request through the Worker. Then run the doctor again. |
|
|
725
880
|
| `traffic.source.worker-version` | `traffic.worker-version.stale` | Redeploy the generated Worker from the credential-owning host. Use `--deploy --confirm-route --confirm-fail-open`. Attach the route manually. |
|
|
@@ -728,14 +883,28 @@ Cloudflare doctor behavior is capability-driven: only
|
|
|
728
883
|
`deliveryMode=direct-push` (and legacy Cloudflare rows with no mode) skips
|
|
729
884
|
pull-watermark lag. The same mode enables `traffic.source.worker-version`.
|
|
730
885
|
That check compares `configJson.workerVersion` with `lastWorkerVersion`.
|
|
731
|
-
|
|
886
|
+
Queue pull uses pull lag checks and skips the direct-push Worker-version check.
|
|
732
887
|
|
|
733
888
|
## Scheduling
|
|
734
889
|
|
|
735
|
-
`cnry schedule` supports `--kind traffic-sync` for pull sources.
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
890
|
+
`cnry schedule` supports `--kind traffic-sync` for pull sources. Activation of
|
|
891
|
+
Queue, Cloud Run, WordPress, or Vercel creates or repoints this schedule at a
|
|
892
|
+
30-minute cadence.
|
|
893
|
+
Cloudflare direct push is event-driven and must not receive a traffic-sync
|
|
894
|
+
schedule. Recurring syncs are safe because of the adapter's overlap ring or
|
|
895
|
+
durable Queue receipts.
|
|
896
|
+
|
|
897
|
+
Keep the auto-created 30-minute schedule for Queue pull. The Worker sends one
|
|
898
|
+
selected event in each Queue message. At its 1,000-message limit, this cadence
|
|
899
|
+
drains about 0.56 selected events per second. If the Queue backlog remains
|
|
900
|
+
above 1,000, run a manual sync. Then shorten the interval:
|
|
901
|
+
|
|
902
|
+
```bash
|
|
903
|
+
cnry schedule set <project> --kind traffic-sync \
|
|
904
|
+
--source <source-id> --cron "*/10 * * * *"
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
Use these cadences only for Cloud Run, WordPress, and Vercel:
|
|
739
908
|
|
|
740
909
|
| Cadence | Use case |
|
|
741
910
|
|---|---|
|
|
@@ -798,8 +967,8 @@ domains, or PII are surfaced.
|
|
|
798
967
|
no client IP, so every Vercel crawler hit is unverified by
|
|
799
968
|
construction (UA-only). A Vercel source reading 100% unverified is
|
|
800
969
|
expected, not a misconfiguration.
|
|
801
|
-
- **Four adapters ship: Cloud Run, WordPress, Vercel, and Cloudflare
|
|
802
|
-
direct
|
|
970
|
+
- **Four adapters ship: Cloud Run, WordPress, Vercel, and Cloudflare with
|
|
971
|
+
direct-push and queue-pull delivery. More adapters are planned.** The doctor checks and report renderer are
|
|
803
972
|
adapter-agnostic — adding a new adapter is just a new entry in
|
|
804
973
|
`traffic_sources.source_type` and a `TrafficSourceValidator`
|
|
805
974
|
registration.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as a,u as h,j as e}from"./vendor-tanstack-Dock0eH1.js";import{cc as L,cd as P,bx as E,k as u,B as x,d as v,T as k}from"./index-
|
|
1
|
+
import{r as a,u as h,j as e}from"./vendor-tanstack-Dock0eH1.js";import{cc as L,cd as P,bx as E,k as u,B as x,d as v,T as k}from"./index-CQGAqmDx.js";const o=100;function M(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 H({projectName:s}){const[n,l]=a.useState(0),[c,y]=a.useState("90d"),[m,N]=a.useState(""),[p,w]=a.useState(""),S=a.useMemo(()=>M(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(k,{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{H as A};
|