@stravica-ai/digital-operator-pa 0.1.18 → 0.1.19

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.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -6,8 +6,9 @@ domain whose DNS lives on Cloudflare in an account the operator controls.
6
6
  ## Status
7
7
 
8
8
  Implemented: `add-property`, `verify-via-cf-dns`, `submit-sitemap`,
9
- `pull-analytics`, `list-properties`. Auth via Google service-account JSON
10
- loaded from the SOPS vault. Cloudflare planting via the CF v4 REST API.
9
+ `sitemap-status`, `pull-analytics`, `list-properties`. Auth via Google
10
+ service-account JSON loaded from the SOPS vault. Cloudflare planting via
11
+ the CF v4 REST API.
11
12
 
12
13
  ## Dependencies
13
14
 
@@ -118,6 +119,11 @@ pnpm exec pa gsc verify-via-cf-dns besafetraining.co.uk --account clive
118
119
  # Submit a sitemap
119
120
  pnpm exec pa gsc submit-sitemap stravica.ai https://stravica.ai/sitemap.xml
120
121
 
122
+ # Read sitemap state (list every sitemap the property has)
123
+ pnpm exec pa gsc sitemap-status stravica.ai
124
+ # Read state for one named sitemap
125
+ pnpm exec pa gsc sitemap-status stravica.ai https://stravica.ai/sitemap.xml
126
+
121
127
  # Pull search analytics (default 28d, top queries, 100 rows)
122
128
  pnpm exec pa gsc pull-analytics stravica.ai
123
129
  pnpm exec pa gsc pull-analytics stravica.ai --days 90 --dimensions query,page --rows 500
@@ -9,6 +9,7 @@
9
9
  import { runAddProperty } from './commands/add-property.mjs';
10
10
  import { runVerifyViaCfDns } from './commands/verify-via-cf-dns.mjs';
11
11
  import { runSubmitSitemap } from './commands/submit-sitemap.mjs';
12
+ import { runSitemapStatus } from './commands/sitemap-status.mjs';
12
13
  import { runPullAnalytics } from './commands/pull-analytics.mjs';
13
14
  import { runListProperties } from './commands/list-properties.mjs';
14
15
 
@@ -29,6 +30,12 @@ Usage:
29
30
  pa gsc submit-sitemap <domain> <sitemap-url>
30
31
  Submit a sitemap to GSC for the property.
31
32
 
33
+ pa gsc sitemap-status <domain> [sitemap-url]
34
+ Read sitemap state for the property (sitemaps.list) or one named
35
+ sitemap (sitemaps.get). Reports path, lastSubmitted, lastDownloaded,
36
+ isPending, warnings, errors, and per-type contents[] submitted/indexed
37
+ counts. Read-only.
38
+
32
39
  pa gsc pull-analytics <domain> [--days N] [--dimensions D1,D2,...] [--rows N]
33
40
  Pull search-analytics rows. Defaults: --days 28 --dimensions query
34
41
  --rows 100. Outputs a JSON array on stdout.
