@itznexus/itznexus-cli 1.135.0 → 1.137.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.
Files changed (48) hide show
  1. package/dist/commands/api/agent-work.js +22 -2
  2. package/dist/commands/api/agent-work.js.map +1 -1
  3. package/dist/commands/api/projects.js +0 -2
  4. package/dist/commands/api/projects.js.map +1 -1
  5. package/dist/commands/api/reply-suggestions.d.ts +2 -0
  6. package/dist/commands/api/reply-suggestions.js +196 -0
  7. package/dist/commands/api/reply-suggestions.js.map +1 -0
  8. package/dist/commands/api/seo-backlinks.d.ts +2 -0
  9. package/dist/commands/api/seo-backlinks.js +132 -0
  10. package/dist/commands/api/seo-backlinks.js.map +1 -0
  11. package/dist/commands/api/seo-catalog.d.ts +2 -0
  12. package/dist/commands/api/seo-catalog.js +184 -0
  13. package/dist/commands/api/seo-catalog.js.map +1 -0
  14. package/dist/commands/api/seo-changes.d.ts +2 -0
  15. package/dist/commands/api/seo-changes.js +101 -0
  16. package/dist/commands/api/seo-changes.js.map +1 -0
  17. package/dist/commands/api/seo-data.d.ts +2 -0
  18. package/dist/commands/api/seo-data.js +175 -0
  19. package/dist/commands/api/seo-data.js.map +1 -0
  20. package/dist/commands/api/seo-keyword-writes.d.ts +2 -0
  21. package/dist/commands/api/seo-keyword-writes.js +132 -0
  22. package/dist/commands/api/seo-keyword-writes.js.map +1 -0
  23. package/dist/commands/api/seo-keywords.d.ts +2 -0
  24. package/dist/commands/api/seo-keywords.js +103 -0
  25. package/dist/commands/api/seo-keywords.js.map +1 -0
  26. package/dist/commands/api/seo-page-writes.d.ts +2 -0
  27. package/dist/commands/api/seo-page-writes.js +130 -0
  28. package/dist/commands/api/seo-page-writes.js.map +1 -0
  29. package/dist/commands/api/seo-pages.d.ts +2 -0
  30. package/dist/commands/api/seo-pages.js +144 -0
  31. package/dist/commands/api/seo-pages.js.map +1 -0
  32. package/dist/commands/api/seo-settings.d.ts +2 -0
  33. package/dist/commands/api/seo-settings.js +148 -0
  34. package/dist/commands/api/seo-settings.js.map +1 -0
  35. package/dist/commands/api/seo-shared.d.ts +15 -0
  36. package/dist/commands/api/seo-shared.js +77 -0
  37. package/dist/commands/api/seo-shared.js.map +1 -0
  38. package/dist/commands/api/seo.d.ts +2 -0
  39. package/dist/commands/api/seo.js +23 -0
  40. package/dist/commands/api/seo.js.map +1 -0
  41. package/dist/commands/api/sprints.js +1 -1
  42. package/dist/commands/api/sprints.js.map +1 -1
  43. package/dist/index.js +8 -0
  44. package/dist/index.js.map +1 -1
  45. package/package.json +1 -1
  46. package/dist/commands/api/project-next-batch.d.ts +0 -2
  47. package/dist/commands/api/project-next-batch.js +0 -83
  48. package/dist/commands/api/project-next-batch.js.map +0 -1
