@gscdump/cli 3.4.4 → 3.6.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 +217 -54
- package/bin/gscdump.mjs +6 -1
- package/dist/analysis-local.mjs +1 -1
- package/dist/auth-state.mjs +112 -0
- package/dist/auth.mjs +109 -122
- package/dist/bing-auth.mjs +200 -0
- package/dist/bing-data.mjs +160 -0
- package/dist/bing-hosted.mjs +121 -0
- package/dist/cli.d.mts +4 -4
- package/dist/cli.mjs +20 -21
- package/dist/cloud-google.mjs +92 -0
- package/dist/command-meta.mjs +13 -1
- package/dist/command-registry.mjs +5 -2
- package/dist/commands/analyze.mjs +34 -177
- package/dist/commands/auth.mjs +161 -7
- package/dist/commands/bing.mjs +337 -0
- package/dist/commands/config.mjs +36 -43
- package/dist/commands/doctor.mjs +73 -24
- package/dist/commands/dump.mjs +1 -1
- package/dist/commands/entities.mjs +4 -4
- package/dist/commands/indexing.mjs +11 -14
- package/dist/commands/init.mjs +1 -1
- package/dist/commands/inspect.mjs +3 -3
- package/dist/commands/mcp.mjs +16 -2
- package/dist/commands/papercut.mjs +76 -0
- package/dist/commands/profile-selection.mjs +2 -2
- package/dist/commands/profile.mjs +8 -3
- package/dist/commands/query.mjs +86 -40
- package/dist/commands/report.mjs +5 -3
- package/dist/commands/sitemaps.mjs +24 -18
- package/dist/commands/skill.mjs +52 -0
- package/dist/commands/stats.mjs +58 -32
- package/dist/commands/sync.mjs +58 -25
- package/dist/config.mjs +39 -4
- package/dist/context.mjs +13 -8
- package/dist/env-file.mjs +1 -1
- package/dist/local-store.mjs +2 -2
- package/dist/mcp/errors.mjs +8 -0
- package/dist/mcp/handlers/diagnostics.mjs +31 -0
- package/dist/mcp/handlers/reports.mjs +38 -11
- package/dist/mcp/server/index.mjs +9 -10
- package/dist/mcp/types.mjs +8 -3
- package/dist/package.mjs +1 -1
- package/dist/papercut.mjs +99 -0
- package/dist/render/analysis.mjs +98 -0
- package/dist/render/charts.mjs +170 -0
- package/dist/render/layout.mjs +87 -0
- package/dist/render/metrics.mjs +163 -0
- package/dist/render/query.mjs +30 -0
- package/dist/render/report.mjs +69 -0
- package/dist/render/terminal.mjs +25 -0
- package/dist/runtime.d.mts +5 -5
- package/dist/runtime.mjs +1 -1
- package/dist/skill.mjs +45 -0
- package/dist/utils.mjs +10 -38
- package/package.json +14 -12
- package/skills/gscdump/SKILL.md +287 -0
package/dist/commands/query.mjs
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { queryCommandMeta } from "../command-meta.mjs";
|
|
2
2
|
import { loadConfig } from "../config.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { terminalOutputOptions } from "../render/terminal.mjs";
|
|
4
|
+
import { ALL_SEARCH_TYPES, logger, parseSearchType, toCSV } from "../utils.mjs";
|
|
4
5
|
import { allTables, inferTable } from "../local-store.mjs";
|
|
5
6
|
import { createCommandContext } from "../context.mjs";
|
|
6
7
|
import { gscErrorHandler } from "../error-handler.mjs";
|
|
8
|
+
import { renderTable } from "../render/layout.mjs";
|
|
9
|
+
import { asRecord, columnsFor } from "../render/analysis.mjs";
|
|
10
|
+
import { renderQuery } from "../render/query.mjs";
|
|
7
11
|
import process from "node:process";
|
|
8
12
|
import { defineCommand } from "citty";
|
|
9
13
|
import fs from "node:fs/promises";
|
|
@@ -53,6 +57,8 @@ const AGGREGATION_TYPES = [
|
|
|
53
57
|
"byPage",
|
|
54
58
|
"byProperty"
|
|
55
59
|
];
|
|
60
|
+
const POSITIVE_INTEGER_RE = /^\d+$/;
|
|
61
|
+
const ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
56
62
|
function buildFilterFromArg(col, raw) {
|
|
57
63
|
if (raw.startsWith("!~")) return makeLeaf(col, "notContains", raw.slice(2));
|
|
58
64
|
if (raw.startsWith("!re:")) return notRegex(col, raw.slice(4));
|
|
@@ -148,8 +154,7 @@ const queryCommand = defineCommand({
|
|
|
148
154
|
"limit": {
|
|
149
155
|
type: "string",
|
|
150
156
|
alias: "l",
|
|
151
|
-
default:
|
|
152
|
-
description: "Max rows (default: 1000)"
|
|
157
|
+
description: "Max rows (default: saved defaultLimit or 1000)"
|
|
153
158
|
},
|
|
154
159
|
"output": {
|
|
155
160
|
type: "string",
|
|
@@ -159,8 +164,7 @@ const queryCommand = defineCommand({
|
|
|
159
164
|
"format": {
|
|
160
165
|
type: "string",
|
|
161
166
|
alias: "f",
|
|
162
|
-
|
|
163
|
-
description: "Output format: json or csv"
|
|
167
|
+
description: "Output format: table, json, or csv (default: saved defaultFormat or json)"
|
|
164
168
|
},
|
|
165
169
|
"sql": {
|
|
166
170
|
type: "string",
|
|
@@ -231,24 +235,33 @@ const queryCommand = defineCommand({
|
|
|
231
235
|
}
|
|
232
236
|
},
|
|
233
237
|
async run({ args }) {
|
|
238
|
+
const ctxConfig = await loadConfig();
|
|
239
|
+
const format = args.format ?? ctxConfig.defaultFormat ?? "json";
|
|
240
|
+
if (format !== "json" && format !== "csv" && format !== "table") {
|
|
241
|
+
logger.error("Invalid --format. Use table, json, or csv.");
|
|
242
|
+
process.exit(1);
|
|
243
|
+
}
|
|
234
244
|
if (args.sql) {
|
|
235
245
|
await runRawSqlMode({
|
|
236
246
|
sql: String(args.sql),
|
|
237
247
|
site: args.site ? String(args.site) : void 0,
|
|
238
248
|
table: args.table ? String(args.table) : "pages",
|
|
239
249
|
output: args.output ? String(args.output) : void 0,
|
|
250
|
+
format,
|
|
240
251
|
quiet: Boolean(args.quiet),
|
|
241
252
|
searchType: parseSearchType(args.type, "--type")
|
|
242
253
|
});
|
|
243
254
|
return;
|
|
244
255
|
}
|
|
245
|
-
const ctxConfig = await loadConfig();
|
|
246
256
|
const dimNames = await resolveDimensions(args);
|
|
247
257
|
const { startDate, endDate } = await resolveRange(args);
|
|
248
258
|
await promptFilters(args);
|
|
249
|
-
const limitArg = args.limit
|
|
250
|
-
const rowLimit =
|
|
251
|
-
|
|
259
|
+
const limitArg = String(args.limit ?? ctxConfig.defaultLimit ?? 1e3);
|
|
260
|
+
const rowLimit = Number(limitArg);
|
|
261
|
+
if (!POSITIVE_INTEGER_RE.test(limitArg) || !Number.isSafeInteger(rowLimit) || rowLimit < 1) {
|
|
262
|
+
logger.error("Invalid --limit. Use a positive safe integer, such as --limit 1000.");
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
252
265
|
const dimensionFilter = buildDimensionFilter(args);
|
|
253
266
|
const searchType = parseSearchType(args.type ?? ctxConfig.defaultSearchType, "--type");
|
|
254
267
|
const dataState = args["data-state"] ? String(args["data-state"]) : ctxConfig.defaultDataState;
|
|
@@ -285,7 +298,7 @@ const queryCommand = defineCommand({
|
|
|
285
298
|
}, null, 2));
|
|
286
299
|
return;
|
|
287
300
|
}
|
|
288
|
-
if (!args.quiet) logger.
|
|
301
|
+
if (!args.quiet) logger.debug(`Querying ${siteUrl} via live GSC API...`);
|
|
289
302
|
const result = await runLiveQuery(ctx.client, siteUrl, {
|
|
290
303
|
startDate,
|
|
291
304
|
endDate,
|
|
@@ -314,7 +327,7 @@ const queryCommand = defineCommand({
|
|
|
314
327
|
return;
|
|
315
328
|
}
|
|
316
329
|
if (dataState || aggregationType) logger.warn("--data-state / --aggregation-type are ignored without --live");
|
|
317
|
-
if (!args.quiet) logger.
|
|
330
|
+
if (!args.quiet) logger.debug(`Querying ${siteUrl} from local Parquet store...`);
|
|
318
331
|
const state = buildLocalState(dimNames, startDate, endDate, rowLimit, dimensionFilter);
|
|
319
332
|
const store = ctx.store;
|
|
320
333
|
const table = inferTable(dimNames);
|
|
@@ -357,7 +370,14 @@ const queryCommand = defineCommand({
|
|
|
357
370
|
}
|
|
358
371
|
});
|
|
359
372
|
async function resolveDimensions(args) {
|
|
360
|
-
if (args.dimensions
|
|
373
|
+
if (args.dimensions != null) {
|
|
374
|
+
const dimensions = String(args.dimensions).split(",").map((d) => d.trim());
|
|
375
|
+
if (dimensions.some((d) => !DIMENSIONS.includes(d))) {
|
|
376
|
+
logger.error(`Invalid --dimensions. Use a comma-separated list from: ${DIMENSIONS.join(", ")}.`);
|
|
377
|
+
process.exit(1);
|
|
378
|
+
}
|
|
379
|
+
return [...new Set(dimensions)];
|
|
380
|
+
}
|
|
361
381
|
if (args.interactive) {
|
|
362
382
|
const selected = await multiselect({
|
|
363
383
|
message: "Select dimensions",
|
|
@@ -376,35 +396,48 @@ async function resolveDimensions(args) {
|
|
|
376
396
|
return ["page", "query"];
|
|
377
397
|
}
|
|
378
398
|
async function resolveRange(args) {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
endDate: String(args.end)
|
|
382
|
-
};
|
|
399
|
+
let startDate = args.start != null ? String(args.start) : void 0;
|
|
400
|
+
let endDate = args.end != null ? String(args.end) : void 0;
|
|
383
401
|
if (args.interactive) {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
402
|
+
if (startDate === void 0) {
|
|
403
|
+
const startInput = await text({
|
|
404
|
+
message: "Start date (YYYY-MM-DD)",
|
|
405
|
+
placeholder: daysAgoUtc(28)
|
|
406
|
+
});
|
|
407
|
+
if (isCancel(startInput)) {
|
|
408
|
+
cancel("Cancelled");
|
|
409
|
+
process.exit(0);
|
|
410
|
+
}
|
|
411
|
+
startDate = String(startInput) || daysAgoUtc(28);
|
|
391
412
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
413
|
+
if (endDate === void 0) {
|
|
414
|
+
const endInput = await text({
|
|
415
|
+
message: "End date (YYYY-MM-DD)",
|
|
416
|
+
placeholder: daysAgoUtc(3)
|
|
417
|
+
});
|
|
418
|
+
if (isCancel(endInput)) {
|
|
419
|
+
cancel("Cancelled");
|
|
420
|
+
process.exit(0);
|
|
421
|
+
}
|
|
422
|
+
endDate = String(endInput) || daysAgoUtc(3);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
startDate ??= daysAgoUtc(31);
|
|
426
|
+
endDate ??= daysAgoUtc(3);
|
|
427
|
+
for (const [flag, value] of [["--start", startDate], ["--end", endDate]]) {
|
|
428
|
+
const date = /* @__PURE__ */ new Date(`${value}T00:00:00Z`);
|
|
429
|
+
if (!ISO_DATE_RE.test(value) || !Number.isFinite(date.getTime()) || date.toISOString().slice(0, 10) !== value) {
|
|
430
|
+
logger.error(`Invalid ${flag}. Use a calendar date in YYYY-MM-DD format.`);
|
|
431
|
+
process.exit(1);
|
|
399
432
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
433
|
+
}
|
|
434
|
+
if (startDate > endDate) {
|
|
435
|
+
logger.error("Invalid date range. --start must be on or before --end.");
|
|
436
|
+
process.exit(1);
|
|
404
437
|
}
|
|
405
438
|
return {
|
|
406
|
-
startDate
|
|
407
|
-
endDate
|
|
439
|
+
startDate,
|
|
440
|
+
endDate
|
|
408
441
|
};
|
|
409
442
|
}
|
|
410
443
|
async function promptFilters(args) {
|
|
@@ -492,7 +525,7 @@ async function runRawSqlMode(opts) {
|
|
|
492
525
|
});
|
|
493
526
|
const siteUrl = await ctx.resolveSite(opts.site);
|
|
494
527
|
const store = ctx.store;
|
|
495
|
-
if (!opts.quiet) logger.
|
|
528
|
+
if (!opts.quiet) logger.debug(`Running raw SQL over table "${opts.table}" for ${siteUrl}`);
|
|
496
529
|
const { rows, sql } = await store.runRawSql({
|
|
497
530
|
sql: opts.sql,
|
|
498
531
|
siteUrl,
|
|
@@ -502,11 +535,11 @@ async function runRawSqlMode(opts) {
|
|
|
502
535
|
logger.error(`SQL failed: ${e.message}`);
|
|
503
536
|
process.exit(1);
|
|
504
537
|
});
|
|
505
|
-
const payload = JSON.stringify({
|
|
538
|
+
const payload = opts.format === "table" ? renderTable(rows, columnsFor(rows), terminalOutputOptions(Boolean(opts.output && opts.output !== "-"))).join("\n") : opts.format === "csv" ? toCSV(rows, Object.keys(rows[0] ?? {})) : JSON.stringify({
|
|
506
539
|
sql,
|
|
507
540
|
total: rows.length,
|
|
508
541
|
data: rows
|
|
509
|
-
},
|
|
542
|
+
}, (_key, value) => typeof value === "bigint" ? value.toString() : value, 2);
|
|
510
543
|
if (opts.output && opts.output !== "-") {
|
|
511
544
|
await fs.writeFile(opts.output, payload);
|
|
512
545
|
if (!opts.quiet) logger.info(`Written to ${opts.output}`);
|
|
@@ -524,7 +557,20 @@ function logProfile(spans) {
|
|
|
524
557
|
logger.info(`Query timing breakdown:\n${lines.join("\n")}\n${row("total", total)}`);
|
|
525
558
|
}
|
|
526
559
|
async function writeOutput(opts) {
|
|
527
|
-
const
|
|
560
|
+
const range = asRecord(opts.output.dateRange);
|
|
561
|
+
const content = opts.format === "table" ? renderQuery({
|
|
562
|
+
site: String(opts.output.siteUrl),
|
|
563
|
+
start: String(range.start),
|
|
564
|
+
end: String(range.end),
|
|
565
|
+
dimensions: opts.output.dimensions,
|
|
566
|
+
rows: opts.output.data
|
|
567
|
+
}, terminalOutputOptions(Boolean(opts.path && opts.path !== "-"))) : opts.format === "csv" ? toCSV(opts.output.data, [
|
|
568
|
+
...opts.output.dimensions,
|
|
569
|
+
"clicks",
|
|
570
|
+
"impressions",
|
|
571
|
+
"ctr",
|
|
572
|
+
"position"
|
|
573
|
+
]) : JSON.stringify(opts.output, null, 2);
|
|
528
574
|
if (opts.path && opts.path !== "-") {
|
|
529
575
|
await fs.writeFile(opts.path, content);
|
|
530
576
|
if (!opts.quiet) logger.info(`Written to ${opts.path}`);
|
package/dist/commands/report.mjs
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { reportCommandMeta } from "../command-meta.mjs";
|
|
2
|
+
import { terminalOutputOptions } from "../render/terminal.mjs";
|
|
2
3
|
import { logger } from "../utils.mjs";
|
|
3
4
|
import { resolveAnalysisSource } from "../analysis-local.mjs";
|
|
5
|
+
import { renderCliReport } from "../render/report.mjs";
|
|
4
6
|
import { defineCommand } from "citty";
|
|
5
7
|
import { defaultAnalyzerRegistry } from "@gscdump/analysis/registry";
|
|
6
|
-
import { err, ok, unwrapResult } from "gscdump/result";
|
|
7
|
-
import { defaultReportRegistry, dryRunReport, formatReport, runReport } from "@gscdump/analysis/report";
|
|
8
8
|
import { resolveWindow } from "@gscdump/engine/period";
|
|
9
|
+
import { err, ok, unwrapResult } from "gscdump/result";
|
|
10
|
+
import { defaultReportRegistry, dryRunReport, runReport } from "@gscdump/analysis/report";
|
|
9
11
|
function reportFlagErrorToException(error) {
|
|
10
12
|
const exception = new Error(error.message);
|
|
11
13
|
exception.reportFlagError = error;
|
|
@@ -201,7 +203,7 @@ function makeReportCommand(report) {
|
|
|
201
203
|
console.log(JSON.stringify(result, null, 2));
|
|
202
204
|
return;
|
|
203
205
|
}
|
|
204
|
-
console.log(
|
|
206
|
+
console.log(renderCliReport(result, terminalOutputOptions()));
|
|
205
207
|
if (result.meta.degraded) logger.warn(`degraded: ${result.meta.steps.filter((s) => s.status === "error").map((s) => `${s.key}(${s.error})`).join(", ")}`);
|
|
206
208
|
}
|
|
207
209
|
});
|
|
@@ -1,31 +1,36 @@
|
|
|
1
|
+
import { parseAuthentication, resolveAuthentication } from "../auth-state.mjs";
|
|
1
2
|
import { sitemapsCommandMeta } from "../command-meta.mjs";
|
|
2
3
|
import { resolveCliEnvironment } from "../environment.mjs";
|
|
3
|
-
import { OUTPUT_ARGS, applyOutputMode, logger, noSubcommandSelected } from "../utils.mjs";
|
|
4
|
+
import { OUTPUT_ARGS, applyOutputMode, logger, noSubcommandSelected, parseIntegerOption } from "../utils.mjs";
|
|
4
5
|
import { createCommandContext } from "../context.mjs";
|
|
5
6
|
import { gscErrorHandler } from "../error-handler.mjs";
|
|
6
7
|
import { discoverLiveSitemap, loadSitemapUrls } from "../sitemap.mjs";
|
|
7
8
|
import process from "node:process";
|
|
8
9
|
import { defineCommand } from "citty";
|
|
9
|
-
import { fetchSitemap } from "gscdump/sites";
|
|
10
10
|
import { createGscdumpV1Client } from "@gscdump/sdk/v1";
|
|
11
|
+
import { fetchSitemap } from "gscdump/sites";
|
|
11
12
|
const HOSTED_ARGS = {
|
|
12
13
|
"api-root": {
|
|
13
14
|
type: "string",
|
|
14
|
-
description: "Hosted API root; defaults to
|
|
15
|
+
description: "Hosted API root; defaults to saved cloud authentication or https://gscdump.com/api"
|
|
15
16
|
},
|
|
16
17
|
"api-key": {
|
|
17
18
|
type: "string",
|
|
18
19
|
description: "Hosted API key; defaults to GSCDUMP_API_KEY"
|
|
19
20
|
}
|
|
20
21
|
};
|
|
21
|
-
function hostedClient(args) {
|
|
22
|
+
async function hostedClient(args) {
|
|
22
23
|
const environment = resolveCliEnvironment().values;
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
const authentication = args["api-key"] ? parseAuthentication({
|
|
25
|
+
_tag: "Cloud",
|
|
26
|
+
apiKey: args["api-key"],
|
|
27
|
+
apiRoot: String(args["api-root"] || environment.GSCDUMP_API_ROOT || "https://gscdump.com/api")
|
|
28
|
+
}) : await resolveAuthentication();
|
|
29
|
+
if (authentication._tag !== "Cloud") throw new Error("Hosted sitemap reads require cloud authentication. Run `gscdump auth login --mode cloud` or supply --api-key.");
|
|
30
|
+
if (args["api-root"] && String(args["api-root"]).replace(/\/+$/, "") !== authentication.apiRoot) throw new Error("The API root changed. Supply --api-key explicitly for the new API root.");
|
|
26
31
|
return createGscdumpV1Client({
|
|
27
|
-
apiRoot,
|
|
28
|
-
credential: apiKey
|
|
32
|
+
apiRoot: authentication.apiRoot,
|
|
33
|
+
credential: authentication.apiKey
|
|
29
34
|
});
|
|
30
35
|
}
|
|
31
36
|
const listCommand = defineCommand({
|
|
@@ -257,8 +262,8 @@ const urlsCommand = defineCommand({
|
|
|
257
262
|
},
|
|
258
263
|
async run({ args }) {
|
|
259
264
|
const { json } = applyOutputMode(args);
|
|
260
|
-
const limit =
|
|
261
|
-
const maxDepth = args["max-depth"]
|
|
265
|
+
const limit = parseIntegerOption(args.limit, "--limit");
|
|
266
|
+
const maxDepth = parseIntegerOption(args["max-depth"], "--max-depth", 0);
|
|
262
267
|
const result = await loadSitemapUrls(String(args.url), {
|
|
263
268
|
maxUrls: limit,
|
|
264
269
|
maxDepth
|
|
@@ -298,7 +303,7 @@ const currentCommand = defineCommand({
|
|
|
298
303
|
},
|
|
299
304
|
async run({ args }) {
|
|
300
305
|
const { json } = applyOutputMode(args);
|
|
301
|
-
const result = await hostedClient(args).getSiteSitemaps({ params: { siteId: String(args["site-id"]) } });
|
|
306
|
+
const result = await (await hostedClient(args)).getSiteSitemaps({ params: { siteId: String(args["site-id"]) } });
|
|
302
307
|
if (json) {
|
|
303
308
|
console.log(JSON.stringify(result.data, null, 2));
|
|
304
309
|
return;
|
|
@@ -328,8 +333,8 @@ const historyCommand = defineCommand({
|
|
|
328
333
|
},
|
|
329
334
|
async run({ args }) {
|
|
330
335
|
const { json } = applyOutputMode(args);
|
|
331
|
-
const days =
|
|
332
|
-
const result = await hostedClient(args).getSiteSitemapChanges({
|
|
336
|
+
const days = parseIntegerOption(args.days, "--days");
|
|
337
|
+
const result = await (await hostedClient(args)).getSiteSitemapChanges({
|
|
333
338
|
params: { siteId: String(args["site-id"]) },
|
|
334
339
|
query: { ...days ? { days } : {} }
|
|
335
340
|
});
|
|
@@ -362,7 +367,7 @@ const membershipCommand = defineCommand({
|
|
|
362
367
|
async run({ args }) {
|
|
363
368
|
const { json } = applyOutputMode(args);
|
|
364
369
|
const urls = String(args.urls).split(",").map((url) => url.trim()).filter(Boolean);
|
|
365
|
-
const result = await hostedClient(args).querySitemapMembership({
|
|
370
|
+
const result = await (await hostedClient(args)).querySitemapMembership({
|
|
366
371
|
params: { siteId: String(args["site-id"]) },
|
|
367
372
|
body: {
|
|
368
373
|
urls,
|
|
@@ -411,13 +416,14 @@ const lastmodCommand = defineCommand({
|
|
|
411
416
|
},
|
|
412
417
|
async run({ args }) {
|
|
413
418
|
const { json } = applyOutputMode(args);
|
|
414
|
-
const
|
|
419
|
+
const limit = parseIntegerOption(args.limit, "--limit");
|
|
420
|
+
const result = await (await hostedClient(args)).listSitemapUrls({
|
|
415
421
|
params: { siteId: String(args["site-id"]) },
|
|
416
422
|
query: {
|
|
417
423
|
...args.generation ? { generationId: String(args.generation) } : {},
|
|
418
424
|
...args.feedpath ? { feedpath: String(args.feedpath) } : {},
|
|
419
425
|
...args.cursor ? { cursor: String(args.cursor) } : {},
|
|
420
|
-
...
|
|
426
|
+
...limit !== void 0 ? { limit } : {}
|
|
421
427
|
}
|
|
422
428
|
});
|
|
423
429
|
if (json) {
|
|
@@ -452,7 +458,7 @@ const exportCommand = defineCommand({
|
|
|
452
458
|
},
|
|
453
459
|
async run({ args }) {
|
|
454
460
|
const { json } = applyOutputMode(args);
|
|
455
|
-
const result = await hostedClient(args).getSitemapExport({
|
|
461
|
+
const result = await (await hostedClient(args)).getSitemapExport({
|
|
456
462
|
params: { siteId: String(args["site-id"]) },
|
|
457
463
|
query: {
|
|
458
464
|
...args.generation ? { generationId: String(args.generation) } : {},
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { skillCommandMeta } from "../command-meta.mjs";
|
|
2
|
+
import { OUTPUT_ARGS, applyOutputMode, displayPath, logger } from "../utils.mjs";
|
|
3
|
+
import { SKILL_AGENTS, SKILL_NAME, installSkill } from "../skill.mjs";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
import { defineCommand } from "citty";
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
const installCommand = defineCommand({
|
|
8
|
+
meta: {
|
|
9
|
+
name: "install",
|
|
10
|
+
description: `Copy the ${SKILL_NAME} skill into an agent skill directory`
|
|
11
|
+
},
|
|
12
|
+
args: {
|
|
13
|
+
agent: {
|
|
14
|
+
type: "string",
|
|
15
|
+
description: `Agent skill directory: ${SKILL_AGENTS.join(" or ")} (default claude)`,
|
|
16
|
+
default: "claude"
|
|
17
|
+
},
|
|
18
|
+
target: {
|
|
19
|
+
type: "string",
|
|
20
|
+
description: "Write into this directory instead of the agent home directory"
|
|
21
|
+
},
|
|
22
|
+
...OUTPUT_ARGS
|
|
23
|
+
},
|
|
24
|
+
async run({ args }) {
|
|
25
|
+
const { json } = applyOutputMode(args);
|
|
26
|
+
const agent = SKILL_AGENTS.find((name) => name === args.agent);
|
|
27
|
+
if (!agent) {
|
|
28
|
+
logger.error(`Unknown agent "${args.agent}". Use ${SKILL_AGENTS.join(" or ")}.`);
|
|
29
|
+
process.exit(2);
|
|
30
|
+
}
|
|
31
|
+
const result = await installSkill({
|
|
32
|
+
agent,
|
|
33
|
+
homeDirectory: os.homedir(),
|
|
34
|
+
target: args.target
|
|
35
|
+
});
|
|
36
|
+
if (result._tag === "Err") {
|
|
37
|
+
logger.error(result.message);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
if (json) {
|
|
41
|
+
console.log(JSON.stringify(result.installation));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
logger.success(`Installed the ${SKILL_NAME} skill for ${agent} at ${displayPath(result.installation.destination)}`);
|
|
45
|
+
logger.info("Read its SKILL.md before running other commands.");
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
const skillCommand = defineCommand({
|
|
49
|
+
meta: skillCommandMeta,
|
|
50
|
+
subCommands: { install: installCommand }
|
|
51
|
+
});
|
|
52
|
+
export { skillCommand };
|
package/dist/commands/stats.mjs
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
import { terminalOutputOptions } from "../render/terminal.mjs";
|
|
1
2
|
import { OUTPUT_ARGS, applyOutputMode, displayPath, formatAge, logger } from "../utils.mjs";
|
|
2
3
|
import { allTables } from "../local-store.mjs";
|
|
3
4
|
import { createCommandContext } from "../context.mjs";
|
|
5
|
+
import { renderTable, textLines } from "../render/layout.mjs";
|
|
6
|
+
import { formatMetric } from "../render/metrics.mjs";
|
|
7
|
+
import { barColumn } from "../render/charts.mjs";
|
|
8
|
+
import { columnsFor } from "../render/analysis.mjs";
|
|
4
9
|
import process from "node:process";
|
|
5
10
|
import { defineCommand } from "citty";
|
|
11
|
+
import { decodeSiteId, parseGscSiteUrl } from "gscdump";
|
|
6
12
|
import { filesystemStats } from "@gscdump/engine/filesystem";
|
|
7
13
|
const statsCommand = defineCommand({
|
|
8
14
|
meta: {
|
|
@@ -68,33 +74,59 @@ const statsCommand = defineCommand({
|
|
|
68
74
|
console.log(JSON.stringify(payload, null, 2));
|
|
69
75
|
return;
|
|
70
76
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
77
|
+
const options = terminalOutputOptions();
|
|
78
|
+
const rows = perTable.filter(({ live, retired }) => live.length || retired.length).map(({ table, live, retired }) => ({
|
|
79
|
+
table,
|
|
80
|
+
liveFiles: live.length,
|
|
81
|
+
liveRows: sumRows(live),
|
|
82
|
+
liveBytes: sumBytes(live),
|
|
83
|
+
retiredFiles: retired.length,
|
|
84
|
+
retiredBytes: sumBytes(retired)
|
|
85
|
+
}));
|
|
86
|
+
const retired = rows.some((row) => row.retiredFiles > 0);
|
|
87
|
+
const lines = [
|
|
88
|
+
...textLines(`Store / ${displayPath(store.dataDir)}`, options, "accent"),
|
|
89
|
+
...textLines(`Disk ${formatMetric("bytes", disk.bytes)} ${disk.files} files`, options),
|
|
90
|
+
"",
|
|
91
|
+
...rows.length ? renderTable(rows, [
|
|
92
|
+
{
|
|
93
|
+
key: "table",
|
|
94
|
+
label: "Table"
|
|
95
|
+
},
|
|
96
|
+
...columnsFor(rows, ["liveFiles", "liveRows"]),
|
|
97
|
+
barColumn(rows, "liveBytes", options),
|
|
98
|
+
...retired ? [{
|
|
99
|
+
key: "retiredFiles",
|
|
100
|
+
label: "Retired files",
|
|
101
|
+
numeric: true
|
|
102
|
+
}, {
|
|
103
|
+
key: "retiredBytes",
|
|
104
|
+
label: "Retired bytes",
|
|
105
|
+
numeric: true,
|
|
106
|
+
format: (value) => formatMetric("bytes", value)
|
|
107
|
+
}] : []
|
|
108
|
+
], options) : textLines("Empty Store.", options)
|
|
109
|
+
];
|
|
110
|
+
if (rows.length > 1) lines.push(...textLines(`Total ${formatMetric("bytes", rows.reduce((sum, row) => sum + row.liveBytes, 0))} live`, options, "muted"));
|
|
111
|
+
if (watermarks.length) lines.push("", ...renderTable(sortWatermarks(watermarks).map((w) => ({
|
|
112
|
+
scope: w.siteId ? `${w.table}@${parseGscSiteUrl(decodeSiteId(w.siteId)).hostname}` : w.table,
|
|
113
|
+
dates: `${w.oldestDateSynced} to ${w.newestDateSynced}`,
|
|
114
|
+
synced: formatAge(w.lastSyncAt)
|
|
115
|
+
})), [
|
|
116
|
+
{
|
|
117
|
+
key: "scope",
|
|
118
|
+
label: ""
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
key: "dates",
|
|
122
|
+
label: "Dates"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
key: "synced",
|
|
126
|
+
label: "Synced"
|
|
95
127
|
}
|
|
96
|
-
|
|
97
|
-
console.log();
|
|
128
|
+
], options));
|
|
129
|
+
console.log(lines.join("\n"));
|
|
98
130
|
}
|
|
99
131
|
});
|
|
100
132
|
function sortWatermarks(ws) {
|
|
@@ -109,10 +141,4 @@ function sumRows(entries) {
|
|
|
109
141
|
function sumBytes(entries) {
|
|
110
142
|
return entries.reduce((acc, e) => acc + e.bytes, 0);
|
|
111
143
|
}
|
|
112
|
-
function formatBytes(n) {
|
|
113
|
-
if (n < 1024) return `${n} B`;
|
|
114
|
-
if (n < 1048576) return `${(n / 1024).toFixed(1)} KB`;
|
|
115
|
-
if (n < 1073741824) return `${(n / 1024 / 1024).toFixed(1)} MB`;
|
|
116
|
-
return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;
|
|
117
|
-
}
|
|
118
144
|
export { statsCommand };
|