@@ -69,6 +76,7 @@ export async function run(argv, overrides = {}) {
69
76
  case 'add-property': return runAddProperty(rest, overrides);
70
77
  case 'verify-via-cf-dns': return runVerifyViaCfDns(rest, overrides);
71
78
  case 'submit-sitemap': return runSubmitSitemap(rest, overrides);
79
+ case 'sitemap-status': return runSitemapStatus(rest, overrides);
72
80
  case 'pull-analytics': return runPullAnalytics(rest, overrides);
73
81
  case 'list-properties': return runListProperties(rest, overrides);
74
82
  default: {
@@ -0,0 +1,95 @@
1
+ // sitemap-status — Read sitemap state for a GSC property.
2
+ //
3
+ // Wraps webmasters.sitemaps.list (no sitemap-url) and
4
+ // webmasters.sitemaps.get (with sitemap-url). Both are read-only.
5
+ //
6
+ // Every previous "did the release-time submit take?" check was a one-off
7
+ // workaround — this verb makes it a first-class subcommand so the pattern
8
+ // is repeatable and auditable.
9
+ //
10
+ // Usage:
11
+ // pa gsc sitemap-status <domain>
12
+ // List every sitemap the property has, with lastSubmitted,
13
+ // lastDownloaded, isPending, warnings, errors, and per-type
14
+ // contents[] submitted/indexed counts.
15
+ //
16
+ // pa gsc sitemap-status <domain> <sitemap-url>
17
+ // Same shape, but only for the named sitemap (webmasters.sitemaps.get).
18
+
19
+ import { buildGscClient, toDomainProperty } from '../auth.mjs';
20
+
21
+ /**
22
+ * Normalise a Sitemap resource from the GSC API into a stable shape.
23
+ * The GSC API returns warnings/errors/submitted/indexed as strings; we
24
+ * keep them as strings (matches the wire format so operators reading the
25
+ * JSON see exactly what the API said).
26
+ */
27
+ function normaliseSitemap(s = {}) {
28
+ const contents = Array.isArray(s.contents)
29
+ ? s.contents.map((c) => ({
30
+ type: c.type ?? null,
31
+ submitted: c.submitted ?? null,
32
+ indexed: c.indexed ?? null,
33
+ }))
34
+ : [];
35
+ return {
36
+ path: s.path ?? null,
37
+ lastSubmitted: s.lastSubmitted ?? null,
38
+ lastDownloaded: s.lastDownloaded ?? null,
39
+ isPending: s.isPending ?? null,
40
+ isSitemapsIndex: s.isSitemapsIndex ?? null,
41
+ type: s.type ?? null,
42
+ warnings: s.warnings ?? null,
43
+ errors: s.errors ?? null,
44
+ contents,
45
+ };
46
+ }
47
+
48
+ export async function runSitemapStatus(args, overrides = {}) {
49
+ const domain = args[0];
50
+ const sitemapUrl = args[1];
51
+
52
+ if (!domain) {
53
+ process.stderr.write('Usage: pa gsc sitemap-status <domain> [sitemap-url]\n');
54
+ return 1;
55
+ }
56
+ if (sitemapUrl && !/^https?:\/\//i.test(sitemapUrl)) {
57
+ process.stderr.write(`[gsc sitemap-status] sitemap-url must be an absolute http(s) URL, got "${sitemapUrl}"\n`);
58
+ return 1;
59
+ }
60
+
61
+ const siteUrl = toDomainProperty(domain);
62
+
63
+ let client;
64
+ try {
65
+ client = await buildGscClient(overrides);
66
+ } catch (e) {
67
+ process.stderr.write(`[gsc sitemap-status] auth failed: ${e.message}\n`);
68
+ return e.exitCode || 2;
69
+ }
70
+
71
+ let sitemaps;
72
+ try {
73
+ if (sitemapUrl) {
74
+ const res = await client.webmasters.sitemaps.get({ siteUrl, feedpath: sitemapUrl });
75
+ // sitemaps.get returns the Sitemap resource directly (no wrapper).
76
+ sitemaps = [normaliseSitemap(res?.data || {})];
77
+ } else {
78
+ const res = await client.webmasters.sitemaps.list({ siteUrl });
79
+ sitemaps = (res?.data?.sitemap || []).map(normaliseSitemap);
80
+ }
81
+ } catch (e) {
82
+ const verb = sitemapUrl ? 'sitemaps.get' : 'sitemaps.list';
83
+ process.stderr.write(`[gsc sitemap-status] ${verb} failed: ${e.message}\n`);
84
+ return 2;
85
+ }
86
+
87
+ const out = overrides.stdout || process.stdout;
88
+ out.write(JSON.stringify({
89
+ siteUrl,
90
+ sitemapUrl: sitemapUrl || null,
91
+ count: sitemaps.length,
92
+ sitemaps,
93
+ }, null, 2) + '\n');
94
+ return 0;
95
+ }
@@ -23,7 +23,7 @@ Usage:
23
23
  pa workspace <verb> Workspace server lifecycle (start | stop | status | open)
24
24
  pa project <verb> Project state writes (create | status | doc add/rm | list)
25
25
  pa work <verb> Work-item state writes (create | status | list)
26
- pa gsc <verb> Google Search Console automation (add-property | verify-via-cf-dns | submit-sitemap | pull-analytics | list-properties)
26
+ pa gsc <verb> Google Search Console automation (add-property | verify-via-cf-dns | submit-sitemap | sitemap-status | pull-analytics | list-properties)
27
27
  pa knowledge <verb> Query the operator's knowledge graph (related | search | domain | orphans | path)
28
28
  pa research <verb> Deep-research session store (run | list | show | gates | prune | mark | paths)
29
29
  pa autonomy <verb> Per-agent per-task-type approval state (init | get | set | list)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stravica-ai/digital-operator-pa",
3
- "version": "0.1.18",
4
- "description": "Digital Operator PA \u2014 single-bundled public package surface (bins + library) for the platform.",
3
+ "version": "0.1.19",
4
+ "description": "Digital Operator PA — single-bundled public package surface (bins + library) for the platform.",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",
7
7
  "exports": {
@@ -49,11 +49,10 @@
49
49
  "engines": {
50
50
  "node": ">=24"
51
51
  },
52
+ "//": "test pins --test-isolation=none: default process-isolation mode silently undercounts (drops passed tests from totals) when a test awaits async work while stdout is patched by a capture() helper — see packages/cli/test/secrets. Counts must never lie.",
52
53
  "scripts": {
53
54
  "build": "node build.mjs",
54
- "prepack": "node build.mjs",
55
55
  "test": "node --test --test-isolation=none test/*.test.mjs",
56
56
  "dry-publish": "node build.mjs && node scripts/dry-publish.mjs"
57
- },
58
- "//": "test pins --test-isolation=none: default process-isolation mode silently undercounts (drops passed tests from totals) when a test awaits async work while stdout is patched by a capture() helper \u2014 see packages/cli/test/secrets. Counts must never lie."
59
- }
57
+ }
58
+ }