@@ -0,0 +1,132 @@
1
+ import { Command } from "commander";
2
+ import { api } from "../../lib/api.js";
3
+ import { handleError, printList } from "../../lib/format.js";
4
+ import { compact, csv, done, nullable, nullableInt, qs, } from "./seo-shared.js";
5
+ const ORIGINS = "directory | launch-platform | expert-quote | podcast | press-release | event | linkable-asset | organic";
6
+ const STATUSES = "planned | submitted | live | rejected | lost";
7
+ function withFieldOptions(cmd, required) {
8
+ const origin = "--origin <origin>";
9
+ const platform = "--platform <name>";
10
+ if (required) {
11
+ cmd
12
+ .requiredOption(origin, ORIGINS)
13
+ .requiredOption(platform, "Where the link lives (directory or site name)");
14
+ }
15
+ else {
16
+ cmd.option(origin, ORIGINS).option(platform, "Where the link lives");
17
+ }
18
+ return cmd
19
+ .option("--platform-url <url>", 'Platform URL ("null" to clear)')
20
+ .option("--account <login>", 'Login used, never a password ("null" to clear)')
21
+ .option("--listing-name <name>", "Submission name")
22
+ .option("--tagline <text>", "Submission tagline")
23
+ .option("--description <text>", "Submission description, in our keyword wording")
24
+ .option("--topics <list>", "Comma-separated topics or categories")
25
+ .option("--source-url <url>", "Page carrying the link")
26
+ .option("--target-url <url>", "Our page it links to")
27
+ .option("--anchor-text <text>", "Anchor text")
28
+ .option("--rel <rel>", "follow | nofollow | ugc | sponsored")
29
+ .option("--domain-authority <n>", "Moz DA of the linking site")
30
+ .option("--notes <text>", 'Notes ("null" to clear)');
31
+ }
32
+ function fieldPayload(opts) {
33
+ return compact({
34
+ origin: opts.origin,
35
+ platform: opts.platform,
36
+ platformUrl: nullable(opts.platformUrl),
37
+ account: nullable(opts.account),
38
+ listingName: nullable(opts.listingName),
39
+ tagline: nullable(opts.tagline),
40
+ description: nullable(opts.description),
41
+ topics: opts.topics !== undefined ? csv(opts.topics) : undefined,
42
+ sourceUrl: nullable(opts.sourceUrl),
43
+ targetUrl: nullable(opts.targetUrl),
44
+ anchorText: nullable(opts.anchorText),
45
+ rel: nullable(opts.rel),
46
+ domainAuthority: nullableInt(opts.domainAuthority, "domain-authority"),
47
+ notes: nullable(opts.notes),
48
+ });
49
+ }
50
+ const listCmd = new Command("list")
51
+ .description("List backlinks and submissions")
52
+ .option("--status <status>", STATUSES)
53
+ .option("--origin <origin>", ORIGINS)
54
+ .option("--json", "Output as JSON")
55
+ .action(async (opts) => {
56
+ try {
57
+ const data = await api.get(`/seo/backlinks${qs({ status: opts.status, origin: opts.origin })}`);
58
+ printList(data, [
59
+ { key: "platform", label: "Platform", width: 24 },
60
+ { key: "origin", label: "Origin", width: 15 },
61
+ { key: "status", label: "Status", width: 10 },
62
+ { key: "rel", label: "Rel", width: 9 },
63
+ { key: "domainAuthority", label: "DA", width: 4 },
64
+ { key: "targetUrl", label: "Target", width: 32 },
65
+ { key: "id", label: "Id", width: 36 },
66
+ ], opts);
67
+ }
68
+ catch (err) {
69
+ handleError(err);
70
+ }
71
+ });
72
+ const addCmd = withFieldOptions(new Command("add").description("Record a link to pursue or one already earned"), true)
73
+ .option("--json", "Output as JSON")
74
+ .action(async (opts) => {
75
+ try {
76
+ const data = await api.post("/seo/backlinks", fieldPayload(opts));
77
+ done(data, opts, `Backlink added: ${data.platform} (${data.id})`);
78
+ }
79
+ catch (err) {
80
+ handleError(err);
81
+ }
82
+ });
83
+ const updateCmd = withFieldOptions(new Command("update")
84
+ .description("Update a backlink's fields (status has its own command)")
85
+ .argument("<id>", "Backlink id"), false)
86
+ .option("--json", "Output as JSON")
87
+ .action(async (id, opts) => {
88
+ try {
89
+ const data = await api.post(`/seo/backlinks/${id}`, fieldPayload(opts));
90
+ done(data, opts, `Backlink updated: ${data.platform}`);
91
+ }
92
+ catch (err) {
93
+ handleError(err);
94
+ }
95
+ });
96
+ const statusCmd = new Command("status")
97
+ .description("Move a backlink through planned, submitted, live, rejected, lost")
98
+ .argument("<id>", "Backlink id")
99
+ .argument("<status>", STATUSES)
100
+ .option("--json", "Output as JSON")
101
+ .action(async (id, status, opts) => {
102
+ try {
103
+ const data = await api.post(`/seo/backlinks/${id}/status`, {
104
+ status,
105
+ });
106
+ done(data, opts, `Backlink ${data.platform}: ${data.status}`);
107
+ }
108
+ catch (err) {
109
+ handleError(err);
110
+ }
111
+ });
112
+ const deleteCmd = new Command("delete")
113
+ .description("Delete a backlink record")
114
+ .argument("<id>", "Backlink id")
115
+ .option("--json", "Output as JSON")
116
+ .action(async (id, opts) => {
117
+ try {
118
+ const data = await api.delete(`/seo/backlinks/${id}`);
119
+ done(data, opts, `Backlink deleted: ${id}`);
120
+ }
121
+ catch (err) {
122
+ handleError(err);
123
+ }
124
+ });
125
+ export const seoBacklinkCommand = new Command("backlink")
126
+ .description("Backlinks and off-site submissions (directories, launches, quotes)")
127
+ .addCommand(listCmd)
128
+ .addCommand(addCmd)
129
+ .addCommand(updateCmd)
130
+ .addCommand(statusCmd)
131
+ .addCommand(deleteCmd);
132
+ //# sourceMappingURL=seo-backlinks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seo-backlinks.js","sourceRoot":"","sources":["../../../src/commands/api/seo-backlinks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare const seoCatalogCommands: Command[];
@@ -0,0 +1,184 @@
1
+ import { Command } from "commander";
2
+ import { api } from "../../lib/api.js";
3
+ import { handleError, printList } from "../../lib/format.js";
4
+ import { compact, csv, done, nullable, nullableInt, parseBool, toInt, } from "./seo-shared.js";
5
+ function listCommand(path, description, columns, shape = (row) => row) {
6
+ return new Command("list")
7
+ .description(description)
8
+ .option("--json", "Output as JSON")
9
+ .action(async (opts) => {
10
+ try {
11
+ const data = await api.get(path);
12
+ printList(opts.json ? data : data.map(shape), columns, opts);
13
+ }
14
+ catch (err) {
15
+ handleError(err);
16
+ }
17
+ });
18
+ }
19
+ function archiveCommand(base, noun, argName) {
20
+ return new Command("archive")
21
+ .description(`Archive a ${noun} (never deleted; history keeps it)`)
22
+ .argument(`<${argName}>`, `The ${noun}'s ${argName}`)
23
+ .option("--json", "Output as JSON")
24
+ .action(async (ref, opts) => {
25
+ try {
26
+ const data = await api.post(`${base}/${encodeURIComponent(ref)}/archive`, {});
27
+ done(data, opts, `Archived ${noun} ${ref}`);
28
+ }
29
+ catch (err) {
30
+ handleError(err);
31
+ }
32
+ });
33
+ }
34
+ const marketSet = new Command("set")
35
+ .description("Create or update a market keyed by code (every option required on create)")
36
+ .argument("<code>", "Lowercase ISO 3166-1 alpha-2 code (us, gb, fr)")
37
+ .option("--name <name>", "Display name (United States)")
38
+ .option("--language <lang>", "Site language: en | fr")
39
+ .option("--country-code <code>", "ISO 3166-1 alpha-2, the hreflang region (US)")
40
+ .option("--search-console-country <code>", "ISO 3166-1 alpha-3 as Search Console reports it (usa)")
41
+ .option("--dataforseo-location-code <n>", "DataForSEO location code (2840)")
42
+ .option("--position <n>", "Sort position")
43
+ .option("--json", "Output as JSON")
44
+ .action(async (code, opts) => {
45
+ try {
46
+ const data = await api.post("/seo/markets", compact({
47
+ code,
48
+ name: opts.name,
49
+ language: opts.language,
50
+ countryCode: opts.countryCode,
51
+ searchConsoleCountry: opts.searchConsoleCountry,
52
+ dataforseoLocationCode: opts.dataforseoLocationCode !== undefined
53
+ ? toInt(opts.dataforseoLocationCode, "dataforseo-location-code")
54
+ : undefined,
55
+ position: opts.position !== undefined
56
+ ? toInt(opts.position, "position")
57
+ : undefined,
58
+ }));
59
+ done(data, opts, `Market set: ${code}`);
60
+ }
61
+ catch (err) {
62
+ handleError(err);
63
+ }
64
+ });
65
+ const marketCommand = new Command("market")
66
+ .description("Markets: a language plus a country, with its provider codes")
67
+ .addCommand(listCommand("/seo/markets", "List markets", [
68
+ { key: "code", label: "Code", width: 5 },
69
+ { key: "name", label: "Name", width: 16 },
70
+ { key: "language", label: "Lang", width: 5 },
71
+ { key: "countryCode", label: "Country", width: 8 },
72
+ { key: "searchConsoleCountry", label: "GSC", width: 5 },
73
+ { key: "dataforseoLocationCode", label: "DataForSEO", width: 10 },
74
+ { key: "archivedAt", label: "Archived", width: 24 },
75
+ ]))
76
+ .addCommand(marketSet)
77
+ .addCommand(archiveCommand("/seo/markets", "market", "code"));
78
+ const domainSet = new Command("set")
79
+ .description("Create or update a domain (ours or a competitor), keyed by domain")
80
+ .argument("<domain>", "Domain or URL (normalized to the host without www.)")
81
+ .option("--role <role>", "own | competitor (required on create)")
82
+ .option("--name <name>", 'Display name ("null" to clear)')
83
+ .option("--markets <codes>", "Comma-separated market codes it is mined in (empty: every market)")
84
+ .option("--notes <text>", 'Notes ("null" to clear)')
85
+ .option("--json", "Output as JSON")
86
+ .action(async (domain, opts) => {
87
+ try {
88
+ const data = await api.post("/seo/domains", compact({
89
+ domain,
90
+ role: opts.role,
91
+ name: nullable(opts.name),
92
+ marketCodes: opts.markets !== undefined ? csv(opts.markets) : undefined,
93
+ notes: nullable(opts.notes),
94
+ }));
95
+ done(data, opts, `Domain set: ${String(data.domain)}`);
96
+ }
97
+ catch (err) {
98
+ handleError(err);
99
+ }
100
+ });
101
+ const domainMetrics = new Command("metrics")
102
+ .description("Stamp a domain's Moz link metrics")
103
+ .argument("<domain>", "Domain")
104
+ .option("--domain-authority <n>", "Moz Domain Authority (0-100)")
105
+ .option("--page-authority <n>", "Moz Page Authority of the home page")
106
+ .option("--linking-root-domains <n>", "Linking root domains")
107
+ .option("--spam-score <n>", "Moz spam score")
108
+ .option("--checked-at <iso>", "When the metrics were read (defaults to now)")
109
+ .option("--json", "Output as JSON")
110
+ .action(async (domain, opts) => {
111
+ try {
112
+ const data = await api.post(`/seo/domains/${encodeURIComponent(domain)}/metrics`, compact({
113
+ domainAuthority: nullableInt(opts.domainAuthority, "domain-authority"),
114
+ pageAuthority: nullableInt(opts.pageAuthority, "page-authority"),
115
+ linkingRootDomains: nullableInt(opts.linkingRootDomains, "linking-root-domains"),
116
+ spamScore: nullableInt(opts.spamScore, "spam-score"),
117
+ checkedAt: opts.checkedAt,
118
+ }));
119
+ done(data, opts, `Metrics stamped on ${domain}`);
120
+ }
121
+ catch (err) {
122
+ handleError(err);
123
+ }
124
+ });
125
+ const domainCommand = new Command("domain")
126
+ .description("Our domain and the tracked competitors, with Moz metrics")
127
+ .addCommand(listCommand("/seo/domains", "List domains (ours first)", [
128
+ { key: "domain", label: "Domain", width: 28 },
129
+ { key: "role", label: "Role", width: 11 },
130
+ { key: "domainAuthority", label: "DA", width: 4 },
131
+ { key: "pageAuthority", label: "PA", width: 4 },
132
+ { key: "linkingRootDomains", label: "LRD", width: 6 },
133
+ { key: "markets", label: "Markets", width: 16 },
134
+ { key: "archivedAt", label: "Archived", width: 24 },
135
+ ], (row) => ({
136
+ ...row,
137
+ markets: row.marketCodes.join(",") || "all",
138
+ })))
139
+ .addCommand(domainSet)
140
+ .addCommand(domainMetrics)
141
+ .addCommand(archiveCommand("/seo/domains", "domain", "domain"));
142
+ const hubSet = new Command("set")
143
+ .description("Create or update a hub page keyed by slug (/services/)")
144
+ .argument("<slug>", "Hub URL segment, kebab-case, immutable")
145
+ .option("--title-en <title>", "English hub title")
146
+ .option("--title-fr <title>", "French hub title")
147
+ .option("--in-footer <bool>", "Linked from the footer: true | false")
148
+ .option("--position <n>", "Sort position")
149
+ .option("--json", "Output as JSON")
150
+ .action(async (slug, opts) => {
151
+ try {
152
+ const titles = compact({ en: opts.titleEn, fr: opts.titleFr });
153
+ const data = await api.post("/seo/hubs", compact({
154
+ slug,
155
+ titles: Object.keys(titles).length ? titles : undefined,
156
+ inFooter: opts.inFooter !== undefined
157
+ ? parseBool(opts.inFooter, "in-footer")
158
+ : undefined,
159
+ position: opts.position !== undefined
160
+ ? toInt(opts.position, "position")
161
+ : undefined,
162
+ }));
163
+ done(data, opts, `Hub set: ${slug}`);
164
+ }
165
+ catch (err) {
166
+ handleError(err);
167
+ }
168
+ });
169
+ const hubCommand = new Command("hub")
170
+ .description("Hub pages grouping landing pages (/services/, /alternatives/)")
171
+ .addCommand(listCommand("/seo/hubs", "List hubs", [
172
+ { key: "slug", label: "Slug", width: 16 },
173
+ { key: "titleEn", label: "Title (en)", width: 24 },
174
+ { key: "titleFr", label: "Title (fr)", width: 24 },
175
+ { key: "inFooter", label: "Footer", width: 7 },
176
+ { key: "archivedAt", label: "Archived", width: 24 },
177
+ ], (row) => {
178
+ const titles = row.titles;
179
+ return { ...row, titleEn: titles.en, titleFr: titles.fr };
180
+ }))
181
+ .addCommand(hubSet)
182
+ .addCommand(archiveCommand("/seo/hubs", "hub", "slug"));
183
+ export const seoCatalogCommands = [marketCommand, domainCommand, hubCommand];
184
+ //# sourceMappingURL=seo-catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seo-catalog.js","sourceRoot":"","sources":["../../../src/commands/api/seo-catalog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare const seoChangeCommand: Command;
@@ -0,0 +1,101 @@
1
+ import { Command } from "commander";
2
+ import { api } from "../../lib/api.js";
3
+ import { handleError, printList } from "../../lib/format.js";
4
+ import { compact, done, nullable, qs, readJsonObject, } from "./seo-shared.js";
5
+ const REASONS = "weak-hook | low-ctr | over-optimized | awkward-keyword | spelling | internal-links | mixed-intent | technical | other";
6
+ const listCmd = new Command("list")
7
+ .description("List page changes (one page, or every page by verdict)")
8
+ .option("--page-id <id>", "Changes of one page")
9
+ .option("--verdict <verdict>", "Cross-page filter: pending | improved | no-change | worse")
10
+ .option("--json", "Output as JSON")
11
+ .action(async (opts) => {
12
+ try {
13
+ const data = opts.pageId
14
+ ? await api.get(`/seo/pages/${opts.pageId}/changes`)
15
+ : await api.get(`/seo/changes${qs({ verdict: opts.verdict })}`);
16
+ printList(data, [
17
+ { key: "changedAt", label: "Changed at", width: 24 },
18
+ { key: "pagePath", label: "Page", width: 28 },
19
+ { key: "reason", label: "Reason", width: 16 },
20
+ { key: "verdict", label: "Verdict", width: 10 },
21
+ { key: "change", label: "Change", width: 40 },
22
+ { key: "id", label: "Id", width: 36 },
23
+ ], opts);
24
+ }
25
+ catch (err) {
26
+ handleError(err);
27
+ }
28
+ });
29
+ const addCmd = new Command("add")
30
+ .description("Log a change made to a page (date, reason, exactly what changed)")
31
+ .argument("<pageId>", "Page id")
32
+ .requiredOption("--reason <reason>", REASONS)
33
+ .requiredOption("--change <text>", "Exactly what changed")
34
+ .option("--changed-at <iso>", "When it changed (defaults to now)")
35
+ .option("--resubmitted-at <iso>", "When the URL was resubmitted for indexing")
36
+ .option("--json", "Output as JSON")
37
+ .action(async (pageId, opts) => {
38
+ try {
39
+ const data = await api.post(`/seo/pages/${pageId}/changes`, compact({
40
+ reason: opts.reason,
41
+ change: opts.change,
42
+ changedAt: opts.changedAt,
43
+ resubmittedAt: opts.resubmittedAt,
44
+ }));
45
+ done(data, opts, `Change logged (${data.id})`);
46
+ }
47
+ catch (err) {
48
+ handleError(err);
49
+ }
50
+ });
51
+ const updateCmd = new Command("update")
52
+ .description("Correct a logged change or stamp its resubmission")
53
+ .argument("<pageId>", "Page id")
54
+ .argument("<changeId>", "Change id")
55
+ .option("--reason <reason>", REASONS)
56
+ .option("--change <text>", "Exactly what changed")
57
+ .option("--changed-at <iso>", "When it changed")
58
+ .option("--resubmitted-at <iso>", 'When the URL was resubmitted ("null" to clear)')
59
+ .option("--json", "Output as JSON")
60
+ .action(async (pageId, changeId, opts) => {
61
+ try {
62
+ const data = await api.post(`/seo/pages/${pageId}/changes/${changeId}`, compact({
63
+ reason: opts.reason,
64
+ change: opts.change,
65
+ changedAt: opts.changedAt,
66
+ resubmittedAt: nullable(opts.resubmittedAt),
67
+ }));
68
+ done(data, opts, `Change updated (${data.id})`);
69
+ }
70
+ catch (err) {
71
+ handleError(err);
72
+ }
73
+ });
74
+ const verdictCmd = new Command("verdict")
75
+ .description("Record whether a change worked, with the windows it was judged on")
76
+ .argument("<pageId>", "Page id")
77
+ .argument("<changeId>", "Change id")
78
+ .argument("<verdict>", "improved | no-change | worse")
79
+ .option("--comparison-file <path>", "JSON {before, after}, each {from, to, clicks, impressions, ctr, position}")
80
+ .option("--json", "Output as JSON")
81
+ .action(async (pageId, changeId, verdict, opts) => {
82
+ try {
83
+ const data = await api.post(`/seo/pages/${pageId}/changes/${changeId}/verdict`, compact({
84
+ verdict,
85
+ comparison: opts.comparisonFile
86
+ ? readJsonObject(opts.comparisonFile)
87
+ : undefined,
88
+ }));
89
+ done(data, opts, `Verdict recorded: ${verdict}`);
90
+ }
91
+ catch (err) {
92
+ handleError(err);
93
+ }
94
+ });
95
+ export const seoChangeCommand = new Command("change")
96
+ .description("Per-page change log: what changed, why, and whether it worked")
97
+ .addCommand(listCmd)
98
+ .addCommand(addCmd)
99
+ .addCommand(updateCmd)
100
+ .addCommand(verdictCmd);
101
+ //# sourceMappingURL=seo-changes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seo-changes.js","sourceRoot":"","sources":["../../../src/commands/api/seo-changes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare const seoDataCommands: Command[];
@@ -0,0 +1,175 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import { api } from "../../lib/api.js";
4
+ import { handleError, printList } from "../../lib/format.js";
5
+ import { done, printJson, qs, readJsonRows, } from "./seo-shared.js";
6
+ // The API's per-request caps on bulk imports; larger files are chunked here.
7
+ const RANKINGS_PER_REQUEST = 1000;
8
+ const SEARCH_ANALYTICS_PER_REQUEST = 5000;
9
+ async function importInChunks(path, rows, size) {
10
+ let imported = 0;
11
+ for (let i = 0; i < rows.length; i += size) {
12
+ const res = await api.post(path, {
13
+ rows: rows.slice(i, i + size),
14
+ });
15
+ imported += res.imported;
16
+ }
17
+ return imported;
18
+ }
19
+ const rankingList = new Command("list")
20
+ .description("List tracked positions per keyword, market, and date")
21
+ .option("--keyword-id <id>", "One keyword")
22
+ .option("--market <code>", "Market code")
23
+ .option("--device <device>", "desktop | mobile | tablet")
24
+ .option("--from <date>", "From date (YYYY-MM-DD)")
25
+ .option("--to <date>", "To date (YYYY-MM-DD)")
26
+ .option("--page <n>", "Page number (default 1)")
27
+ .option("--page-size <n>", "Page size (default 100, max 1000)")
28
+ .option("--json", "Output as JSON")
29
+ .action(async (opts) => {
30
+ try {
31
+ const { data, meta } = await api.getWithMeta(`/seo/rankings${qs({
32
+ keywordId: opts.keywordId,
33
+ market: opts.market,
34
+ device: opts.device,
35
+ from: opts.from,
36
+ to: opts.to,
37
+ page: opts.page,
38
+ pageSize: opts.pageSize,
39
+ })}`);
40
+ if (opts.json) {
41
+ printJson({ data, meta });
42
+ return;
43
+ }
44
+ printList(data, [
45
+ { key: "date", label: "Date", width: 10 },
46
+ { key: "market", label: "Mkt", width: 4 },
47
+ { key: "keyword", label: "Keyword", width: 36 },
48
+ { key: "device", label: "Device", width: 7 },
49
+ { key: "position", label: "Pos.", width: 5 },
50
+ { key: "url", label: "URL", width: 48 },
51
+ ], opts);
52
+ if (meta) {
53
+ console.log(chalk.dim(`page ${meta.page}/${meta.totalPages}, ${meta.total} total`));
54
+ }
55
+ }
56
+ catch (err) {
57
+ handleError(err);
58
+ }
59
+ });
60
+ const rankingImport = new Command("import")
61
+ .description("Upsert rank-tracking rows from a JSON file (safe to rerun)")
62
+ .requiredOption("--file <path>", "JSON array (or {rows}) of {keywordId, market, date, device?, position|null, url?}")
63
+ .option("--json", "Output as JSON")
64
+ .action(async (opts) => {
65
+ try {
66
+ const rows = readJsonRows(opts.file);
67
+ const imported = await importInChunks("/seo/rankings", rows, RANKINGS_PER_REQUEST);
68
+ done({ imported }, opts, `Imported ${imported} ranking rows`);
69
+ }
70
+ catch (err) {
71
+ handleError(err);
72
+ }
73
+ });
74
+ const rankingCommand = new Command("ranking")
75
+ .description("Weekly rank tracking per keyword and market")
76
+ .addCommand(rankingList)
77
+ .addCommand(rankingImport);
78
+ const analyticsQuery = new Command("query")
79
+ .description("Aggregate Search Console data by dimensions (clicks, impressions, CTR, position)")
80
+ .option("--from <date>", "From date (default: start of the last 28 days)")
81
+ .option("--to <date>", "To date (default: latest complete day)")
82
+ .option("--dimensions <list>", "Comma-separated: date, path, page, query, country, device (default path)")
83
+ .option("--path <path>", "Only this page path")
84
+ .option("--query <text>", "Query contains")
85
+ .option("--country <code>", "Alpha-3 country (usa, fra)")
86
+ .option("--device <device>", "desktop | mobile | tablet")
87
+ .option("--row-limit <n>", "Max rows (default 1000, max 25000)")
88
+ .option("--json", "Output as JSON")
89
+ .action(async (opts) => {
90
+ try {
91
+ const { data, meta } = await api.getWithMeta(`/seo/search-analytics${qs({
92
+ from: opts.from,
93
+ to: opts.to,
94
+ dimensions: opts.dimensions,
95
+ path: opts.path,
96
+ query: opts.query,
97
+ country: opts.country,
98
+ device: opts.device,
99
+ rowLimit: opts.rowLimit,
100
+ })}`);
101
+ if (opts.json) {
102
+ printJson({ data, meta });
103
+ return;
104
+ }
105
+ const dims = meta?.dimensions ?? ["path"];
106
+ printList(data.map((r) => ({
107
+ ...r,
108
+ ctr: `${(r.ctr * 100).toFixed(1)}%`,
109
+ position: r.position != null ? r.position.toFixed(1) : null,
110
+ })), [
111
+ ...dims.map((d) => ({
112
+ key: d,
113
+ label: d,
114
+ width: d === "query" || d === "page" ? 40 : 24,
115
+ })),
116
+ { key: "clicks", label: "Clicks", width: 7 },
117
+ { key: "impressions", label: "Impr.", width: 7 },
118
+ { key: "ctr", label: "CTR", width: 6 },
119
+ { key: "position", label: "Pos.", width: 5 },
120
+ ], opts);
121
+ if (meta)
122
+ console.log(chalk.dim(`${meta.from} to ${meta.to}`));
123
+ }
124
+ catch (err) {
125
+ handleError(err);
126
+ }
127
+ });
128
+ const analyticsImport = new Command("import")
129
+ .description("Upsert Search Console rows from a JSON file (safe to rerun)")
130
+ .requiredOption("--file <path>", "JSON array (or {rows}) of {date, page, query, country, device, clicks, impressions, ctr, position}")
131
+ .option("--json", "Output as JSON")
132
+ .action(async (opts) => {
133
+ try {
134
+ const rows = readJsonRows(opts.file);
135
+ const imported = await importInChunks("/seo/search-analytics", rows, SEARCH_ANALYTICS_PER_REQUEST);
136
+ done({ imported }, opts, `Imported ${imported} Search Console rows`);
137
+ }
138
+ catch (err) {
139
+ handleError(err);
140
+ }
141
+ });
142
+ const searchAnalyticsCommand = new Command("search-analytics")
143
+ .description("Search Console performance at daily grain (page, query, country, device)")
144
+ .addCommand(analyticsQuery)
145
+ .addCommand(analyticsImport);
146
+ const conversionsCommand = new Command("conversions")
147
+ .description("Captures, Forge sessions, and leads per landing path over a window")
148
+ .option("--days <n>", "Window in days ending now (default 28)")
149
+ .option("--json", "Output as JSON")
150
+ .action(async (opts) => {
151
+ try {
152
+ const { data, meta } = await api.getWithMeta(`/seo/conversions${qs({ days: opts.days })}`);
153
+ if (opts.json) {
154
+ printJson({ data, meta });
155
+ return;
156
+ }
157
+ printList(data, [
158
+ { key: "path", label: "Landing path", width: 40 },
159
+ { key: "captures", label: "Captures", width: 8 },
160
+ { key: "forgeSessions", label: "Forge", width: 6 },
161
+ { key: "leads", label: "Leads", width: 6 },
162
+ ], opts);
163
+ if (meta)
164
+ console.log(chalk.dim(`${meta.window.from} to ${meta.window.to}`));
165
+ }
166
+ catch (err) {
167
+ handleError(err);
168
+ }
169
+ });
170
+ export const seoDataCommands = [
171
+ rankingCommand,
172
+ searchAnalyticsCommand,
173
+ conversionsCommand,
174
+ ];
175
+ //# sourceMappingURL=seo-data.js.map