@gscdump/cli 2.1.0 → 2.1.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.
@@ -84,6 +84,388 @@ const listCommand = defineCommand({
84
84
  }
85
85
  }
86
86
  });
87
+ const getCommand = defineCommand({
88
+ meta: {
89
+ name: "get",
90
+ description: "Get details for a specific sitemap"
91
+ },
92
+ args: {
93
+ ...OUTPUT_ARGS,
94
+ site: {
95
+ type: "string",
96
+ alias: "s",
97
+ description: "Site URL (defaults to config.defaultSite or prompt)"
98
+ },
99
+ url: {
100
+ type: "positional",
101
+ required: true,
102
+ description: "Sitemap URL"
103
+ }
104
+ },
105
+ async run({ args }) {
106
+ const { json } = applyOutputMode(args);
107
+ const ctx = await createCommandContext({ needsAuth: true });
108
+ const siteUrl = await ctx.resolveSite(args.site ? String(args.site) : void 0);
109
+ const client = ctx.client;
110
+ const sitemap = await fetchSitemap(client, siteUrl, args.url).catch(gscErrorHandler);
111
+ if (json) {
112
+ console.log(JSON.stringify(sitemap, null, 2));
113
+ return;
114
+ }
115
+ console.log();
116
+ console.log(` \x1B[1mPath:\x1B[0m ${sitemap.path}`);
117
+ console.log(` \x1B[1mType:\x1B[0m ${sitemap.type || "sitemap"}`);
118
+ console.log(` \x1B[1mLast Submitted:\x1B[0m ${sitemap.lastSubmitted || "N/A"}`);
119
+ console.log(` \x1B[1mLast Downloaded:\x1B[0m ${sitemap.lastDownloaded || "N/A"}`);
120
+ console.log(` \x1B[1mPending:\x1B[0m ${sitemap.isPending ? "Yes" : "No"}`);
121
+ console.log(` \x1B[1mErrors:\x1B[0m ${sitemap.errors || 0}`);
122
+ console.log(` \x1B[1mWarnings:\x1B[0m ${sitemap.warnings || 0}`);
123
+ if (sitemap.contents?.length) {
124
+ console.log();
125
+ console.log(" \x1B[1mContents:\x1B[0m");
126
+ for (const c of sitemap.contents) console.log(` ${c.type}: ${c.submitted} submitted`);
127
+ }
128
+ }
129
+ });
130
+ const submitCommand = defineCommand({
131
+ meta: {
132
+ name: "submit",
133
+ description: "Submit a sitemap to GSC"
134
+ },
135
+ args: {
136
+ ...OUTPUT_ARGS,
137
+ site: {
138
+ type: "string",
139
+ alias: "s",
140
+ description: "Site URL (defaults to config.defaultSite or prompt)"
141
+ },
142
+ url: {
143
+ type: "positional",
144
+ required: true,
145
+ description: "Sitemap URL to submit"
146
+ }
147
+ },
148
+ async run({ args }) {
149
+ const { json } = applyOutputMode(args);
150
+ const ctx = await createCommandContext({ needsAuth: true });
151
+ const siteUrl = await ctx.resolveSite(args.site ? String(args.site) : void 0);
152
+ await ctx.client.sitemaps.submit(siteUrl, args.url).catch(gscErrorHandler);
153
+ if (json) {
154
+ console.log(JSON.stringify({
155
+ siteUrl,
156
+ feedpath: args.url,
157
+ status: "submitted"
158
+ }, null, 2));
159
+ return;
160
+ }
161
+ logger.success(`Submitted sitemap: ${args.url}`);
162
+ }
163
+ });
164
+ const deleteCommand = defineCommand({
165
+ meta: {
166
+ name: "delete",
167
+ description: "Delete a sitemap from GSC"
168
+ },
169
+ args: {
170
+ ...OUTPUT_ARGS,
171
+ site: {
172
+ type: "string",
173
+ alias: "s",
174
+ description: "Site URL (defaults to config.defaultSite or prompt)"
175
+ },
176
+ url: {
177
+ type: "positional",
178
+ required: true,
179
+ description: "Sitemap URL to delete"
180
+ }
181
+ },
182
+ async run({ args }) {
183
+ const { json } = applyOutputMode(args);
184
+ const ctx = await createCommandContext({ needsAuth: true });
185
+ const siteUrl = await ctx.resolveSite(args.site ? String(args.site) : void 0);
186
+ await ctx.client.sitemaps.delete(siteUrl, args.url).catch(gscErrorHandler);
187
+ if (json) {
188
+ console.log(JSON.stringify({
189
+ siteUrl,
190
+ feedpath: args.url,
191
+ status: "deleted"
192
+ }, null, 2));
193
+ return;
194
+ }
195
+ logger.success(`Deleted sitemap: ${args.url}`);
196
+ }
197
+ });
198
+ const discoverCommand = defineCommand({
199
+ meta: {
200
+ name: "discover",
201
+ description: "Probe a domain's robots.txt + common paths for an advertised sitemap (no auth needed)"
202
+ },
203
+ args: {
204
+ ...OUTPUT_ARGS,
205
+ domain: {
206
+ type: "positional",
207
+ required: true,
208
+ description: "Domain (e.g., example.com)"
209
+ }
210
+ },
211
+ async run({ args }) {
212
+ const { json } = applyOutputMode(args);
213
+ const domain = String(args.domain).replace(/^https?:\/\//, "").replace(/\/.*$/, "");
214
+ const discovery = await discoverLiveSitemap(domain);
215
+ const url = discovery._tag === "found" ? discovery.url : null;
216
+ if (json) {
217
+ console.log(JSON.stringify({
218
+ domain,
219
+ sitemap: url,
220
+ status: discovery._tag
221
+ }, null, 2));
222
+ return;
223
+ }
224
+ if (discovery._tag === "incomplete") {
225
+ logger.error(`Sitemap discovery was incomplete: ${discovery.failures[0] ?? "unknown failure"}`);
226
+ process.exit(1);
227
+ }
228
+ if (!url) {
229
+ logger.warn(`No sitemap discovered for ${domain}`);
230
+ process.exit(1);
231
+ }
232
+ logger.success(`Discovered sitemap: ${url}`);
233
+ }
234
+ });
235
+ const urlsCommand = defineCommand({
236
+ meta: {
237
+ name: "urls",
238
+ description: "Fetch a sitemap (or sitemap index) and dump its <loc> URLs (no auth needed)"
239
+ },
240
+ args: {
241
+ ...OUTPUT_ARGS,
242
+ "url": {
243
+ type: "positional",
244
+ required: true,
245
+ description: "Sitemap URL (index files are followed)"
246
+ },
247
+ "limit": {
248
+ type: "string",
249
+ alias: "l",
250
+ description: "Stop after N URLs across all nested sitemaps"
251
+ },
252
+ "max-depth": {
253
+ type: "string",
254
+ description: "Max sitemap-index nesting depth (default: 3)"
255
+ }
256
+ },
257
+ async run({ args }) {
258
+ const { json } = applyOutputMode(args);
259
+ const limit = args.limit ? Number.parseInt(String(args.limit), 10) : void 0;
260
+ const maxDepth = args["max-depth"] ? Number.parseInt(String(args["max-depth"]), 10) : void 0;
261
+ const result = await loadSitemapUrls(String(args.url), {
262
+ maxUrls: limit,
263
+ maxDepth
264
+ });
265
+ if (result._tag === "error") {
266
+ logger.error(`Sitemap fetch failed: ${result.message}`);
267
+ process.exit(1);
268
+ }
269
+ const { urls, complete, documentsRead } = result.value;
270
+ if (json) {
271
+ console.log(JSON.stringify({
272
+ sitemap: args.url,
273
+ count: urls.length,
274
+ complete,
275
+ documentsRead,
276
+ urls
277
+ }, null, 2));
278
+ return;
279
+ }
280
+ if (!complete) logger.warn("Sitemap walk was truncated or a child document failed");
281
+ for (const u of urls) console.log(u);
282
+ }
283
+ });
284
+ const currentCommand = defineCommand({
285
+ meta: {
286
+ name: "current",
287
+ description: "Read the hosted canonical sitemap generation and feed list"
288
+ },
289
+ args: {
290
+ ...OUTPUT_ARGS,
291
+ ...HOSTED_ARGS,
292
+ "site-id": {
293
+ type: "positional",
294
+ required: true,
295
+ description: "Hosted gscdump site id"
296
+ }
297
+ },
298
+ async run({ args }) {
299
+ const { json } = applyOutputMode(args);
300
+ const result = await hostedClient(args).getSiteSitemaps({ params: { siteId: String(args["site-id"]) } });
301
+ if (json) {
302
+ console.log(JSON.stringify(result.data, null, 2));
303
+ return;
304
+ }
305
+ const generation = result.data.generation;
306
+ console.log(generation ? `Generation ${generation.id}, observed ${new Date(generation.observedAt).toISOString()}` : "No canonical generation");
307
+ for (const sitemap of result.data.sitemaps) console.log(`${sitemap.path}\t${sitemap.urlCount}`);
308
+ }
309
+ });
310
+ const historyCommand = defineCommand({
311
+ meta: {
312
+ name: "history",
313
+ description: "Read hosted canonical sitemap membership and lastmod changes"
314
+ },
315
+ args: {
316
+ ...OUTPUT_ARGS,
317
+ ...HOSTED_ARGS,
318
+ "site-id": {
319
+ type: "positional",
320
+ required: true,
321
+ description: "Hosted gscdump site id"
322
+ },
323
+ "days": {
324
+ type: "string",
325
+ description: "History window in days"
326
+ }
327
+ },
328
+ async run({ args }) {
329
+ const { json } = applyOutputMode(args);
330
+ const days = args.days ? Number.parseInt(String(args.days), 10) : void 0;
331
+ const result = await hostedClient(args).getSiteSitemapChanges({
332
+ params: { siteId: String(args["site-id"]) },
333
+ query: { ...days ? { days } : {} }
334
+ });
335
+ console.log(JSON.stringify(result.data, null, json ? 2 : 0));
336
+ }
337
+ });
338
+ const membershipCommand = defineCommand({
339
+ meta: {
340
+ name: "membership",
341
+ description: "Query exact hosted membership evidence for comma-separated URLs"
342
+ },
343
+ args: {
344
+ ...OUTPUT_ARGS,
345
+ ...HOSTED_ARGS,
346
+ "site-id": {
347
+ type: "positional",
348
+ required: true,
349
+ description: "Hosted gscdump site id"
350
+ },
351
+ "urls": {
352
+ type: "string",
353
+ required: true,
354
+ description: "Comma-separated exact URLs"
355
+ },
356
+ "generation": {
357
+ type: "string",
358
+ description: "Pin the query to a generation id"
359
+ }
360
+ },
361
+ async run({ args }) {
362
+ const { json } = applyOutputMode(args);
363
+ const urls = String(args.urls).split(",").map((url) => url.trim()).filter(Boolean);
364
+ const result = await hostedClient(args).querySitemapMembership({
365
+ params: { siteId: String(args["site-id"]) },
366
+ body: {
367
+ urls,
368
+ ...args.generation ? { generationId: String(args.generation) } : {}
369
+ }
370
+ });
371
+ if (json) {
372
+ console.log(JSON.stringify(result.data, null, 2));
373
+ return;
374
+ }
375
+ for (const evidence of result.data.evidence) {
376
+ const detail = evidence._tag === "present" ? `${evidence.feedpath}\t${evidence.lastmod ?? ""}` : evidence._tag === "absent" ? String(evidence.observedAt) : evidence.reason;
377
+ console.log(`${evidence._tag}\t${evidence.url}\t${detail}`);
378
+ }
379
+ }
380
+ });
381
+ const lastmodCommand = defineCommand({
382
+ meta: {
383
+ name: "lastmod",
384
+ description: "List a generation-pinned hosted page of URL lastmod evidence"
385
+ },
386
+ args: {
387
+ ...OUTPUT_ARGS,
388
+ ...HOSTED_ARGS,
389
+ "site-id": {
390
+ type: "positional",
391
+ required: true,
392
+ description: "Hosted gscdump site id"
393
+ },
394
+ "generation": {
395
+ type: "string",
396
+ description: "Pin to a generation id"
397
+ },
398
+ "feedpath": {
399
+ type: "string",
400
+ description: "Restrict to one exact sitemap feed URL"
401
+ },
402
+ "cursor": {
403
+ type: "string",
404
+ description: "Opaque cursor from the prior page"
405
+ },
406
+ "limit": {
407
+ type: "string",
408
+ description: "Page size, maximum 10000"
409
+ }
410
+ },
411
+ async run({ args }) {
412
+ const { json } = applyOutputMode(args);
413
+ const result = await hostedClient(args).listSitemapUrls({
414
+ params: { siteId: String(args["site-id"]) },
415
+ query: {
416
+ ...args.generation ? { generationId: String(args.generation) } : {},
417
+ ...args.feedpath ? { feedpath: String(args.feedpath) } : {},
418
+ ...args.cursor ? { cursor: String(args.cursor) } : {},
419
+ ...args.limit ? { limit: Number.parseInt(String(args.limit), 10) } : {}
420
+ }
421
+ });
422
+ if (json) {
423
+ console.log(JSON.stringify(result.data, null, 2));
424
+ return;
425
+ }
426
+ for (const item of result.data.items) console.log(`${item.url}\t${item.lastmod ?? ""}\t${item.feedpath}`);
427
+ if (result.data.page.nextCursor) logger.info(`Next cursor: ${result.data.page.nextCursor}`);
428
+ }
429
+ });
430
+ const exportCommand = defineCommand({
431
+ meta: {
432
+ name: "export",
433
+ description: "Get a generation-pinned hosted bulk export descriptor"
434
+ },
435
+ args: {
436
+ ...OUTPUT_ARGS,
437
+ ...HOSTED_ARGS,
438
+ "site-id": {
439
+ type: "positional",
440
+ required: true,
441
+ description: "Hosted gscdump site id"
442
+ },
443
+ "generation": {
444
+ type: "string",
445
+ description: "Pin to a generation id"
446
+ },
447
+ "feedpath": {
448
+ type: "string",
449
+ description: "Restrict to one exact sitemap feed URL"
450
+ }
451
+ },
452
+ async run({ args }) {
453
+ const { json } = applyOutputMode(args);
454
+ const result = await hostedClient(args).getSitemapExport({
455
+ params: { siteId: String(args["site-id"]) },
456
+ query: {
457
+ ...args.generation ? { generationId: String(args.generation) } : {},
458
+ ...args.feedpath ? { feedpath: String(args.feedpath) } : {}
459
+ }
460
+ });
461
+ if (json) {
462
+ console.log(JSON.stringify(result.data, null, 2));
463
+ return;
464
+ }
465
+ if (result.data.export._tag === "unavailable") throw new Error(`Sitemap export unavailable: ${result.data.export.reason}`);
466
+ console.log(result.data.export.url);
467
+ }
468
+ });
87
469
  const sitemapsCommand = defineCommand({
88
470
  meta: {
89
471
  name: "sitemaps",
@@ -91,388 +473,16 @@ const sitemapsCommand = defineCommand({
91
473
  },
92
474
  subCommands: {
93
475
  list: listCommand,
94
- get: defineCommand({
95
- meta: {
96
- name: "get",
97
- description: "Get details for a specific sitemap"
98
- },
99
- args: {
100
- ...OUTPUT_ARGS,
101
- site: {
102
- type: "string",
103
- alias: "s",
104
- description: "Site URL (defaults to config.defaultSite or prompt)"
105
- },
106
- url: {
107
- type: "positional",
108
- required: true,
109
- description: "Sitemap URL"
110
- }
111
- },
112
- async run({ args }) {
113
- const { json } = applyOutputMode(args);
114
- const ctx = await createCommandContext({ needsAuth: true });
115
- const siteUrl = await ctx.resolveSite(args.site ? String(args.site) : void 0);
116
- const client = ctx.client;
117
- const sitemap = await fetchSitemap(client, siteUrl, args.url).catch(gscErrorHandler);
118
- if (json) {
119
- console.log(JSON.stringify(sitemap, null, 2));
120
- return;
121
- }
122
- console.log();
123
- console.log(` \x1B[1mPath:\x1B[0m ${sitemap.path}`);
124
- console.log(` \x1B[1mType:\x1B[0m ${sitemap.type || "sitemap"}`);
125
- console.log(` \x1B[1mLast Submitted:\x1B[0m ${sitemap.lastSubmitted || "N/A"}`);
126
- console.log(` \x1B[1mLast Downloaded:\x1B[0m ${sitemap.lastDownloaded || "N/A"}`);
127
- console.log(` \x1B[1mPending:\x1B[0m ${sitemap.isPending ? "Yes" : "No"}`);
128
- console.log(` \x1B[1mErrors:\x1B[0m ${sitemap.errors || 0}`);
129
- console.log(` \x1B[1mWarnings:\x1B[0m ${sitemap.warnings || 0}`);
130
- if (sitemap.contents?.length) {
131
- console.log();
132
- console.log(" \x1B[1mContents:\x1B[0m");
133
- for (const c of sitemap.contents) console.log(` ${c.type}: ${c.submitted} submitted, ${c.indexed} indexed`);
134
- }
135
- }
136
- }),
137
- submit: defineCommand({
138
- meta: {
139
- name: "submit",
140
- description: "Submit a sitemap to GSC"
141
- },
142
- args: {
143
- ...OUTPUT_ARGS,
144
- site: {
145
- type: "string",
146
- alias: "s",
147
- description: "Site URL (defaults to config.defaultSite or prompt)"
148
- },
149
- url: {
150
- type: "positional",
151
- required: true,
152
- description: "Sitemap URL to submit"
153
- }
154
- },
155
- async run({ args }) {
156
- const { json } = applyOutputMode(args);
157
- const ctx = await createCommandContext({ needsAuth: true });
158
- const siteUrl = await ctx.resolveSite(args.site ? String(args.site) : void 0);
159
- await ctx.client.sitemaps.submit(siteUrl, args.url).catch(gscErrorHandler);
160
- if (json) {
161
- console.log(JSON.stringify({
162
- siteUrl,
163
- feedpath: args.url,
164
- status: "submitted"
165
- }, null, 2));
166
- return;
167
- }
168
- logger.success(`Submitted sitemap: ${args.url}`);
169
- }
170
- }),
171
- delete: defineCommand({
172
- meta: {
173
- name: "delete",
174
- description: "Delete a sitemap from GSC"
175
- },
176
- args: {
177
- ...OUTPUT_ARGS,
178
- site: {
179
- type: "string",
180
- alias: "s",
181
- description: "Site URL (defaults to config.defaultSite or prompt)"
182
- },
183
- url: {
184
- type: "positional",
185
- required: true,
186
- description: "Sitemap URL to delete"
187
- }
188
- },
189
- async run({ args }) {
190
- const { json } = applyOutputMode(args);
191
- const ctx = await createCommandContext({ needsAuth: true });
192
- const siteUrl = await ctx.resolveSite(args.site ? String(args.site) : void 0);
193
- await ctx.client.sitemaps.delete(siteUrl, args.url).catch(gscErrorHandler);
194
- if (json) {
195
- console.log(JSON.stringify({
196
- siteUrl,
197
- feedpath: args.url,
198
- status: "deleted"
199
- }, null, 2));
200
- return;
201
- }
202
- logger.success(`Deleted sitemap: ${args.url}`);
203
- }
204
- }),
205
- discover: defineCommand({
206
- meta: {
207
- name: "discover",
208
- description: "Probe a domain's robots.txt + common paths for an advertised sitemap (no auth needed)"
209
- },
210
- args: {
211
- ...OUTPUT_ARGS,
212
- domain: {
213
- type: "positional",
214
- required: true,
215
- description: "Domain (e.g., example.com)"
216
- }
217
- },
218
- async run({ args }) {
219
- const { json } = applyOutputMode(args);
220
- const domain = String(args.domain).replace(/^https?:\/\//, "").replace(/\/.*$/, "");
221
- const discovery = await discoverLiveSitemap(domain);
222
- const url = discovery._tag === "found" ? discovery.url : null;
223
- if (json) {
224
- console.log(JSON.stringify({
225
- domain,
226
- sitemap: url,
227
- status: discovery._tag
228
- }, null, 2));
229
- return;
230
- }
231
- if (discovery._tag === "incomplete") {
232
- logger.error(`Sitemap discovery was incomplete: ${discovery.failures[0] ?? "unknown failure"}`);
233
- process.exit(1);
234
- }
235
- if (!url) {
236
- logger.warn(`No sitemap discovered for ${domain}`);
237
- process.exit(1);
238
- }
239
- logger.success(`Discovered sitemap: ${url}`);
240
- }
241
- }),
242
- urls: defineCommand({
243
- meta: {
244
- name: "urls",
245
- description: "Fetch a sitemap (or sitemap index) and dump its <loc> URLs (no auth needed)"
246
- },
247
- args: {
248
- ...OUTPUT_ARGS,
249
- "url": {
250
- type: "positional",
251
- required: true,
252
- description: "Sitemap URL (index files are followed)"
253
- },
254
- "limit": {
255
- type: "string",
256
- alias: "l",
257
- description: "Stop after N URLs across all nested sitemaps"
258
- },
259
- "max-depth": {
260
- type: "string",
261
- description: "Max sitemap-index nesting depth (default: 3)"
262
- }
263
- },
264
- async run({ args }) {
265
- const { json } = applyOutputMode(args);
266
- const limit = args.limit ? Number.parseInt(String(args.limit), 10) : void 0;
267
- const maxDepth = args["max-depth"] ? Number.parseInt(String(args["max-depth"]), 10) : void 0;
268
- const result = await loadSitemapUrls(String(args.url), {
269
- maxUrls: limit,
270
- maxDepth
271
- });
272
- if (result._tag === "error") {
273
- logger.error(`Sitemap fetch failed: ${result.message}`);
274
- process.exit(1);
275
- }
276
- const { urls, complete, documentsRead } = result.value;
277
- if (json) {
278
- console.log(JSON.stringify({
279
- sitemap: args.url,
280
- count: urls.length,
281
- complete,
282
- documentsRead,
283
- urls
284
- }, null, 2));
285
- return;
286
- }
287
- if (!complete) logger.warn("Sitemap walk was truncated or a child document failed");
288
- for (const u of urls) console.log(u);
289
- }
290
- }),
291
- current: defineCommand({
292
- meta: {
293
- name: "current",
294
- description: "Read the hosted canonical sitemap generation and feed list"
295
- },
296
- args: {
297
- ...OUTPUT_ARGS,
298
- ...HOSTED_ARGS,
299
- "site-id": {
300
- type: "positional",
301
- required: true,
302
- description: "Hosted gscdump site id"
303
- }
304
- },
305
- async run({ args }) {
306
- const { json } = applyOutputMode(args);
307
- const result = await hostedClient(args).getSiteSitemaps({ params: { siteId: String(args["site-id"]) } });
308
- if (json) {
309
- console.log(JSON.stringify(result.data, null, 2));
310
- return;
311
- }
312
- const generation = result.data.generation;
313
- console.log(generation ? `Generation ${generation.id}, observed ${new Date(generation.observedAt).toISOString()}` : "No canonical generation");
314
- for (const sitemap of result.data.sitemaps) console.log(`${sitemap.path}\t${sitemap.urlCount}`);
315
- }
316
- }),
317
- history: defineCommand({
318
- meta: {
319
- name: "history",
320
- description: "Read hosted canonical sitemap membership and lastmod changes"
321
- },
322
- args: {
323
- ...OUTPUT_ARGS,
324
- ...HOSTED_ARGS,
325
- "site-id": {
326
- type: "positional",
327
- required: true,
328
- description: "Hosted gscdump site id"
329
- },
330
- "days": {
331
- type: "string",
332
- description: "History window in days"
333
- }
334
- },
335
- async run({ args }) {
336
- const { json } = applyOutputMode(args);
337
- const days = args.days ? Number.parseInt(String(args.days), 10) : void 0;
338
- const result = await hostedClient(args).getSiteSitemapChanges({
339
- params: { siteId: String(args["site-id"]) },
340
- query: { ...days ? { days } : {} }
341
- });
342
- console.log(JSON.stringify(result.data, null, json ? 2 : 0));
343
- }
344
- }),
345
- membership: defineCommand({
346
- meta: {
347
- name: "membership",
348
- description: "Query exact hosted membership evidence for comma-separated URLs"
349
- },
350
- args: {
351
- ...OUTPUT_ARGS,
352
- ...HOSTED_ARGS,
353
- "site-id": {
354
- type: "positional",
355
- required: true,
356
- description: "Hosted gscdump site id"
357
- },
358
- "urls": {
359
- type: "string",
360
- required: true,
361
- description: "Comma-separated exact URLs"
362
- },
363
- "generation": {
364
- type: "string",
365
- description: "Pin the query to a generation id"
366
- }
367
- },
368
- async run({ args }) {
369
- const { json } = applyOutputMode(args);
370
- const urls = String(args.urls).split(",").map((url) => url.trim()).filter(Boolean);
371
- const result = await hostedClient(args).querySitemapMembership({
372
- params: { siteId: String(args["site-id"]) },
373
- body: {
374
- urls,
375
- ...args.generation ? { generationId: String(args.generation) } : {}
376
- }
377
- });
378
- if (json) {
379
- console.log(JSON.stringify(result.data, null, 2));
380
- return;
381
- }
382
- for (const evidence of result.data.evidence) {
383
- const detail = evidence._tag === "present" ? `${evidence.feedpath}\t${evidence.lastmod ?? ""}` : evidence._tag === "absent" ? String(evidence.observedAt) : evidence.reason;
384
- console.log(`${evidence._tag}\t${evidence.url}\t${detail}`);
385
- }
386
- }
387
- }),
388
- lastmod: defineCommand({
389
- meta: {
390
- name: "lastmod",
391
- description: "List a generation-pinned hosted page of URL lastmod evidence"
392
- },
393
- args: {
394
- ...OUTPUT_ARGS,
395
- ...HOSTED_ARGS,
396
- "site-id": {
397
- type: "positional",
398
- required: true,
399
- description: "Hosted gscdump site id"
400
- },
401
- "generation": {
402
- type: "string",
403
- description: "Pin to a generation id"
404
- },
405
- "feedpath": {
406
- type: "string",
407
- description: "Restrict to one exact sitemap feed URL"
408
- },
409
- "cursor": {
410
- type: "string",
411
- description: "Opaque cursor from the prior page"
412
- },
413
- "limit": {
414
- type: "string",
415
- description: "Page size, maximum 10000"
416
- }
417
- },
418
- async run({ args }) {
419
- const { json } = applyOutputMode(args);
420
- const result = await hostedClient(args).listSitemapUrls({
421
- params: { siteId: String(args["site-id"]) },
422
- query: {
423
- ...args.generation ? { generationId: String(args.generation) } : {},
424
- ...args.feedpath ? { feedpath: String(args.feedpath) } : {},
425
- ...args.cursor ? { cursor: String(args.cursor) } : {},
426
- ...args.limit ? { limit: Number.parseInt(String(args.limit), 10) } : {}
427
- }
428
- });
429
- if (json) {
430
- console.log(JSON.stringify(result.data, null, 2));
431
- return;
432
- }
433
- for (const item of result.data.items) console.log(`${item.url}\t${item.lastmod ?? ""}\t${item.feedpath}`);
434
- if (result.data.page.nextCursor) logger.info(`Next cursor: ${result.data.page.nextCursor}`);
435
- }
436
- }),
437
- export: defineCommand({
438
- meta: {
439
- name: "export",
440
- description: "Get a generation-pinned hosted bulk export descriptor"
441
- },
442
- args: {
443
- ...OUTPUT_ARGS,
444
- ...HOSTED_ARGS,
445
- "site-id": {
446
- type: "positional",
447
- required: true,
448
- description: "Hosted gscdump site id"
449
- },
450
- "generation": {
451
- type: "string",
452
- description: "Pin to a generation id"
453
- },
454
- "feedpath": {
455
- type: "string",
456
- description: "Restrict to one exact sitemap feed URL"
457
- }
458
- },
459
- async run({ args }) {
460
- const { json } = applyOutputMode(args);
461
- const result = await hostedClient(args).getSitemapExport({
462
- params: { siteId: String(args["site-id"]) },
463
- query: {
464
- ...args.generation ? { generationId: String(args.generation) } : {},
465
- ...args.feedpath ? { feedpath: String(args.feedpath) } : {}
466
- }
467
- });
468
- if (json) {
469
- console.log(JSON.stringify(result.data, null, 2));
470
- return;
471
- }
472
- if (result.data.export._tag === "unavailable") throw new Error(`Sitemap export unavailable: ${result.data.export.reason}`);
473
- console.log(result.data.export.url);
474
- }
475
- })
476
+ get: getCommand,
477
+ submit: submitCommand,
478
+ delete: deleteCommand,
479
+ discover: discoverCommand,
480
+ urls: urlsCommand,
481
+ current: currentCommand,
482
+ history: historyCommand,
483
+ membership: membershipCommand,
484
+ lastmod: lastmodCommand,
485
+ export: exportCommand
476
486
  },
477
487
  async run({ args }) {
478
488
  if (!noSubcommandSelected("sitemaps", [