n-seo 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,27 @@ uses [Semantic Versioning](https://semver.org/).
8
8
 
9
9
  Nothing yet.
10
10
 
11
+ ## [0.7.1] - 2026-09-28
12
+
13
+ ### Fixed
14
+ - **One HTTP/2 hiccup no longer fails the whole index-status step.** The
15
+ retry helper only retried the curl exits it knew about, and treated every
16
+ other one as a bad request. A dropped HTTP/2 connection (curl exit 16)
17
+ mid-run failed the first URL Inspection call it hit, which took the step
18
+ down, and the daily run's one step-level retry hit the same thing. Exits
19
+ 16 and 92 (HTTP/2 connection and stream errors) and 55 (send failure) now
20
+ get the same per-request backoff as timeouts and resets.
21
+ - **A curl failure now says why.** Requests ran with `-s`, which silences
22
+ curl's own error line along with the progress meter, so the failure read
23
+ `curl exit 16: ` with nothing after it. They run with `-sS` now.
24
+ - **A slow first answer no longer reads as a regression.** The site probe
25
+ fetched each file once with a 15-second limit. A cold origin behind a CDN
26
+ can miss that on its first request of the morning, and the run then
27
+ alerted `robots.txt REGRESSED` for a site serving it fine. It happened
28
+ every few days to one site. The probe now tries once more, five seconds
29
+ later, when there is no answer or a 5xx. A site that is really down fails
30
+ both tries and is still reported.
31
+
11
32
  ## [0.7.0] - 2026-09-18
12
33
 
13
34
  ### Added
@@ -537,7 +558,9 @@ First public release.
537
558
  `n-seo upgrade` gate — without changing the reported test counts, and an
538
559
  in-place install ran those assertions against the owner's live data.
539
560
 
540
- [Unreleased]: https://github.com/en-dash-consulting/n-seo/compare/v0.6.1...HEAD
561
+ [Unreleased]: https://github.com/en-dash-consulting/n-seo/compare/v0.7.1...HEAD
562
+ [0.7.1]: https://github.com/en-dash-consulting/n-seo/compare/v0.7.0...v0.7.1
563
+ [0.7.0]: https://github.com/en-dash-consulting/n-seo/compare/v0.6.1...v0.7.0
541
564
  [0.6.1]: https://github.com/en-dash-consulting/n-seo/compare/v0.6.0...v0.6.1
542
565
  [0.6.0]: https://github.com/en-dash-consulting/n-seo/compare/v0.5.0...v0.6.0
543
566
  [0.5.0]: https://github.com/en-dash-consulting/n-seo/compare/v0.4.1...v0.5.0
@@ -14,9 +14,10 @@ import subprocess
14
14
  import sys
15
15
  import time
16
16
 
17
- # curl exits worth another try: 6/7 resolve+connect, 28 timeout,
18
- # 35 TLS handshake, 52 empty reply, 56 recv error.
19
- RETRYABLE_EXITS = {6, 7, 28, 35, 52, 56}
17
+ # curl exits worth another try: 6/7 resolve+connect, 16 HTTP/2 framing,
18
+ # 28 timeout, 35 TLS handshake, 52 empty reply, 55 send error,
19
+ # 56 recv error, 92 HTTP/2 stream reset.
20
+ RETRYABLE_EXITS = {6, 7, 16, 28, 35, 52, 55, 56, 92}
20
21
  TIMEOUT = 180
21
22
  ATTEMPTS = 4
22
23
 
@@ -29,7 +30,8 @@ def curl_json(args, *, timeout=TIMEOUT, attempts=ATTEMPTS, label=""):
29
30
  """
30
31
  last = None
31
32
  for attempt in range(1, attempts + 1):
32
- p = subprocess.run(["curl", "-s", "--max-time", str(timeout), *args],
33
+ # -sS: no progress meter, but curl still says why it failed.
34
+ p = subprocess.run(["curl", "-sS", "--max-time", str(timeout), *args],
33
35
  capture_output=True, text=True)
34
36
  if p.returncode == 0:
35
37
  try:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n-seo",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Agentic, local-first SEO / AEO / GEO control plane: pulls Search Console + GA4, probes your sites, and ranks the next moves. Your LLM writes the proposals; your coding agent works the queue over MCP.",
5
5
  "license": "MIT",
6
6
  "author": "En Dash Consulting (https://endash.us)",
@@ -13,6 +13,7 @@ Stdlib only — no pip installs needed.
13
13
  import json
14
14
  import re
15
15
  import sys
16
+ import time
16
17
  from datetime import datetime, timezone
17
18
  from html.parser import HTMLParser
18
19
  from pathlib import Path
@@ -33,7 +34,15 @@ UA = "Mozilla/5.0 (compatible; n-seo-probe/0.1)"
33
34
 
34
35
 
35
36
  def fetch(url, timeout=15):
36
- return fetch_text(url, timeout=timeout, ua=UA)
37
+ """One retry when the site doesn't answer or answers 5xx. A cold origin
38
+ behind a CDN can take longer than the timeout on its first request of the
39
+ morning, and without the retry that read as robots.txt or sitemap.xml
40
+ REGRESSED. A site that is really down fails both tries."""
41
+ status, body = fetch_text(url, timeout=timeout, ua=UA)
42
+ if status is None or status >= 500:
43
+ time.sleep(5)
44
+ status, body = fetch_text(url, timeout=timeout, ua=UA)
45
+ return status, body
37
46
 
38
47
 
39
48
  class MetaParser(HTMLParser):