@gscdump/cli 7.11.1 → 7.12.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/dist/agent-outcome.mjs +38 -0
- package/dist/bing-hosted.mjs +4 -1
- package/dist/cli.mjs +12 -0
- package/dist/command-meta.mjs +5 -1
- package/dist/command-registry.mjs +3 -2
- package/dist/commands/auth.mjs +1 -0
- package/dist/commands/papercut.mjs +51 -6
- package/dist/commands/profile-selection.mjs +1 -1
- package/dist/commands/report.mjs +24 -1
- package/dist/commands/skill.mjs +34 -3
- package/dist/commands/telemetry.mjs +142 -0
- package/dist/config.mjs +1 -1
- package/dist/dump-writers.mjs +1 -1
- package/dist/hosted-context.mjs +13 -0
- package/dist/hosted-report.mjs +40 -0
- package/dist/hosted-site.mjs +3 -1
- package/dist/package.mjs +1 -1
- package/dist/papercut.mjs +5 -15
- package/dist/runtime.mjs +1 -1
- package/dist/skill.mjs +78 -7
- package/dist/utils.mjs +1 -1
- package/package.json +7 -7
- package/skills/gscdump/SKILL.md +41 -2
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { agentOutcomeReceiptSchema } from "@gscdump/contracts";
|
|
2
|
+
async function submitAgentOutcome(body, deps) {
|
|
3
|
+
const response = await deps.fetch(deps.url, {
|
|
4
|
+
method: "POST",
|
|
5
|
+
redirect: "error",
|
|
6
|
+
headers: {
|
|
7
|
+
"content-type": "application/json",
|
|
8
|
+
...deps.credential ? { authorization: `Bearer ${deps.credential}` } : {}
|
|
9
|
+
},
|
|
10
|
+
body: JSON.stringify(body),
|
|
11
|
+
signal: AbortSignal.timeout(1e4)
|
|
12
|
+
}).catch((cause) => ({
|
|
13
|
+
_tag: "FetchFailed",
|
|
14
|
+
cause
|
|
15
|
+
}));
|
|
16
|
+
if ("_tag" in response) return {
|
|
17
|
+
_tag: "Err",
|
|
18
|
+
reason: "unreachable",
|
|
19
|
+
message: "Could not reach the outcome endpoint. Do not retry an uncertain submission."
|
|
20
|
+
};
|
|
21
|
+
if (!response.ok) return {
|
|
22
|
+
_tag: "Err",
|
|
23
|
+
reason: "rejected",
|
|
24
|
+
message: `Outcome reporting returned HTTP ${response.status}.`
|
|
25
|
+
};
|
|
26
|
+
const data = await response.json().catch((cause) => ({ invalidResponse: cause instanceof Error ? cause.message : String(cause) }));
|
|
27
|
+
const parsed = agentOutcomeReceiptSchema.safeParse(data);
|
|
28
|
+
if (!parsed.success) return {
|
|
29
|
+
_tag: "Err",
|
|
30
|
+
reason: "rejected",
|
|
31
|
+
message: "The outcome endpoint returned an invalid receipt."
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
_tag: "Ok",
|
|
35
|
+
receipt: parsed.data
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export { submitAgentOutcome };
|
package/dist/bing-hosted.mjs
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
+
import { useCliRuntime } from "./runtime.mjs";
|
|
1
2
|
import { stopError } from "./stop.mjs";
|
|
2
3
|
import { getHostedAccount, hostedCredential, siteManagerOf } from "./auth-state.mjs";
|
|
3
4
|
import { logger } from "./utils.mjs";
|
|
4
5
|
import { writeBingDump } from "./bing-data.mjs";
|
|
6
|
+
import { hostedRequestContext } from "./hosted-context.mjs";
|
|
5
7
|
import { noSitesStop } from "./hosted-site.mjs";
|
|
6
8
|
import { createGscdumpV1Client } from "@gscdump/sdk/v1";
|
|
7
9
|
const BING_SITES_COMMAND = "gscdump bing sites --json";
|
|
8
10
|
function hostedBingClient(state) {
|
|
9
11
|
return createGscdumpV1Client({
|
|
10
12
|
apiRoot: state.apiRoot,
|
|
11
|
-
credential: hostedCredential(state)
|
|
13
|
+
credential: hostedCredential(state),
|
|
14
|
+
requestContext: hostedRequestContext(useCliRuntime().environment)
|
|
12
15
|
});
|
|
13
16
|
}
|
|
14
17
|
async function listHostedBingSites(state) {
|
package/dist/cli.mjs
CHANGED
|
@@ -7,7 +7,9 @@ import { resolveCliEnvironment } from "./environment.mjs";
|
|
|
7
7
|
import { applyProfileFromCli } from "./commands/profile-selection.mjs";
|
|
8
8
|
import { reportCliError } from "./error-handler.mjs";
|
|
9
9
|
import { resolveOutputOptions, terminalOutputOptions } from "./render/terminal.mjs";
|
|
10
|
+
import { SKILL_AGENTS, inspectSkillInstallation } from "./skill.mjs";
|
|
10
11
|
import { VERSION, setNoColor, showSplash, withConfiguredOutput } from "./utils.mjs";
|
|
12
|
+
import os from "node:os";
|
|
11
13
|
import process from "node:process";
|
|
12
14
|
import { defineCommand, renderUsage, runCommand } from "citty";
|
|
13
15
|
function shouldShowSplash(rawArgs) {
|
|
@@ -139,6 +141,16 @@ async function runCli(opts = {}) {
|
|
|
139
141
|
runtime.jsonOutput = asksForJson(rawArgs);
|
|
140
142
|
const argumentError = await checkCliArgs(main, rawArgs);
|
|
141
143
|
if (argumentError) throw commandLineError(argumentError);
|
|
144
|
+
if (rawArgs[0] !== "skill" && !rawArgs.some((arg) => HELP_FLAGS.has(arg) || arg === "--version") && runtime.environment.GSCDUMP_NO_SKILL_CHECK !== "1") {
|
|
145
|
+
const states = await Promise.all(SKILL_AGENTS.map((agent) => inspectSkillInstallation({
|
|
146
|
+
agent,
|
|
147
|
+
homeDirectory: os.homedir()
|
|
148
|
+
})));
|
|
149
|
+
for (const state of states) {
|
|
150
|
+
if (state.state === "stale") runtime.logger.warn(`The ${state.agent} skill differs from this CLI. Run gscdump skill install --agent ${state.agent}, then re-read SKILL.md.`);
|
|
151
|
+
if (state.state === "unavailable") runtime.logger.warn(`Could not check the ${state.agent} skill: ${state.message}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
142
154
|
await withConfiguredOutput(() => runMainCommand(rawArgs));
|
|
143
155
|
return 0;
|
|
144
156
|
} catch (error) {
|
package/dist/command-meta.mjs
CHANGED
|
@@ -88,8 +88,12 @@ const papercutCommandMeta = {
|
|
|
88
88
|
name: "papercut",
|
|
89
89
|
description: "Report a CLI problem to gscdump.com (anonymous, sanitized)"
|
|
90
90
|
};
|
|
91
|
+
const telemetryCommandMeta = {
|
|
92
|
+
name: "telemetry",
|
|
93
|
+
description: "Record an explicit agent task outcome"
|
|
94
|
+
};
|
|
91
95
|
const bingCommandMeta = {
|
|
92
96
|
name: "bing",
|
|
93
97
|
description: "Authenticate Bing, list sites, dump data, and read Indexing Evidence"
|
|
94
98
|
};
|
|
95
|
-
export { analyzeCommandMeta, authCommandMeta, bingCommandMeta, configCommandMeta, doctorCommandMeta, dumpCommandMeta, entitiesCommandMeta, indexingCommandMeta, initCommandMeta, inspectCommandMeta, mcpCommandMeta, papercutCommandMeta, profileCommandMeta, queryCommandMeta, reportCommandMeta, sitemapsCommandMeta, sitesCommandMeta, skillCommandMeta, storeCommandMeta, syncCommandMeta };
|
|
99
|
+
export { analyzeCommandMeta, authCommandMeta, bingCommandMeta, configCommandMeta, doctorCommandMeta, dumpCommandMeta, entitiesCommandMeta, indexingCommandMeta, initCommandMeta, inspectCommandMeta, mcpCommandMeta, papercutCommandMeta, profileCommandMeta, queryCommandMeta, reportCommandMeta, sitemapsCommandMeta, sitesCommandMeta, skillCommandMeta, storeCommandMeta, syncCommandMeta, telemetryCommandMeta };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { analyzeCommandMeta, authCommandMeta, bingCommandMeta, configCommandMeta, doctorCommandMeta, dumpCommandMeta, entitiesCommandMeta, indexingCommandMeta, initCommandMeta, inspectCommandMeta, mcpCommandMeta, papercutCommandMeta, profileCommandMeta, queryCommandMeta, reportCommandMeta, sitemapsCommandMeta, sitesCommandMeta, skillCommandMeta, storeCommandMeta, syncCommandMeta } from "./command-meta.mjs";
|
|
1
|
+
import { analyzeCommandMeta, authCommandMeta, bingCommandMeta, configCommandMeta, doctorCommandMeta, dumpCommandMeta, entitiesCommandMeta, indexingCommandMeta, initCommandMeta, inspectCommandMeta, mcpCommandMeta, papercutCommandMeta, profileCommandMeta, queryCommandMeta, reportCommandMeta, sitemapsCommandMeta, sitesCommandMeta, skillCommandMeta, storeCommandMeta, syncCommandMeta, telemetryCommandMeta } from "./command-meta.mjs";
|
|
2
2
|
function resolveValue(value) {
|
|
3
3
|
return typeof value === "function" ? value() : value;
|
|
4
4
|
}
|
|
@@ -130,6 +130,7 @@ const CLI_SUBCOMMANDS = {
|
|
|
130
130
|
doctor: shallowCommand(doctorCommandMeta, () => import("./commands/doctor.mjs").then((module) => module.doctorCommand)),
|
|
131
131
|
mcp: shallowCommand(mcpCommandMeta, () => import("./commands/mcp.mjs").then((module) => module.mcpCommand)),
|
|
132
132
|
skill: shallowCommand(skillCommandMeta, () => import("./commands/skill.mjs").then((module) => module.skillCommand)),
|
|
133
|
-
papercut: shallowCommand(papercutCommandMeta, () => import("./commands/papercut.mjs").then((module) => module.papercutCommand))
|
|
133
|
+
papercut: shallowCommand(papercutCommandMeta, () => import("./commands/papercut.mjs").then((module) => module.papercutCommand)),
|
|
134
|
+
telemetry: shallowCommand(telemetryCommandMeta, () => import("./commands/telemetry.mjs").then((module) => module.telemetryCommand))
|
|
134
135
|
};
|
|
135
136
|
export { CLI_SUBCOMMANDS, findSubCommandIndex, isUsageError, resolveUsageTarget, usageError };
|
package/dist/commands/auth.mjs
CHANGED
|
@@ -8,24 +8,60 @@ import { confirm, isCancel } from "@clack/prompts";
|
|
|
8
8
|
const papercutCommand = defineCommand({
|
|
9
9
|
meta: papercutCommandMeta,
|
|
10
10
|
args: {
|
|
11
|
-
command: {
|
|
11
|
+
"command": {
|
|
12
12
|
type: "string",
|
|
13
13
|
description: "The gscdump command that misbehaved, for example \"report triage\""
|
|
14
14
|
},
|
|
15
|
-
comment: {
|
|
15
|
+
"comment": {
|
|
16
16
|
type: "string",
|
|
17
17
|
description: "What you ran, what you expected, what happened, any workaround (sanitized)"
|
|
18
18
|
},
|
|
19
|
-
agent: {
|
|
19
|
+
"agent": {
|
|
20
20
|
type: "string",
|
|
21
21
|
description: "Reporter name, for example \"Claude Code\" or a person's handle"
|
|
22
22
|
},
|
|
23
|
-
intent: {
|
|
23
|
+
"intent": {
|
|
24
24
|
type: "string",
|
|
25
25
|
description: "bug (default) or improvement",
|
|
26
26
|
default: "bug"
|
|
27
27
|
},
|
|
28
|
-
|
|
28
|
+
"request-id": {
|
|
29
|
+
type: "string",
|
|
30
|
+
description: "Request ID returned by the failed operation"
|
|
31
|
+
},
|
|
32
|
+
"parent-request-id": {
|
|
33
|
+
type: "string",
|
|
34
|
+
description: "Request ID returned by the calling application"
|
|
35
|
+
},
|
|
36
|
+
"workflow-id": {
|
|
37
|
+
type: "string",
|
|
38
|
+
description: "Opaque task identifier without customer content"
|
|
39
|
+
},
|
|
40
|
+
"origin": {
|
|
41
|
+
type: "string",
|
|
42
|
+
description: "Application host, for example requestindexing.com"
|
|
43
|
+
},
|
|
44
|
+
"product": {
|
|
45
|
+
type: "string",
|
|
46
|
+
description: "Application identifier"
|
|
47
|
+
},
|
|
48
|
+
"surface": {
|
|
49
|
+
type: "string",
|
|
50
|
+
description: "cli, mcp, api, skill, or browser"
|
|
51
|
+
},
|
|
52
|
+
"operation": {
|
|
53
|
+
type: "string",
|
|
54
|
+
description: "Operation identifier"
|
|
55
|
+
},
|
|
56
|
+
"skill-digest": {
|
|
57
|
+
type: "string",
|
|
58
|
+
description: "SHA-256 digest of the skill artifact"
|
|
59
|
+
},
|
|
60
|
+
"skill-version": {
|
|
61
|
+
type: "string",
|
|
62
|
+
description: "Declared skill version"
|
|
63
|
+
},
|
|
64
|
+
"yes": {
|
|
29
65
|
type: "boolean",
|
|
30
66
|
alias: "y",
|
|
31
67
|
default: false,
|
|
@@ -39,7 +75,16 @@ const papercutCommand = defineCommand({
|
|
|
39
75
|
command: args.command,
|
|
40
76
|
comment: args.comment,
|
|
41
77
|
agent: args.agent,
|
|
42
|
-
intent: args.intent
|
|
78
|
+
intent: args.intent,
|
|
79
|
+
requestId: args["request-id"],
|
|
80
|
+
parentRequestId: args["parent-request-id"],
|
|
81
|
+
workflowId: args["workflow-id"],
|
|
82
|
+
origin: args.origin,
|
|
83
|
+
product: args.product,
|
|
84
|
+
surface: args.surface,
|
|
85
|
+
operation: args.operation,
|
|
86
|
+
skillDigest: args["skill-digest"],
|
|
87
|
+
skillVersion: args["skill-version"]
|
|
43
88
|
});
|
|
44
89
|
if (built._tag === "Err") {
|
|
45
90
|
logger.error(`Invalid papercut: ${built.message}`);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useCliRuntime } from "../runtime.mjs";
|
|
2
2
|
import { setConfigDir } from "../config.mjs";
|
|
3
3
|
import { resolveCliEnvironment } from "../environment.mjs";
|
|
4
|
-
import path from "node:path";
|
|
5
4
|
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
6
|
import fs from "node:fs";
|
|
7
7
|
const ROOT_DIR = path.join(os.homedir(), ".config", "gscdump");
|
|
8
8
|
const PROFILES_DIR = path.join(ROOT_DIR, "profiles");
|
package/dist/commands/report.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { resolveAuthentication } from "../auth-state.mjs";
|
|
1
2
|
import { reportCommandMeta } from "../command-meta.mjs";
|
|
2
3
|
import { terminalOutputOptions } from "../render/terminal.mjs";
|
|
3
4
|
import { logger, parseFetchBudget, setJsonOutput } from "../utils.mjs";
|
|
4
5
|
import { COMPARISON_FLAGS, PERIOD_FLAGS, parseWindowFlags, windowFlagErrorToException } from "../window.mjs";
|
|
5
6
|
import { analysisNeeds, analyzerSources, analyzerTables, resolveAnalysisSource } from "../analysis-local.mjs";
|
|
7
|
+
import { runHostedReport } from "../hosted-report.mjs";
|
|
6
8
|
import { renderCliReport } from "../render/report.mjs";
|
|
7
9
|
import { defineCommand } from "citty";
|
|
8
10
|
import { getLatestGscDate } from "gscdump/dates";
|
|
@@ -109,10 +111,15 @@ function makeReportCommand(report) {
|
|
|
109
111
|
default: false,
|
|
110
112
|
description: "Force live GSC API; bypass local store"
|
|
111
113
|
},
|
|
114
|
+
"force": {
|
|
115
|
+
type: "boolean",
|
|
116
|
+
default: false,
|
|
117
|
+
description: "Regenerate a Hosted report instead of reading its cache"
|
|
118
|
+
},
|
|
112
119
|
"json": {
|
|
113
120
|
type: "boolean",
|
|
114
121
|
default: false,
|
|
115
|
-
description: "Emit
|
|
122
|
+
description: "Emit report JSON with evidence coverage"
|
|
116
123
|
},
|
|
117
124
|
"explain": {
|
|
118
125
|
type: "boolean",
|
|
@@ -147,6 +154,22 @@ function makeReportCommand(report) {
|
|
|
147
154
|
}, null, 2));
|
|
148
155
|
return;
|
|
149
156
|
}
|
|
157
|
+
if (!args.live && (await resolveAuthentication())._tag === "Hosted") {
|
|
158
|
+
const result = await runHostedReport(report.id, args);
|
|
159
|
+
if (args.json) {
|
|
160
|
+
console.log(JSON.stringify(result, null, 2));
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
console.log(`${report.id}: ${result.site}\n${result.window.start} to ${result.window.end}`);
|
|
164
|
+
for (const section of result.sections) {
|
|
165
|
+
console.log(`\n${section.title}${section.dataUnavailable ? " (unavailable)" : ""}`);
|
|
166
|
+
for (const finding of section.findings) console.log(`${finding.entity.value}: ${Object.entries(finding.metrics).map(([key, value]) => `${key}=${value}`).join(", ")}`);
|
|
167
|
+
if (section.truncated) logger.warn(`${section.id}: ${section.truncated.kept} of ${section.truncated.total} findings returned.`);
|
|
168
|
+
}
|
|
169
|
+
if (result.meta.degraded) logger.warn("Report evidence is incomplete. Read section coverage before drawing conclusions.");
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (args.force) throw new Error("--force regenerates a Hosted report. Select Hosted mode to use it.");
|
|
150
173
|
const steps = (window) => report.plan(params, window).map((step) => ({
|
|
151
174
|
type: step.type,
|
|
152
175
|
params: {
|
package/dist/commands/skill.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { skillCommandMeta } from "../command-meta.mjs";
|
|
2
|
+
import { SKILL_AGENTS, SKILL_NAME, inspectSkillInstallation, installSkill } from "../skill.mjs";
|
|
2
3
|
import { OUTPUT_ARGS, applyOutputMode, displayPath, logger } from "../utils.mjs";
|
|
3
|
-
import
|
|
4
|
+
import os from "node:os";
|
|
4
5
|
import process from "node:process";
|
|
5
6
|
import { defineCommand } from "citty";
|
|
6
|
-
import os from "node:os";
|
|
7
7
|
const installCommand = defineCommand({
|
|
8
8
|
meta: {
|
|
9
9
|
name: "install",
|
|
@@ -44,6 +44,37 @@ const installCommand = defineCommand({
|
|
|
44
44
|
});
|
|
45
45
|
const skillCommand = defineCommand({
|
|
46
46
|
meta: skillCommandMeta,
|
|
47
|
-
subCommands: {
|
|
47
|
+
subCommands: {
|
|
48
|
+
install: installCommand,
|
|
49
|
+
status: defineCommand({
|
|
50
|
+
meta: {
|
|
51
|
+
name: "status",
|
|
52
|
+
description: "Compare installed skill content with the packaged artifact"
|
|
53
|
+
},
|
|
54
|
+
args: {
|
|
55
|
+
agent: {
|
|
56
|
+
type: "string",
|
|
57
|
+
default: "claude",
|
|
58
|
+
description: `Agent skill directory: ${SKILL_AGENTS.join(" or ")}`
|
|
59
|
+
},
|
|
60
|
+
target: {
|
|
61
|
+
type: "string",
|
|
62
|
+
description: "Inspect this directory instead of the agent home directory"
|
|
63
|
+
},
|
|
64
|
+
...OUTPUT_ARGS
|
|
65
|
+
},
|
|
66
|
+
async run({ args }) {
|
|
67
|
+
applyOutputMode(args);
|
|
68
|
+
const agent = SKILL_AGENTS.find((name) => name === args.agent);
|
|
69
|
+
if (!agent) throw new Error(`Unknown agent "${args.agent}". Use ${SKILL_AGENTS.join(" or ")}.`);
|
|
70
|
+
const result = await inspectSkillInstallation({
|
|
71
|
+
agent,
|
|
72
|
+
homeDirectory: os.homedir(),
|
|
73
|
+
target: args.target
|
|
74
|
+
});
|
|
75
|
+
console.log(JSON.stringify(result));
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
}
|
|
48
79
|
});
|
|
49
80
|
export { skillCommand };
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { useCliRuntime } from "../runtime.mjs";
|
|
2
|
+
import { hostedCredential, resolveAuthentication } from "../auth-state.mjs";
|
|
3
|
+
import { OUTPUT_ARGS, VERSION, applyOutputMode } from "../utils.mjs";
|
|
4
|
+
import { resolvePapercutUrl } from "../papercut.mjs";
|
|
5
|
+
import { submitAgentOutcome } from "../agent-outcome.mjs";
|
|
6
|
+
import process from "node:process";
|
|
7
|
+
import { defineCommand } from "citty";
|
|
8
|
+
import { randomUUID } from "node:crypto";
|
|
9
|
+
import { agentOutcomeBodySchema } from "@gscdump/contracts";
|
|
10
|
+
import { confirm, isCancel } from "@clack/prompts";
|
|
11
|
+
const telemetryCommand = defineCommand({
|
|
12
|
+
meta: {
|
|
13
|
+
name: "telemetry",
|
|
14
|
+
description: "Record an explicit agent task outcome"
|
|
15
|
+
},
|
|
16
|
+
subCommands: { record: defineCommand({
|
|
17
|
+
meta: {
|
|
18
|
+
name: "record",
|
|
19
|
+
description: "Submit bounded task metadata without prompts or output bodies"
|
|
20
|
+
},
|
|
21
|
+
args: {
|
|
22
|
+
"event-id": {
|
|
23
|
+
type: "string",
|
|
24
|
+
description: "Stable event ID; defaults to a new UUID"
|
|
25
|
+
},
|
|
26
|
+
"workflow-id": {
|
|
27
|
+
type: "string",
|
|
28
|
+
required: true,
|
|
29
|
+
description: "Opaque task identifier"
|
|
30
|
+
},
|
|
31
|
+
"request-id": {
|
|
32
|
+
type: "string",
|
|
33
|
+
description: "Related server request ID"
|
|
34
|
+
},
|
|
35
|
+
"parent-request-id": {
|
|
36
|
+
type: "string",
|
|
37
|
+
description: "Related consumer request ID"
|
|
38
|
+
},
|
|
39
|
+
"product": {
|
|
40
|
+
type: "string",
|
|
41
|
+
default: "gscdump",
|
|
42
|
+
description: "Declared originating application"
|
|
43
|
+
},
|
|
44
|
+
"surface": {
|
|
45
|
+
type: "string",
|
|
46
|
+
default: "cli",
|
|
47
|
+
description: "cli, mcp, api, skill, or browser"
|
|
48
|
+
},
|
|
49
|
+
"operation": {
|
|
50
|
+
type: "string",
|
|
51
|
+
required: true,
|
|
52
|
+
description: "Operation identifier without arguments"
|
|
53
|
+
},
|
|
54
|
+
"step": {
|
|
55
|
+
type: "string",
|
|
56
|
+
default: "complete",
|
|
57
|
+
description: "setup, read, diagnose, fix, verify, or complete"
|
|
58
|
+
},
|
|
59
|
+
"outcome": {
|
|
60
|
+
type: "string",
|
|
61
|
+
required: true,
|
|
62
|
+
description: "success, failure, blocked, or cancelled"
|
|
63
|
+
},
|
|
64
|
+
"semantic-flags": {
|
|
65
|
+
type: "string",
|
|
66
|
+
description: "Comma-separated empty, unavailable, refused, provisional, partial, stale, truncated"
|
|
67
|
+
},
|
|
68
|
+
"row-count": {
|
|
69
|
+
type: "string",
|
|
70
|
+
description: "Measured domain row count"
|
|
71
|
+
},
|
|
72
|
+
"duration-ms": {
|
|
73
|
+
type: "string",
|
|
74
|
+
description: "Measured task duration"
|
|
75
|
+
},
|
|
76
|
+
"quality": {
|
|
77
|
+
type: "string",
|
|
78
|
+
default: "unknown",
|
|
79
|
+
description: "Declared useful, unusable, incorrect, or unknown"
|
|
80
|
+
},
|
|
81
|
+
"error-code": {
|
|
82
|
+
type: "string",
|
|
83
|
+
description: "Stable error code"
|
|
84
|
+
},
|
|
85
|
+
"skill-digest": {
|
|
86
|
+
type: "string",
|
|
87
|
+
description: "SHA-256 digest of the skill artifact"
|
|
88
|
+
},
|
|
89
|
+
"skill-version": {
|
|
90
|
+
type: "string",
|
|
91
|
+
description: "Declared skill version"
|
|
92
|
+
},
|
|
93
|
+
"yes": {
|
|
94
|
+
type: "boolean",
|
|
95
|
+
alias: "y",
|
|
96
|
+
default: false,
|
|
97
|
+
description: "Submit without confirmation"
|
|
98
|
+
},
|
|
99
|
+
...OUTPUT_ARGS
|
|
100
|
+
},
|
|
101
|
+
async run({ args }) {
|
|
102
|
+
const { json } = applyOutputMode(args);
|
|
103
|
+
const body = agentOutcomeBodySchema.parse({
|
|
104
|
+
eventId: args["event-id"] || randomUUID(),
|
|
105
|
+
workflowId: args["workflow-id"],
|
|
106
|
+
requestId: args["request-id"],
|
|
107
|
+
parentRequestId: args["parent-request-id"],
|
|
108
|
+
product: args.product,
|
|
109
|
+
surface: args.surface,
|
|
110
|
+
operation: args.operation,
|
|
111
|
+
step: args.step,
|
|
112
|
+
outcome: args.outcome,
|
|
113
|
+
semanticFlags: args["semantic-flags"] ? args["semantic-flags"].split(",").map((flag) => flag.trim()) : [],
|
|
114
|
+
rowCount: args["row-count"] !== void 0 ? Number(args["row-count"]) : void 0,
|
|
115
|
+
durationMs: args["duration-ms"] !== void 0 ? Number(args["duration-ms"]) : void 0,
|
|
116
|
+
quality: args.quality,
|
|
117
|
+
errorCode: args["error-code"],
|
|
118
|
+
skillDigest: args["skill-digest"],
|
|
119
|
+
skillVersion: args["skill-version"],
|
|
120
|
+
cliVersion: VERSION
|
|
121
|
+
});
|
|
122
|
+
if (!args.yes) {
|
|
123
|
+
if (!process.stdin.isTTY || json) throw new Error("Pass --yes to submit this declared task outcome.");
|
|
124
|
+
const accepted = await confirm({ message: "Submit this declared agent task outcome?" });
|
|
125
|
+
if (isCancel(accepted) || !accepted) return;
|
|
126
|
+
}
|
|
127
|
+
const runtime = useCliRuntime();
|
|
128
|
+
const url = resolvePapercutUrl(runtime.environment).replace(/\/papercuts$/, "/agent-outcomes");
|
|
129
|
+
const authentication = await resolveAuthentication();
|
|
130
|
+
if (authentication._tag === "Hosted" && new URL(url).origin !== new URL(authentication.apiRoot).origin) throw new Error("The outcome endpoint does not match the Hosted credential origin.");
|
|
131
|
+
const credential = authentication._tag === "Hosted" ? hostedCredential(authentication) : void 0;
|
|
132
|
+
const result = await submitAgentOutcome(body, {
|
|
133
|
+
fetch: globalThis.fetch,
|
|
134
|
+
url,
|
|
135
|
+
credential
|
|
136
|
+
});
|
|
137
|
+
if (result._tag === "Err") throw new Error(result.message);
|
|
138
|
+
console.log(JSON.stringify(result.receipt));
|
|
139
|
+
}
|
|
140
|
+
}) }
|
|
141
|
+
});
|
|
142
|
+
export { telemetryCommand };
|
package/dist/config.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useCliRuntime } from "./runtime.mjs";
|
|
2
|
+
import os from "node:os";
|
|
2
3
|
import fs from "node:fs/promises";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { z } from "zod";
|
|
5
|
-
import os from "node:os";
|
|
6
6
|
import { SearchTypes } from "gscdump/query";
|
|
7
7
|
function setConfigDir(dir) {
|
|
8
8
|
useCliRuntime().configDir = dir;
|
package/dist/dump-writers.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { exportColumns, readParquetSql, sourceSelectSql, sqlString } from "./table-sources.mjs";
|
|
2
|
+
import os from "node:os";
|
|
2
3
|
import { createHash } from "node:crypto";
|
|
3
4
|
import fs from "node:fs/promises";
|
|
4
5
|
import path from "node:path";
|
|
5
|
-
import os from "node:os";
|
|
6
6
|
import { DuckDBInstance } from "@duckdb/node-api";
|
|
7
7
|
import { encodeRowsToParquetFlex } from "@gscdump/engine/hyparquet";
|
|
8
8
|
const DUMP_FORMATS = [
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { VERSION } from "./utils.mjs";
|
|
2
|
+
import { agentRequestContextSchema } from "@gscdump/contracts";
|
|
3
|
+
function hostedRequestContext(environment) {
|
|
4
|
+
return agentRequestContextSchema.parse({
|
|
5
|
+
cliVersion: VERSION,
|
|
6
|
+
client: "gscdump-cli",
|
|
7
|
+
product: environment.GSCDUMP_PRODUCT ?? "gscdump",
|
|
8
|
+
workflowId: environment.GSCDUMP_WORKFLOW_ID,
|
|
9
|
+
parentRequestId: environment.GSCDUMP_PARENT_REQUEST_ID,
|
|
10
|
+
skillDigest: environment.GSCDUMP_SKILL_DIGEST
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export { hostedRequestContext };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { resolveHostedSite } from "./hosted-site.mjs";
|
|
2
|
+
import { runReportV1RequestSchema } from "@gscdump/contracts/v1";
|
|
3
|
+
function hostedReportRequest(reportId, args) {
|
|
4
|
+
if (args["prev-start"] || args["prev-end"] || args["fetch-budget"]) throw new Error("Hosted reports do not accept --prev-start, --prev-end, or --fetch-budget. Use --vs for comparison.");
|
|
5
|
+
const period = args.period ? String(args.period).replace(/^(\d+d)$/, "last-$1") : void 0;
|
|
6
|
+
return runReportV1RequestSchema.parse({
|
|
7
|
+
reportId,
|
|
8
|
+
period,
|
|
9
|
+
comparison: args.vs,
|
|
10
|
+
start: args.start,
|
|
11
|
+
end: args.end,
|
|
12
|
+
target: args.target,
|
|
13
|
+
targetKind: args["target-kind"],
|
|
14
|
+
brandTerms: args["brand-terms"],
|
|
15
|
+
topic: args.topic,
|
|
16
|
+
maxFindings: args["max-findings"] !== void 0 ? Number(args["max-findings"]) : void 0,
|
|
17
|
+
minClicksChange: args["min-clicks-change"] !== void 0 ? Number(args["min-clicks-change"]) : void 0,
|
|
18
|
+
force: Boolean(args.force)
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async function runHostedReport(reportId, args) {
|
|
22
|
+
const body = hostedReportRequest(reportId, args);
|
|
23
|
+
const { client, site } = await resolveHostedSite(args, {
|
|
24
|
+
name: `report ${reportId}`,
|
|
25
|
+
localAlternative: "run the report against your Store"
|
|
26
|
+
});
|
|
27
|
+
const response = await client.runReport({
|
|
28
|
+
params: { siteId: site.siteId },
|
|
29
|
+
body
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
...response.data,
|
|
33
|
+
meta: {
|
|
34
|
+
...response.data.meta,
|
|
35
|
+
requestId: response.meta.requestId,
|
|
36
|
+
source: "hosted"
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export { hostedReportRequest, runHostedReport };
|
package/dist/hosted-site.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { stopError } from "./stop.mjs";
|
|
|
2
2
|
import { connectSiteStep, getHostedAccount, hostedCredential, parseAuthentication, resolveAuthentication, siteManagerOf } from "./auth-state.mjs";
|
|
3
3
|
import { loadConfig } from "./config.mjs";
|
|
4
4
|
import { resolveCliEnvironment } from "./environment.mjs";
|
|
5
|
+
import { hostedRequestContext } from "./hosted-context.mjs";
|
|
5
6
|
import { createGscdumpV1Client } from "@gscdump/sdk/v1";
|
|
6
7
|
const HOSTED_ARGS = {
|
|
7
8
|
"site": {
|
|
@@ -104,7 +105,8 @@ async function resolveHostedSite(args, command) {
|
|
|
104
105
|
return {
|
|
105
106
|
client: createGscdumpV1Client({
|
|
106
107
|
apiRoot: authentication.apiRoot,
|
|
107
|
-
credential: hostedCredential(authentication)
|
|
108
|
+
credential: hostedCredential(authentication),
|
|
109
|
+
requestContext: hostedRequestContext(environment)
|
|
108
110
|
}),
|
|
109
111
|
site: match.site,
|
|
110
112
|
authentication,
|
package/dist/package.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var version = "7.
|
|
1
|
+
var version = "7.12.0";
|
|
2
2
|
export { version };
|
package/dist/papercut.mjs
CHANGED
|
@@ -1,28 +1,18 @@
|
|
|
1
1
|
import { VERSION } from "./utils.mjs";
|
|
2
2
|
import process from "node:process";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { cliPapercutBodySchema } from "@gscdump/contracts";
|
|
4
5
|
const DEFAULT_PAPERCUT_BASE_URL = "https://gscdump.com";
|
|
5
6
|
const PAPERCUT_PATH = "/api/cli/papercuts";
|
|
6
7
|
const REQUEST_TIMEOUT_MS = 1e4;
|
|
7
|
-
const
|
|
8
|
-
const papercutBodySchema = z.strictObject({
|
|
9
|
-
command: singleLine(200),
|
|
10
|
-
comment: z.string().trim().min(1).max(2e3),
|
|
11
|
-
agent: singleLine(100),
|
|
12
|
-
intent: z.enum(["bug", "improvement"]).default("bug"),
|
|
13
|
-
cliVersion: singleLine(64),
|
|
14
|
-
node: singleLine(64),
|
|
15
|
-
platform: singleLine(32)
|
|
16
|
-
});
|
|
8
|
+
const papercutBodySchema = cliPapercutBodySchema;
|
|
17
9
|
const receiptSchema = z.object({
|
|
18
10
|
id: z.string().min(1),
|
|
19
11
|
status: z.literal("new")
|
|
20
12
|
});
|
|
21
13
|
function buildPapercutBody(input) {
|
|
22
14
|
const parsed = papercutBodySchema.safeParse({
|
|
23
|
-
|
|
24
|
-
comment: input.comment,
|
|
25
|
-
agent: input.agent,
|
|
15
|
+
...input,
|
|
26
16
|
intent: input.intent ?? "bug",
|
|
27
17
|
cliVersion: VERSION,
|
|
28
18
|
node: process.versions.node,
|
|
@@ -77,14 +67,14 @@ async function submitPapercut(body, deps) {
|
|
|
77
67
|
if (response.status === 400 || response.status === 422) return {
|
|
78
68
|
_tag: "Err",
|
|
79
69
|
reason: "invalid_input",
|
|
80
|
-
message: `gscdump.com rejected the papercut: ${(await response.text()
|
|
70
|
+
message: `gscdump.com rejected the papercut: ${(await response.text()).slice(0, 300) || response.status}`
|
|
81
71
|
};
|
|
82
72
|
if (!response.ok) return {
|
|
83
73
|
_tag: "Err",
|
|
84
74
|
reason: "rejected",
|
|
85
75
|
message: `gscdump.com answered ${response.status}.`
|
|
86
76
|
};
|
|
87
|
-
const json = await response.json().catch(() =>
|
|
77
|
+
const json = await response.json().catch((cause) => ({ invalidResponse: cause instanceof Error ? cause.message : String(cause) }));
|
|
88
78
|
const receipt = receiptSchema.safeParse(json);
|
|
89
79
|
if (!receipt.success) return {
|
|
90
80
|
_tag: "Err",
|
package/dist/runtime.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import os from "node:os";
|
|
1
2
|
import process from "node:process";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4
|
-
import os from "node:os";
|
|
5
5
|
import { createConsola } from "consola";
|
|
6
6
|
let runtimeStorage;
|
|
7
7
|
let fallbackRuntime;
|
package/dist/skill.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { cp, mkdir, mkdtemp, readFile, readdir, rename, rm, stat } from "node:fs/promises";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { fileURLToPath } from "node:url";
|
|
4
5
|
const SKILL_NAME = "gscdump";
|
|
@@ -13,6 +14,78 @@ function skillSourceDirectory(moduleUrl = import.meta.url) {
|
|
|
13
14
|
function skillDestination(homeDirectory, agent) {
|
|
14
15
|
return path.join(homeDirectory, AGENT_DIRECTORIES[agent], "skills", SKILL_NAME);
|
|
15
16
|
}
|
|
17
|
+
async function skillDigest(directory) {
|
|
18
|
+
const hash = createHash("sha256");
|
|
19
|
+
async function read(relative) {
|
|
20
|
+
const entries = await readdir(path.join(directory, relative), { withFileTypes: true });
|
|
21
|
+
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
22
|
+
for (const entry of entries) {
|
|
23
|
+
if (entry.name.startsWith(".")) continue;
|
|
24
|
+
const name = relative ? `${relative}/${entry.name}` : entry.name;
|
|
25
|
+
if (entry.isDirectory()) await read(name);
|
|
26
|
+
else if (entry.isFile()) hash.update(name).update("\0").update(await readFile(path.join(directory, name))).update("\0");
|
|
27
|
+
else throw new Error(`The skill contains an unsupported file at ${name}.`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
await read("");
|
|
31
|
+
return hash.digest("hex");
|
|
32
|
+
}
|
|
33
|
+
async function inspectSkillInstallation(options) {
|
|
34
|
+
const destination = options.target ? path.join(options.target, SKILL_NAME) : skillDestination(options.homeDirectory, options.agent);
|
|
35
|
+
if (!await stat(destination).then(() => true).catch((cause) => {
|
|
36
|
+
if (cause !== null && typeof cause === "object" && "code" in cause && cause.code === "ENOENT") return false;
|
|
37
|
+
throw cause;
|
|
38
|
+
})) return {
|
|
39
|
+
state: "not-installed",
|
|
40
|
+
agent: options.agent,
|
|
41
|
+
destination
|
|
42
|
+
};
|
|
43
|
+
return Promise.all([skillDigest(destination), skillDigest(options.sourceDirectory ?? skillSourceDirectory())]).then(([installedDigest, packagedDigest]) => ({
|
|
44
|
+
state: installedDigest === packagedDigest ? "current" : "stale",
|
|
45
|
+
agent: options.agent,
|
|
46
|
+
destination,
|
|
47
|
+
installedDigest,
|
|
48
|
+
packagedDigest
|
|
49
|
+
})).catch((cause) => ({
|
|
50
|
+
state: "unavailable",
|
|
51
|
+
agent: options.agent,
|
|
52
|
+
destination,
|
|
53
|
+
message: cause instanceof Error ? cause.message : String(cause)
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
async function replaceSkill(source, destination) {
|
|
57
|
+
await mkdir(path.dirname(destination), { recursive: true });
|
|
58
|
+
const staging = await mkdtemp(path.join(path.dirname(destination), ".gscdump-skill-"));
|
|
59
|
+
const candidate = path.join(staging, "candidate");
|
|
60
|
+
const backup = path.join(staging, "previous");
|
|
61
|
+
const digest = await cp(source, candidate, {
|
|
62
|
+
recursive: true,
|
|
63
|
+
force: true
|
|
64
|
+
}).then(() => skillDigest(candidate)).catch(async (cause) => {
|
|
65
|
+
await rm(staging, {
|
|
66
|
+
recursive: true,
|
|
67
|
+
force: true
|
|
68
|
+
});
|
|
69
|
+
throw cause;
|
|
70
|
+
});
|
|
71
|
+
const previous = await rename(destination, backup).then(() => "moved").catch((cause) => {
|
|
72
|
+
if (cause !== null && typeof cause === "object" && "code" in cause && cause.code === "ENOENT") return "absent";
|
|
73
|
+
throw cause;
|
|
74
|
+
});
|
|
75
|
+
await rename(candidate, destination).catch(async (cause) => {
|
|
76
|
+
if (previous === "moved") await rename(backup, destination);
|
|
77
|
+
await rm(staging, {
|
|
78
|
+
recursive: true,
|
|
79
|
+
force: true
|
|
80
|
+
});
|
|
81
|
+
throw cause;
|
|
82
|
+
});
|
|
83
|
+
await rm(staging, {
|
|
84
|
+
recursive: true,
|
|
85
|
+
force: true
|
|
86
|
+
});
|
|
87
|
+
return digest;
|
|
88
|
+
}
|
|
16
89
|
async function installSkill(options) {
|
|
17
90
|
const source = options.sourceDirectory ?? skillSourceDirectory();
|
|
18
91
|
if (!(await stat(source).catch(() => {
|
|
@@ -23,15 +96,13 @@ async function installSkill(options) {
|
|
|
23
96
|
message: `The packaged skill is missing at ${source}. Reinstall @gscdump/cli, then run the command again.`
|
|
24
97
|
};
|
|
25
98
|
const destination = options.target ? path.join(options.target, SKILL_NAME) : skillDestination(options.homeDirectory, options.agent);
|
|
26
|
-
return
|
|
27
|
-
recursive: true,
|
|
28
|
-
force: true
|
|
29
|
-
})).then(() => ({
|
|
99
|
+
return replaceSkill(source, destination).then((digest) => ({
|
|
30
100
|
_tag: "Ok",
|
|
31
101
|
installation: {
|
|
32
102
|
agent: options.agent,
|
|
33
103
|
source,
|
|
34
|
-
destination
|
|
104
|
+
destination,
|
|
105
|
+
digest
|
|
35
106
|
}
|
|
36
107
|
})).catch((cause) => {
|
|
37
108
|
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
@@ -42,4 +113,4 @@ async function installSkill(options) {
|
|
|
42
113
|
};
|
|
43
114
|
});
|
|
44
115
|
}
|
|
45
|
-
export { SKILL_AGENTS, SKILL_NAME, installSkill, skillDestination, skillSourceDirectory };
|
|
116
|
+
export { SKILL_AGENTS, SKILL_NAME, inspectSkillInstallation, installSkill, skillDestination, skillSourceDirectory };
|
package/dist/utils.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { useCliRuntime } from "./runtime.mjs";
|
|
2
2
|
import { commandLineError } from "./stop.mjs";
|
|
3
3
|
import { version } from "./package.mjs";
|
|
4
|
+
import os from "node:os";
|
|
4
5
|
import process from "node:process";
|
|
5
6
|
import fs from "node:fs/promises";
|
|
6
|
-
import os from "node:os";
|
|
7
7
|
import { SearchTypes } from "gscdump/query";
|
|
8
8
|
import { Buffer } from "node:buffer";
|
|
9
9
|
import { MAX_FETCH_BUDGET } from "@gscdump/engine/analysis-types";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gscdump/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "7.
|
|
4
|
+
"version": "7.12.0",
|
|
5
5
|
"description": "CLI for Google Search Console and Bing with hosted or local authentication, data exports, and an MCP server",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -44,16 +44,16 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@clack/prompts": "^1.8.1",
|
|
46
46
|
"@duckdb/node-api": "1.5.5-r.5",
|
|
47
|
-
"@gscdump/analysis": "^7.
|
|
48
|
-
"@gscdump/contracts": "^7.
|
|
49
|
-
"@gscdump/engine": "^7.
|
|
50
|
-
"@gscdump/engine-gsc-api": "^7.
|
|
51
|
-
"@gscdump/sdk": "^7.
|
|
47
|
+
"@gscdump/analysis": "^7.12.0",
|
|
48
|
+
"@gscdump/contracts": "^7.12.0",
|
|
49
|
+
"@gscdump/engine": "^7.12.0",
|
|
50
|
+
"@gscdump/engine-gsc-api": "^7.12.0",
|
|
51
|
+
"@gscdump/sdk": "^7.12.0",
|
|
52
52
|
"@modelcontextprotocol/sdk": "^1.30.1",
|
|
53
53
|
"citty": "^0.2.2",
|
|
54
54
|
"consola": "^3.4.2",
|
|
55
55
|
"google-auth-library": "^11.1.0",
|
|
56
|
-
"gscdump": "^7.
|
|
56
|
+
"gscdump": "^7.12.0",
|
|
57
57
|
"ofetch": "^1.5.1",
|
|
58
58
|
"open": "^11.0.4",
|
|
59
59
|
"proper-lockfile": "^4.1.2",
|
package/skills/gscdump/SKILL.md
CHANGED
|
@@ -18,6 +18,10 @@ Example: `gscdump query --site=SITE --start=DATE --end=DATE -d page -f json`.
|
|
|
18
18
|
|
|
19
19
|
## Start each task
|
|
20
20
|
|
|
21
|
+
Run `gscdump skill status --agent codex --json` for Codex, or use `--agent claude` for Claude.
|
|
22
|
+
If the result is `stale`, reinstall that agent's skill and re-read it before continuing.
|
|
23
|
+
The check compares the complete artifact, including references. A matching version alone does not prove matching instructions.
|
|
24
|
+
|
|
21
25
|
1. Before reading traffic, run `gscdump auth status --json`. Do this even when the user says authentication works.
|
|
22
26
|
2. Keep the requested Site, dates, dimensions, and task scope. A request for pages does not need query dimensions.
|
|
23
27
|
3. In Hosted mode, skip steps 3 to 5. `gscdump query` reads the hosted record, and `sync` needs Local mode.
|
|
@@ -73,7 +77,7 @@ gscdump sites --json
|
|
|
73
77
|
gscdump query --site example.com -d page -f json
|
|
74
78
|
```
|
|
75
79
|
|
|
76
|
-
Hosted mode can run
|
|
80
|
+
Hosted mode can run these commands: `sites`, `query`, `report`, `sitemaps current`, `sitemaps history`, `sitemaps membership`,
|
|
77
81
|
`sitemaps lastmod`, `sitemaps export`, `sitemaps inspect`, `indexing urls`, `indexing inspect --yes`, `indexing summary`, `indexing watch list|add|remove`,
|
|
78
82
|
and the `bing` commands `login --site`, `sites`, `status`, `dump`, `inspect`, and `verify`.
|
|
79
83
|
Direct Google commands need Local mode: `sync`, `inspect`, `query --live`, `analyze --live`, `report --live`,
|
|
@@ -119,7 +123,13 @@ Hosted sitemap reads and the hosted `indexing` commands need Hosted mode. Their
|
|
|
119
123
|
## Routing
|
|
120
124
|
|
|
121
125
|
This section covers Local mode. In Hosted mode, `query` always reads the hosted record, and JSON has `meta.source: "hosted"`.
|
|
122
|
-
`
|
|
126
|
+
`report` runs deterministic reports through the hosted record in Hosted mode. It needs no local Store.
|
|
127
|
+
Its JSON preserves `cached`, `generatedAt`, the resolved `window`, section `coverage`, `truncated`, and `dataUnavailable`.
|
|
128
|
+
Its `meta.requestId` correlates successful reads with feedback. `meta.source` is `hosted`.
|
|
129
|
+
A section with `dataUnavailable: true` is unavailable evidence, not an empty finding.
|
|
130
|
+
`--force` regenerates the Hosted report. `--prev-start`, `--prev-end`, and `--fetch-budget` need Local mode.
|
|
131
|
+
Hosted reports preserve `--max-findings` from 1 to 1000. Movers also preserves `--min-clicks-change`.
|
|
132
|
+
`analyze` reads the Store only in Hosted mode. When the Store cannot answer, it stops with the Local mode command.
|
|
123
133
|
|
|
124
134
|
`query`, `analyze`, and `report` choose one source for each run. One run never
|
|
125
135
|
mixes Store rows and live rows.
|
|
@@ -292,6 +302,7 @@ A routing stop also has `siteUrl`. `STORE_RANGE_NOT_COVERED` adds `missingDates`
|
|
|
292
302
|
| `gscdump mcp` | Start Google MCP tools in Local mode. Hosted mode uses `https://gscdump.com/mcp` |
|
|
293
303
|
| `gscdump skill install` | Copy this skill into an agent skill directory |
|
|
294
304
|
| `gscdump papercut` | Report a CLI problem to gscdump.com |
|
|
305
|
+
| `gscdump telemetry record` | Explicitly record bounded, declared task quality without prompts or output bodies |
|
|
295
306
|
|
|
296
307
|
`gscdump login`, `gscdump logout`, and `gscdump status` are top-level aliases of
|
|
297
308
|
the matching `auth` subcommands.
|
|
@@ -404,6 +415,11 @@ gscdump papercut --command "report triage" --agent "Claude Code" \
|
|
|
404
415
|
- Include what you ran, what you expected, what happened, and any workaround.
|
|
405
416
|
- `--intent improvement` marks a suggestion. The default is `bug`.
|
|
406
417
|
- The CLI adds its version, Node version, and platform.
|
|
418
|
+
- Add `--request-id` when the failed operation returned one.
|
|
419
|
+
- Add `--parent-request-id` when a consumer returned a separate request ID.
|
|
420
|
+
- Use `--origin requestindexing.com --product requestindexing` for work started there.
|
|
421
|
+
- `--surface cli|mcp|api|skill|browser`, `--operation`, `--workflow-id`, `--skill-digest`, and `--skill-version` add safe context.
|
|
422
|
+
- These fields are declared context. They do not prove authorization or credential origin.
|
|
407
423
|
- Send sanitized details only. Remove tokens, cookies, personal data, private
|
|
408
424
|
URLs, and customer rows. Use placeholder arguments.
|
|
409
425
|
|
|
@@ -415,6 +431,29 @@ The endpoint is anonymous and allows ten reports per network address each
|
|
|
415
431
|
hour. Success returns `id` and `status: "new"`. If reporting fails, mention
|
|
416
432
|
the failure and continue. Never retry an uncertain submission.
|
|
417
433
|
|
|
434
|
+
Hosted requests send the CLI version and client identity. Optional environment variables add declared context:
|
|
435
|
+
`GSCDUMP_PRODUCT`, `GSCDUMP_WORKFLOW_ID`, `GSCDUMP_PARENT_REQUEST_ID`, and `GSCDUMP_SKILL_DIGEST`.
|
|
436
|
+
Use opaque IDs, never prompts, private URLs, or customer rows. No local command history uploads automatically.
|
|
437
|
+
|
|
438
|
+
## Record task quality
|
|
439
|
+
|
|
440
|
+
After an agent workflow, record one explicit terminal outcome when reporting is allowed.
|
|
441
|
+
Reuse one opaque workflow ID throughout the task. Never put customer content in an ID.
|
|
442
|
+
|
|
443
|
+
```sh
|
|
444
|
+
gscdump telemetry record --workflow-id task_01 --operation indexing.summary \
|
|
445
|
+
--outcome success --quality useful --yes --json
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
Use `failure`, `blocked`, or `cancelled` when the task did not complete.
|
|
449
|
+
Add measured `--row-count` and `--duration-ms` only when known.
|
|
450
|
+
Use `--semantic-flags` for simultaneous conditions, such as `partial,stale,truncated`.
|
|
451
|
+
Add request IDs, product, and skill digest when available. Use `--quality unknown` when usefulness is unmeasured.
|
|
452
|
+
This records an agent claim. It never proves that a repair passed engine verification.
|
|
453
|
+
Record incorrect or unusable answers, and submit a separate sanitized papercut for reproduction.
|
|
454
|
+
This skill authorizes bounded task-outcome reports, subject to the user's reporting restrictions.
|
|
455
|
+
If submission fails, mention it and continue. Never retry an uncertain submission.
|
|
456
|
+
|
|
418
457
|
## Guardrails
|
|
419
458
|
|
|
420
459
|
- **Get consent before a mutation.** `sites add`, `sites delete`,
|