outrank-cli 0.1.1 → 0.3.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 +218 -25
- package/SKILL.md +439 -282
- package/package.json +1 -1
- package/src/commands/keywords.js +17 -24
- package/src/commands/products.js +711 -2
- package/src/index.js +5 -1
package/src/commands/products.js
CHANGED
|
@@ -2,6 +2,11 @@ import { parseArgs, getRequiredPositional, readJsonInput, hasFlag, parseIntegerO
|
|
|
2
2
|
import { requestAgentApi } from '../core/api.js';
|
|
3
3
|
import { cliSuccess } from '../core/errors.js';
|
|
4
4
|
|
|
5
|
+
const BACKLINK_LIST_LIMIT_RANGE = {
|
|
6
|
+
min: 1,
|
|
7
|
+
max: 100,
|
|
8
|
+
};
|
|
9
|
+
|
|
5
10
|
export const PRODUCTS_HELP = `Usage:
|
|
6
11
|
outrank-cli products list [--status active|disabled] [--limit <n>] [--cursor <iso-timestamp>]
|
|
7
12
|
outrank-cli products get <product-id>
|
|
@@ -9,6 +14,25 @@ export const PRODUCTS_HELP = `Usage:
|
|
|
9
14
|
outrank-cli products update <product-id> --json <object>
|
|
10
15
|
outrank-cli products pause <product-id>
|
|
11
16
|
outrank-cli products resume <product-id>
|
|
17
|
+
outrank-cli products integrations providers
|
|
18
|
+
outrank-cli products integrations prepare <provider> <product-id> [--json <object>]
|
|
19
|
+
outrank-cli products integrations connect <provider> <product-id> [--json <object>]
|
|
20
|
+
outrank-cli products integrations poll <provider> <product-id> --attempt-id <attempt-id>
|
|
21
|
+
outrank-cli products integrations notion databases <product-id> <integration-id>
|
|
22
|
+
outrank-cli products integrations notion schema <product-id> <integration-id> <database-id>
|
|
23
|
+
outrank-cli products integrations framer collections --json <object>
|
|
24
|
+
outrank-cli products integrations framer fields --json <object>
|
|
25
|
+
outrank-cli products integrations webflow sites --json <object>
|
|
26
|
+
outrank-cli products integrations webflow collections --json <object>
|
|
27
|
+
outrank-cli products integrations webflow fields --json <object>
|
|
28
|
+
outrank-cli products integrations webflow items --json <object>
|
|
29
|
+
outrank-cli products integrations validate <provider> --json <object>
|
|
30
|
+
outrank-cli products integrations list <product-id>
|
|
31
|
+
outrank-cli products integrations create <product-id> --json <object>
|
|
32
|
+
outrank-cli products integrations get <product-id> <integration-id>
|
|
33
|
+
outrank-cli products integrations update <product-id> <integration-id> --json <object>
|
|
34
|
+
outrank-cli products integrations test <product-id> <integration-id>
|
|
35
|
+
outrank-cli products integrations delete <product-id> <integration-id>
|
|
12
36
|
outrank-cli products output-settings get <product-id>
|
|
13
37
|
outrank-cli products output-settings update <product-id> --json <object>
|
|
14
38
|
outrank-cli products target-audiences list <product-id>
|
|
@@ -27,6 +51,25 @@ export const PRODUCTS_HELP = `Usage:
|
|
|
27
51
|
outrank-cli products gsc search-analytics <product-id> [--dimensions <csv>] [--limit <n>] [--months <n>] [--start-date <yyyy-mm-dd>] [--end-date <yyyy-mm-dd>] [--page <text>] [--query <text>] [--country <cc>] [--device <device>]
|
|
28
52
|
outrank-cli products gsc inspect-url <product-id> --url <url> [--language-code <code>]
|
|
29
53
|
outrank-cli products gsc cannibalization <product-id>
|
|
54
|
+
outrank-cli products linking sources get <product-id>
|
|
55
|
+
outrank-cli products linking sources update <product-id> --json <object>
|
|
56
|
+
outrank-cli products linking detect <product-id>
|
|
57
|
+
outrank-cli products linking target-config get <product-id>
|
|
58
|
+
outrank-cli products linking target-config update <product-id> --json <object>
|
|
59
|
+
outrank-cli products linking targets list <product-id>
|
|
60
|
+
outrank-cli products linking targets add-manual <product-id> --json <object>
|
|
61
|
+
outrank-cli products linking targets upsert <product-id> --json <object>
|
|
62
|
+
outrank-cli products linking targets clear-customizations <product-id> <target-id>
|
|
63
|
+
outrank-cli products linking targets delete <product-id> <target-id>
|
|
64
|
+
outrank-cli products backlinks get <product-id>
|
|
65
|
+
outrank-cli products backlinks update <product-id> --json <object>
|
|
66
|
+
outrank-cli products backlinks credits <product-id>
|
|
67
|
+
outrank-cli products backlinks earned <product-id> [--limit <n>] [--cursor <iso>] [--status <status>] [--sort-by created_at|status|credits_applied] [--sort-order desc] [--include-article] [--include-summary]
|
|
68
|
+
outrank-cli products backlinks unverified <product-id> [--limit <n>] [--cursor <iso>] [--status <status>] [--sort-by created_at|verification_error_reason] [--sort-order desc]
|
|
69
|
+
outrank-cli products backlinks check-removal <product-id> <backlink-id>
|
|
70
|
+
outrank-cli products backlinks reverify <product-id> <backlink-id>
|
|
71
|
+
outrank-cli products backlinks subscription <product-id>
|
|
72
|
+
outrank-cli products backlinks subscribe-quote <product-id> --plan premium|enterprise
|
|
30
73
|
|
|
31
74
|
JSON Input:
|
|
32
75
|
Use one of: --json '<object>', --json-file payload.json, or --json-stdin
|
|
@@ -35,6 +78,40 @@ Examples:
|
|
|
35
78
|
outrank-cli products list --limit 20
|
|
36
79
|
outrank-cli products create --json-file product.json
|
|
37
80
|
outrank-cli products update 00000000-0000-0000-0000-000000000000 --json '{"daily_article_count":2}'
|
|
81
|
+
outrank-cli products integrations providers
|
|
82
|
+
outrank-cli products integrations prepare wordpress-plugin 00000000-0000-0000-0000-000000000000
|
|
83
|
+
outrank-cli products integrations connect wordpress-com 00000000-0000-0000-0000-000000000000 --json '{"name":"Main WordPress.com","publish_status":"publish"}'
|
|
84
|
+
outrank-cli products integrations connect notion 00000000-0000-0000-0000-000000000000
|
|
85
|
+
outrank-cli products integrations poll wordpress-com 00000000-0000-0000-0000-000000000000 --attempt-id <attempt-id>
|
|
86
|
+
outrank-cli products integrations notion databases 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111
|
|
87
|
+
outrank-cli products integrations notion schema 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111 aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
|
|
88
|
+
outrank-cli products integrations framer collections --json '{"project_url":"https://framer.com/projects/your-project","server_api_key":"framer_server_key"}'
|
|
89
|
+
outrank-cli products integrations framer fields --json '{"project_url":"https://framer.com/projects/your-project","server_api_key":"framer_server_key","collection_id":"collection_id"}'
|
|
90
|
+
outrank-cli products integrations webflow sites --json-file webflow-sites.json
|
|
91
|
+
outrank-cli products integrations webflow collections --json '{"api_token":"wf_token","site_id":"site_id"}'
|
|
92
|
+
outrank-cli products integrations webflow fields --json '{"api_token":"wf_token","collection_id":"collection_id"}'
|
|
93
|
+
outrank-cli products integrations webflow items --json '{"api_token":"wf_token","collection_id":"collection_id"}'
|
|
94
|
+
outrank-cli products integrations validate webhook --json '{"endpoint":"https://api.example.com/webhook","access_token":"secret"}'
|
|
95
|
+
outrank-cli products integrations validate framer --json '{"project_url":"https://framer.com/projects/your-project","server_api_key":"framer_server_key"}'
|
|
96
|
+
outrank-cli products integrations validate webflow --json '{"api_token":"wf_token"}'
|
|
97
|
+
outrank-cli products integrations validate shopify --json '{"access_token":"shpat_xxx"}'
|
|
98
|
+
outrank-cli products integrations validate wix --json '{"site_id":"00000000-0000-0000-0000-000000000000","api_key":"wix_api_key"}'
|
|
99
|
+
outrank-cli products integrations validate wordpress-plugin --json '{"domain":"https://example.com","api_key":"secret-token"}'
|
|
100
|
+
outrank-cli products integrations list 00000000-0000-0000-0000-000000000000
|
|
101
|
+
outrank-cli products integrations create 00000000-0000-0000-0000-000000000000 --json '{"provider":"webhook","name":"Main Webhook","endpoint":"https://api.example.com/webhook","access_token":"secret"}'
|
|
102
|
+
outrank-cli products integrations create 00000000-0000-0000-0000-000000000000 --json '{"provider":"framer","name":"Main Framer","project_url":"https://framer.com/projects/your-project","server_api_key":"framer_server_key","author_name":"Outrank AI","publish_mode":"deploy","collection_setup_mode":"create"}'
|
|
103
|
+
outrank-cli products integrations create 00000000-0000-0000-0000-000000000000 --json '{"provider":"shopify","name":"Main Shopify","access_token":"shpat_xxx","blog_id":"123456789","author_name":"Outrank","publish_status":"publish"}'
|
|
104
|
+
outrank-cli products integrations create 00000000-0000-0000-0000-000000000000 --json '{"provider":"wix","name":"Main Wix","site_id":"00000000-0000-0000-0000-000000000000","api_key":"wix_api_key","publish_status":"publish"}'
|
|
105
|
+
outrank-cli products integrations create 00000000-0000-0000-0000-000000000000 --json-file webflow-create.json
|
|
106
|
+
outrank-cli products integrations create 00000000-0000-0000-0000-000000000000 --json '{"provider":"wordpress-plugin","domain":"https://example.com","api_key":"secret-token"}'
|
|
107
|
+
outrank-cli products integrations create 00000000-0000-0000-0000-000000000000 --json '{"provider":"nextjs-blog"}'
|
|
108
|
+
outrank-cli products integrations get 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111
|
|
109
|
+
outrank-cli products integrations update 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111 --json '{"endpoint":"https://api.example.com/webhook-v2"}'
|
|
110
|
+
outrank-cli products integrations update 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111 --json '{"blog_id":"987654321","author_name":"Editorial Team","publish_status":"draft"}'
|
|
111
|
+
outrank-cli products integrations update 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111 --json '{"publish_status":"draft"}'
|
|
112
|
+
outrank-cli products integrations update 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111 --json-file webflow-update.json
|
|
113
|
+
outrank-cli products integrations update 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111 --json '{"domain":"https://example.com/blog","api_key":"new-secret-token"}'
|
|
114
|
+
outrank-cli products integrations test 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111
|
|
38
115
|
outrank-cli products output-settings update 00000000-0000-0000-0000-000000000000 --json-file output-settings.json
|
|
39
116
|
outrank-cli products target-audiences replace 00000000-0000-0000-0000-000000000000 --json-file target-audiences.json
|
|
40
117
|
outrank-cli products competitors replace 00000000-0000-0000-0000-000000000000 --json-file competitors.json
|
|
@@ -42,6 +119,21 @@ Examples:
|
|
|
42
119
|
outrank-cli products gsc properties 00000000-0000-0000-0000-000000000000 --email owner@example.com
|
|
43
120
|
outrank-cli products gsc performance 00000000-0000-0000-0000-000000000000 --months 3
|
|
44
121
|
outrank-cli products gsc inspect-url 00000000-0000-0000-0000-000000000000 --url https://example.com/blog/post
|
|
122
|
+
outrank-cli products linking sources get 00000000-0000-0000-0000-000000000000
|
|
123
|
+
outrank-cli products linking sources update 00000000-0000-0000-0000-000000000000 --json-file linking-sources.json
|
|
124
|
+
outrank-cli products linking detect 00000000-0000-0000-0000-000000000000
|
|
125
|
+
outrank-cli products linking target-config update 00000000-0000-0000-0000-000000000000 --json '{"only_prioritized":true}'
|
|
126
|
+
outrank-cli products linking targets add-manual 00000000-0000-0000-0000-000000000000 --json-file manual-target.json
|
|
127
|
+
outrank-cli products linking targets clear-customizations 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111
|
|
128
|
+
outrank-cli products backlinks get 00000000-0000-0000-0000-000000000000
|
|
129
|
+
outrank-cli products backlinks update 00000000-0000-0000-0000-000000000000 --json '{"enabled":true,"min_domain_rating":10}'
|
|
130
|
+
outrank-cli products backlinks credits 00000000-0000-0000-0000-000000000000
|
|
131
|
+
outrank-cli products backlinks earned 00000000-0000-0000-0000-000000000000 --limit 20 --include-article
|
|
132
|
+
outrank-cli products backlinks unverified 00000000-0000-0000-0000-000000000000 --limit 20
|
|
133
|
+
outrank-cli products backlinks check-removal 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111
|
|
134
|
+
outrank-cli products backlinks reverify 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111
|
|
135
|
+
outrank-cli products backlinks subscription 00000000-0000-0000-0000-000000000000
|
|
136
|
+
outrank-cli products backlinks subscribe-quote 00000000-0000-0000-0000-000000000000 --plan premium
|
|
45
137
|
`;
|
|
46
138
|
|
|
47
139
|
const buildQueryPath = (basePath, searchParams) => {
|
|
@@ -115,12 +207,148 @@ const handleProductsResume = async (parsed) => {
|
|
|
115
207
|
return response.payload;
|
|
116
208
|
};
|
|
117
209
|
|
|
118
|
-
const
|
|
210
|
+
const handleBacklinks = async (argv) => {
|
|
119
211
|
const subcommand = argv[0];
|
|
120
212
|
const parsed = parseArgs(argv.slice(1));
|
|
121
213
|
const wantsHelp =
|
|
122
214
|
!subcommand || subcommand === '--help' || subcommand === '-h' || subcommand === 'help' || hasFlag(parsed, 'help');
|
|
123
215
|
|
|
216
|
+
if (wantsHelp) {
|
|
217
|
+
return cliSuccess(
|
|
218
|
+
{
|
|
219
|
+
usage: `Usage:
|
|
220
|
+
outrank-cli products backlinks get <product-id>
|
|
221
|
+
outrank-cli products backlinks update <product-id> --json <object>
|
|
222
|
+
outrank-cli products backlinks credits <product-id>
|
|
223
|
+
outrank-cli products backlinks earned <product-id> [--limit <n>] [--cursor <iso>] [--status <status>] [--sort-by created_at|status|credits_applied] [--sort-order desc] [--include-article] [--include-summary]
|
|
224
|
+
outrank-cli products backlinks unverified <product-id> [--limit <n>] [--cursor <iso>] [--status <status>] [--sort-by created_at|verification_error_reason] [--sort-order desc]
|
|
225
|
+
outrank-cli products backlinks check-removal <product-id> <backlink-id>
|
|
226
|
+
outrank-cli products backlinks reverify <product-id> <backlink-id>
|
|
227
|
+
outrank-cli products backlinks subscription <product-id>
|
|
228
|
+
outrank-cli products backlinks subscribe-quote <product-id> --plan premium|enterprise`,
|
|
229
|
+
},
|
|
230
|
+
'Use `get` and `update` for product backlink settings, `credits` and `subscription` for status, `earned` and `unverified` for review queues, and the action commands for verification follow-up.',
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const productId = getRequiredPositional(parsed, 0, '<product-id>');
|
|
235
|
+
const basePath = `/products/${productId}/backlinks`;
|
|
236
|
+
|
|
237
|
+
if (subcommand === 'get') {
|
|
238
|
+
const response = await requestAgentApi(parsed, {
|
|
239
|
+
method: 'GET',
|
|
240
|
+
path: basePath,
|
|
241
|
+
});
|
|
242
|
+
return response.payload;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (subcommand === 'update') {
|
|
246
|
+
const body = await readJsonInput(parsed);
|
|
247
|
+
const response = await requestAgentApi(parsed, {
|
|
248
|
+
method: 'PATCH',
|
|
249
|
+
path: basePath,
|
|
250
|
+
body,
|
|
251
|
+
});
|
|
252
|
+
return response.payload;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (subcommand === 'credits') {
|
|
256
|
+
const response = await requestAgentApi(parsed, {
|
|
257
|
+
method: 'GET',
|
|
258
|
+
path: `${basePath}/credits`,
|
|
259
|
+
});
|
|
260
|
+
return response.payload;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (subcommand === 'earned') {
|
|
264
|
+
const path = buildQueryPath(`${basePath}/earned`, {
|
|
265
|
+
limit: parseIntegerOption(parsed, 'limit', BACKLINK_LIST_LIMIT_RANGE),
|
|
266
|
+
cursor: getOption(parsed, 'cursor'),
|
|
267
|
+
status: getOption(parsed, 'status'),
|
|
268
|
+
sort_by: getOption(parsed, 'sort-by'),
|
|
269
|
+
sort_order: getOption(parsed, 'sort-order'),
|
|
270
|
+
include_article: hasFlag(parsed, 'include-article') ? true : undefined,
|
|
271
|
+
include_summary: hasFlag(parsed, 'include-summary') ? true : undefined,
|
|
272
|
+
});
|
|
273
|
+
const response = await requestAgentApi(parsed, {
|
|
274
|
+
method: 'GET',
|
|
275
|
+
path,
|
|
276
|
+
});
|
|
277
|
+
return response.payload;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (subcommand === 'unverified') {
|
|
281
|
+
const path = buildQueryPath(`${basePath}/unverified`, {
|
|
282
|
+
limit: parseIntegerOption(parsed, 'limit', BACKLINK_LIST_LIMIT_RANGE),
|
|
283
|
+
cursor: getOption(parsed, 'cursor'),
|
|
284
|
+
status: getOption(parsed, 'status'),
|
|
285
|
+
sort_by: getOption(parsed, 'sort-by'),
|
|
286
|
+
sort_order: getOption(parsed, 'sort-order'),
|
|
287
|
+
});
|
|
288
|
+
const response = await requestAgentApi(parsed, {
|
|
289
|
+
method: 'GET',
|
|
290
|
+
path,
|
|
291
|
+
});
|
|
292
|
+
return response.payload;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (subcommand === 'check-removal') {
|
|
296
|
+
const backlinkId = getRequiredPositional(parsed, 1, '<backlink-id>');
|
|
297
|
+
const response = await requestAgentApi(parsed, {
|
|
298
|
+
method: 'POST',
|
|
299
|
+
path: `${basePath}/${backlinkId}/check-removal`,
|
|
300
|
+
});
|
|
301
|
+
return response.payload;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (subcommand === 'reverify') {
|
|
305
|
+
const backlinkId = getRequiredPositional(parsed, 1, '<backlink-id>');
|
|
306
|
+
const response = await requestAgentApi(parsed, {
|
|
307
|
+
method: 'POST',
|
|
308
|
+
path: `${basePath}/${backlinkId}/reverify`,
|
|
309
|
+
});
|
|
310
|
+
return response.payload;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (subcommand === 'subscription') {
|
|
314
|
+
const response = await requestAgentApi(parsed, {
|
|
315
|
+
method: 'GET',
|
|
316
|
+
path: `${basePath}/subscription`,
|
|
317
|
+
});
|
|
318
|
+
return response.payload;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (subcommand === 'subscribe-quote') {
|
|
322
|
+
const plan = getOption(parsed, 'plan');
|
|
323
|
+
|
|
324
|
+
if (!plan) {
|
|
325
|
+
throw Object.assign(new Error('Missing required option: --plan premium|enterprise.'), {
|
|
326
|
+
cliCode: 'CLI_INVALID_USAGE',
|
|
327
|
+
guidance:
|
|
328
|
+
'Run `outrank-cli products backlinks subscribe-quote <product-id> --plan premium|enterprise` to inspect the dashboard-equivalent quote.',
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const response = await requestAgentApi(parsed, {
|
|
333
|
+
method: 'GET',
|
|
334
|
+
path: buildQueryPath(`${basePath}/subscribe-quote`, {
|
|
335
|
+
plan,
|
|
336
|
+
}),
|
|
337
|
+
});
|
|
338
|
+
return response.payload;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
throw Object.assign(new Error(`Unknown products backlinks subcommand: ${subcommand}.`), {
|
|
342
|
+
cliCode: 'CLI_INVALID_USAGE',
|
|
343
|
+
guidance: 'Run `outrank-cli products backlinks --help` to see the available backlink commands.',
|
|
344
|
+
});
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
const handleOutputSettings = async (argv) => {
|
|
348
|
+
const subcommand = argv[0];
|
|
349
|
+
const parsed = parseArgs(argv.slice(1));
|
|
350
|
+
const wantsHelp = !subcommand || subcommand === '--help' || subcommand === '-h' || subcommand === 'help' || hasFlag(parsed, 'help');
|
|
351
|
+
|
|
124
352
|
if (wantsHelp) {
|
|
125
353
|
return cliSuccess(
|
|
126
354
|
{
|
|
@@ -158,6 +386,274 @@ const handleOutputSettings = async (argv) => {
|
|
|
158
386
|
});
|
|
159
387
|
};
|
|
160
388
|
|
|
389
|
+
const handleIntegrations = async (argv) => {
|
|
390
|
+
const subcommand = argv[0];
|
|
391
|
+
const parsed = parseArgs(argv.slice(1));
|
|
392
|
+
const wantsHelp =
|
|
393
|
+
!subcommand ||
|
|
394
|
+
subcommand === '--help' ||
|
|
395
|
+
subcommand === '-h' ||
|
|
396
|
+
subcommand === 'help' ||
|
|
397
|
+
((subcommand !== 'notion' && subcommand !== 'webflow' && subcommand !== 'framer') && hasFlag(parsed, 'help'));
|
|
398
|
+
|
|
399
|
+
if (wantsHelp) {
|
|
400
|
+
return cliSuccess(
|
|
401
|
+
{
|
|
402
|
+
usage: `Usage:
|
|
403
|
+
outrank-cli products integrations providers
|
|
404
|
+
outrank-cli products integrations prepare <provider> <product-id> [--json <object>]
|
|
405
|
+
outrank-cli products integrations connect <provider> <product-id> [--json <object>]
|
|
406
|
+
outrank-cli products integrations poll <provider> <product-id> --attempt-id <attempt-id>
|
|
407
|
+
outrank-cli products integrations notion databases <product-id> <integration-id>
|
|
408
|
+
outrank-cli products integrations notion schema <product-id> <integration-id> <database-id>
|
|
409
|
+
outrank-cli products integrations framer collections --json <object>
|
|
410
|
+
outrank-cli products integrations framer fields --json <object>
|
|
411
|
+
outrank-cli products integrations webflow sites --json <object>
|
|
412
|
+
outrank-cli products integrations webflow collections --json <object>
|
|
413
|
+
outrank-cli products integrations webflow fields --json <object>
|
|
414
|
+
outrank-cli products integrations webflow items --json <object>
|
|
415
|
+
outrank-cli products integrations validate <provider> --json <object>
|
|
416
|
+
outrank-cli products integrations list <product-id>
|
|
417
|
+
outrank-cli products integrations create <product-id> --json <object>
|
|
418
|
+
outrank-cli products integrations get <product-id> <integration-id>
|
|
419
|
+
outrank-cli products integrations update <product-id> <integration-id> --json <object>
|
|
420
|
+
outrank-cli products integrations test <product-id> <integration-id>
|
|
421
|
+
outrank-cli products integrations delete <product-id> <integration-id>`,
|
|
422
|
+
},
|
|
423
|
+
'Use `providers` to inspect known provider availability, auth method, required connect/create fields, and provider notes. For WordPress Plugin, start with `prepare` to generate the integration token, then validate and create. For WordPress.com and Notion, use `connect`, have the human open the returned connect_url, then `poll` until the OAuth handoff completes. Notion exposes `notion databases` and `notion schema` discovery commands. Framer exposes `framer collections` and `framer fields` discovery for the newer Server API-based setup, not the older plugin flow. Webflow exposes `webflow sites`, `webflow collections`, `webflow fields`, and `webflow items` discovery before create or update. Shopify validation returns the resolved `shop_name` plus available blogs, so the human should only need to provide the access token before blog selection. Wix is a direct validate/create/update/test flow using `site_id`, `api_key`, and `publish_status`. Next.js Blog is a one-step create flow that returns a one-time API key in the response. V2 products are intended to keep one publishing integration; multiple rows usually indicate legacy cleanup work.',
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (subcommand === 'providers') {
|
|
428
|
+
const response = await requestAgentApi(parsed, {
|
|
429
|
+
method: 'GET',
|
|
430
|
+
path: '/integration-providers',
|
|
431
|
+
});
|
|
432
|
+
return response.payload;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (subcommand === 'prepare') {
|
|
436
|
+
const provider = getRequiredPositional(parsed, 0, '<provider>');
|
|
437
|
+
const productId = getRequiredPositional(parsed, 1, '<product-id>');
|
|
438
|
+
const hasJsonInput =
|
|
439
|
+
getOption(parsed, 'json') !== undefined ||
|
|
440
|
+
getOption(parsed, 'json-file') !== undefined ||
|
|
441
|
+
hasFlag(parsed, 'json-stdin');
|
|
442
|
+
const body = hasJsonInput ? await readJsonInput(parsed) : undefined;
|
|
443
|
+
const response = await requestAgentApi(parsed, {
|
|
444
|
+
method: 'POST',
|
|
445
|
+
path: `/products/${productId}/integrations/prepare/${provider}`,
|
|
446
|
+
...(body !== undefined ? { body } : {}),
|
|
447
|
+
});
|
|
448
|
+
return response.payload;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (subcommand === 'connect') {
|
|
452
|
+
const provider = getRequiredPositional(parsed, 0, '<provider>');
|
|
453
|
+
const productId = getRequiredPositional(parsed, 1, '<product-id>');
|
|
454
|
+
const hasJsonInput =
|
|
455
|
+
getOption(parsed, 'json') !== undefined ||
|
|
456
|
+
getOption(parsed, 'json-file') !== undefined ||
|
|
457
|
+
hasFlag(parsed, 'json-stdin');
|
|
458
|
+
const body = hasJsonInput ? await readJsonInput(parsed) : undefined;
|
|
459
|
+
const response = await requestAgentApi(parsed, {
|
|
460
|
+
method: 'POST',
|
|
461
|
+
path: `/products/${productId}/integrations/connect/${provider}`,
|
|
462
|
+
...(body !== undefined ? { body } : {}),
|
|
463
|
+
});
|
|
464
|
+
return response.payload;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (subcommand === 'poll') {
|
|
468
|
+
const provider = getRequiredPositional(parsed, 0, '<provider>');
|
|
469
|
+
const productId = getRequiredPositional(parsed, 1, '<product-id>');
|
|
470
|
+
const attemptId = getOption(parsed, 'attempt-id');
|
|
471
|
+
|
|
472
|
+
if (!attemptId) {
|
|
473
|
+
throw Object.assign(new Error('Missing required option: --attempt-id <attempt-id>.'), {
|
|
474
|
+
cliCode: 'CLI_INVALID_USAGE',
|
|
475
|
+
guidance:
|
|
476
|
+
'Run `outrank-cli products integrations poll <provider> <product-id> --attempt-id <attempt-id>` after connect starts.',
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const response = await requestAgentApi(parsed, {
|
|
481
|
+
method: 'GET',
|
|
482
|
+
path: buildQueryPath(`/products/${productId}/integrations/connect/${provider}`, {
|
|
483
|
+
attempt_id: attemptId,
|
|
484
|
+
}),
|
|
485
|
+
});
|
|
486
|
+
return response.payload;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (subcommand === 'notion') {
|
|
490
|
+
const notionSubcommand = argv[1];
|
|
491
|
+
const notionParsed = parseArgs(argv.slice(2));
|
|
492
|
+
|
|
493
|
+
if (!notionSubcommand || notionSubcommand === '--help' || notionSubcommand === '-h' || notionSubcommand === 'help') {
|
|
494
|
+
return cliSuccess(
|
|
495
|
+
{
|
|
496
|
+
usage: `Usage:
|
|
497
|
+
outrank-cli products integrations notion databases <product-id> <integration-id>
|
|
498
|
+
outrank-cli products integrations notion schema <product-id> <integration-id> <database-id>`,
|
|
499
|
+
},
|
|
500
|
+
'Use `databases` after Notion OAuth completes to list accessible Notion databases, then use `schema` to inspect one database before PATCHing the saved integration row with name, database_id, and fields.',
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
const productId = getRequiredPositional(notionParsed, 0, '<product-id>');
|
|
505
|
+
const integrationId = getRequiredPositional(notionParsed, 1, '<integration-id>');
|
|
506
|
+
|
|
507
|
+
if (notionSubcommand === 'databases') {
|
|
508
|
+
const response = await requestAgentApi(notionParsed, {
|
|
509
|
+
method: 'GET',
|
|
510
|
+
path: `/products/${productId}/integrations/${integrationId}/notion/databases`,
|
|
511
|
+
});
|
|
512
|
+
return response.payload;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (notionSubcommand === 'schema') {
|
|
516
|
+
const databaseId = getRequiredPositional(notionParsed, 2, '<database-id>');
|
|
517
|
+
const response = await requestAgentApi(notionParsed, {
|
|
518
|
+
method: 'GET',
|
|
519
|
+
path: `/products/${productId}/integrations/${integrationId}/notion/databases/${databaseId}/schema`,
|
|
520
|
+
});
|
|
521
|
+
return response.payload;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
throw Object.assign(new Error(`Unknown products integrations notion subcommand: ${notionSubcommand}.`), {
|
|
525
|
+
cliCode: 'CLI_INVALID_USAGE',
|
|
526
|
+
guidance:
|
|
527
|
+
'Run `outrank-cli products integrations notion --help` to see the available Notion discovery commands.',
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (subcommand === 'webflow') {
|
|
532
|
+
const webflowSubcommand = argv[1];
|
|
533
|
+
const webflowParsed = parseArgs(argv.slice(2));
|
|
534
|
+
|
|
535
|
+
if (!webflowSubcommand || webflowSubcommand === '--help' || webflowSubcommand === '-h' || webflowSubcommand === 'help') {
|
|
536
|
+
return cliSuccess(
|
|
537
|
+
{
|
|
538
|
+
usage: `Usage:
|
|
539
|
+
outrank-cli products integrations webflow sites --json <object>
|
|
540
|
+
outrank-cli products integrations webflow collections --json <object>
|
|
541
|
+
outrank-cli products integrations webflow fields --json <object>
|
|
542
|
+
outrank-cli products integrations webflow items --json <object>`,
|
|
543
|
+
},
|
|
544
|
+
'Use `sites` first to validate a Webflow token and list accessible sites, then `collections`, `fields`, and `items` to build the exact create or update payload for the chosen CMS collection.',
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const body = await readJsonInput(webflowParsed);
|
|
549
|
+
const response = await requestAgentApi(webflowParsed, {
|
|
550
|
+
method: 'POST',
|
|
551
|
+
path: `/integration-providers/webflow/discover/${webflowSubcommand}`,
|
|
552
|
+
body,
|
|
553
|
+
});
|
|
554
|
+
return response.payload;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (subcommand === 'framer') {
|
|
558
|
+
const framerSubcommand = argv[1];
|
|
559
|
+
const framerParsed = parseArgs(argv.slice(2));
|
|
560
|
+
|
|
561
|
+
if (!framerSubcommand || framerSubcommand === '--help' || framerSubcommand === '-h' || framerSubcommand === 'help') {
|
|
562
|
+
return cliSuccess(
|
|
563
|
+
{
|
|
564
|
+
usage: `Usage:
|
|
565
|
+
outrank-cli products integrations framer collections --json <object>
|
|
566
|
+
outrank-cli products integrations framer fields --json <object>`,
|
|
567
|
+
},
|
|
568
|
+
'Use `collections` first with the Framer project URL and Server API key. Then use `fields` for one chosen collection if you want `collection_setup_mode: "existing"` instead of auto-creating a new collection.',
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const body = await readJsonInput(framerParsed);
|
|
573
|
+
const response = await requestAgentApi(framerParsed, {
|
|
574
|
+
method: 'POST',
|
|
575
|
+
path: `/integration-providers/framer/discover/${framerSubcommand}`,
|
|
576
|
+
body,
|
|
577
|
+
});
|
|
578
|
+
return response.payload;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (subcommand === 'validate') {
|
|
582
|
+
const provider = getRequiredPositional(parsed, 0, '<provider>');
|
|
583
|
+
const body = await readJsonInput(parsed);
|
|
584
|
+
const response = await requestAgentApi(parsed, {
|
|
585
|
+
method: 'POST',
|
|
586
|
+
path: `/integration-providers/${provider}/validate`,
|
|
587
|
+
body,
|
|
588
|
+
});
|
|
589
|
+
return response.payload;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const productId = getRequiredPositional(parsed, 0, '<product-id>');
|
|
593
|
+
|
|
594
|
+
if (subcommand === 'list') {
|
|
595
|
+
const response = await requestAgentApi(parsed, {
|
|
596
|
+
method: 'GET',
|
|
597
|
+
path: `/products/${productId}/integrations`,
|
|
598
|
+
});
|
|
599
|
+
return response.payload;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
if (subcommand === 'create') {
|
|
603
|
+
const body = await readJsonInput(parsed);
|
|
604
|
+
const response = await requestAgentApi(parsed, {
|
|
605
|
+
method: 'POST',
|
|
606
|
+
path: `/products/${productId}/integrations`,
|
|
607
|
+
body,
|
|
608
|
+
});
|
|
609
|
+
return response.payload;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
if (subcommand === 'get') {
|
|
613
|
+
const integrationId = getRequiredPositional(parsed, 1, '<integration-id>');
|
|
614
|
+
const response = await requestAgentApi(parsed, {
|
|
615
|
+
method: 'GET',
|
|
616
|
+
path: `/products/${productId}/integrations/${integrationId}`,
|
|
617
|
+
});
|
|
618
|
+
return response.payload;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
if (subcommand === 'update') {
|
|
622
|
+
const integrationId = getRequiredPositional(parsed, 1, '<integration-id>');
|
|
623
|
+
const body = await readJsonInput(parsed);
|
|
624
|
+
const response = await requestAgentApi(parsed, {
|
|
625
|
+
method: 'PATCH',
|
|
626
|
+
path: `/products/${productId}/integrations/${integrationId}`,
|
|
627
|
+
body,
|
|
628
|
+
});
|
|
629
|
+
return response.payload;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
if (subcommand === 'test') {
|
|
633
|
+
const integrationId = getRequiredPositional(parsed, 1, '<integration-id>');
|
|
634
|
+
const response = await requestAgentApi(parsed, {
|
|
635
|
+
method: 'POST',
|
|
636
|
+
path: `/products/${productId}/integrations/${integrationId}/test`,
|
|
637
|
+
});
|
|
638
|
+
return response.payload;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
if (subcommand === 'delete') {
|
|
642
|
+
const integrationId = getRequiredPositional(parsed, 1, '<integration-id>');
|
|
643
|
+
const response = await requestAgentApi(parsed, {
|
|
644
|
+
method: 'DELETE',
|
|
645
|
+
path: `/products/${productId}/integrations/${integrationId}`,
|
|
646
|
+
});
|
|
647
|
+
return response.payload;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
throw Object.assign(new Error(`Unknown products integrations subcommand: ${subcommand}.`), {
|
|
651
|
+
cliCode: 'CLI_INVALID_USAGE',
|
|
652
|
+
guidance:
|
|
653
|
+
'Run `outrank-cli products integrations --help` to see the available integration commands.',
|
|
654
|
+
});
|
|
655
|
+
};
|
|
656
|
+
|
|
161
657
|
const handleCompetitors = async (argv) => {
|
|
162
658
|
const subcommand = argv[0];
|
|
163
659
|
const parsed = parseArgs(argv.slice(1));
|
|
@@ -444,6 +940,216 @@ const handleGoogleSearchConsole = async (argv) => {
|
|
|
444
940
|
});
|
|
445
941
|
};
|
|
446
942
|
|
|
943
|
+
const handleLinkingSources = async (argv) => {
|
|
944
|
+
const subcommand = argv[0];
|
|
945
|
+
const parsed = parseArgs(argv.slice(1));
|
|
946
|
+
const wantsHelp =
|
|
947
|
+
!subcommand || subcommand === '--help' || subcommand === '-h' || subcommand === 'help' || hasFlag(parsed, 'help');
|
|
948
|
+
|
|
949
|
+
if (wantsHelp) {
|
|
950
|
+
return cliSuccess(
|
|
951
|
+
{
|
|
952
|
+
usage:
|
|
953
|
+
'Usage:\n outrank-cli products linking sources get <product-id>\n outrank-cli products linking sources update <product-id> --json <object>',
|
|
954
|
+
},
|
|
955
|
+
'Use `get` to inspect current linking sources and `update` to change the primary or additional sources.',
|
|
956
|
+
);
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
const productId = getRequiredPositional(parsed, 0, '<product-id>');
|
|
960
|
+
|
|
961
|
+
if (subcommand === 'get') {
|
|
962
|
+
const response = await requestAgentApi(parsed, {
|
|
963
|
+
method: 'GET',
|
|
964
|
+
path: `/products/${productId}/linking/sources`,
|
|
965
|
+
});
|
|
966
|
+
return response.payload;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
if (subcommand === 'update' || subcommand === 'set') {
|
|
970
|
+
const body = await readJsonInput(parsed);
|
|
971
|
+
const response = await requestAgentApi(parsed, {
|
|
972
|
+
method: 'PATCH',
|
|
973
|
+
path: `/products/${productId}/linking/sources`,
|
|
974
|
+
body,
|
|
975
|
+
});
|
|
976
|
+
return response.payload;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
throw Object.assign(new Error(`Unknown products linking sources subcommand: ${subcommand}.`), {
|
|
980
|
+
cliCode: 'CLI_INVALID_USAGE',
|
|
981
|
+
guidance: 'Run `outrank-cli products linking sources --help` to see available linking-source commands.',
|
|
982
|
+
});
|
|
983
|
+
};
|
|
984
|
+
|
|
985
|
+
const handleLinkingTargetConfig = async (argv) => {
|
|
986
|
+
const subcommand = argv[0];
|
|
987
|
+
const parsed = parseArgs(argv.slice(1));
|
|
988
|
+
const wantsHelp =
|
|
989
|
+
!subcommand || subcommand === '--help' || subcommand === '-h' || subcommand === 'help' || hasFlag(parsed, 'help');
|
|
990
|
+
|
|
991
|
+
if (wantsHelp) {
|
|
992
|
+
return cliSuccess(
|
|
993
|
+
{
|
|
994
|
+
usage:
|
|
995
|
+
'Usage:\n outrank-cli products linking target-config get <product-id>\n outrank-cli products linking target-config update <product-id> --json <object>',
|
|
996
|
+
},
|
|
997
|
+
'Use `get` to inspect product-wide linking target behavior and `update` to change it.',
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
const productId = getRequiredPositional(parsed, 0, '<product-id>');
|
|
1002
|
+
|
|
1003
|
+
if (subcommand === 'get') {
|
|
1004
|
+
const response = await requestAgentApi(parsed, {
|
|
1005
|
+
method: 'GET',
|
|
1006
|
+
path: `/products/${productId}/linking/target-config`,
|
|
1007
|
+
});
|
|
1008
|
+
return response.payload;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
if (subcommand === 'update') {
|
|
1012
|
+
const body = await readJsonInput(parsed);
|
|
1013
|
+
const response = await requestAgentApi(parsed, {
|
|
1014
|
+
method: 'PUT',
|
|
1015
|
+
path: `/products/${productId}/linking/target-config`,
|
|
1016
|
+
body,
|
|
1017
|
+
});
|
|
1018
|
+
return response.payload;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
throw Object.assign(new Error(`Unknown products linking target-config subcommand: ${subcommand}.`), {
|
|
1022
|
+
cliCode: 'CLI_INVALID_USAGE',
|
|
1023
|
+
guidance:
|
|
1024
|
+
'Run `outrank-cli products linking target-config --help` to see available target-config commands.',
|
|
1025
|
+
});
|
|
1026
|
+
};
|
|
1027
|
+
|
|
1028
|
+
const handleLinkingTargets = async (argv) => {
|
|
1029
|
+
const subcommand = argv[0];
|
|
1030
|
+
const parsed = parseArgs(argv.slice(1));
|
|
1031
|
+
const wantsHelp =
|
|
1032
|
+
!subcommand || subcommand === '--help' || subcommand === '-h' || subcommand === 'help' || hasFlag(parsed, 'help');
|
|
1033
|
+
|
|
1034
|
+
if (wantsHelp) {
|
|
1035
|
+
return cliSuccess(
|
|
1036
|
+
{
|
|
1037
|
+
usage: `Usage:
|
|
1038
|
+
outrank-cli products linking targets list <product-id>
|
|
1039
|
+
outrank-cli products linking targets add-manual <product-id> --json <object>
|
|
1040
|
+
outrank-cli products linking targets upsert <product-id> --json <object>
|
|
1041
|
+
outrank-cli products linking targets clear-customizations <product-id> <target-id>
|
|
1042
|
+
outrank-cli products linking targets delete <product-id> <target-id>`,
|
|
1043
|
+
},
|
|
1044
|
+
'Use `list` to inspect saved targets, `add-manual` or `upsert` to save URLs, and clear/delete for cleanup.',
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
const productId = getRequiredPositional(parsed, 0, '<product-id>');
|
|
1049
|
+
|
|
1050
|
+
if (subcommand === 'list') {
|
|
1051
|
+
const response = await requestAgentApi(parsed, {
|
|
1052
|
+
method: 'GET',
|
|
1053
|
+
path: `/products/${productId}/linking/targets`,
|
|
1054
|
+
});
|
|
1055
|
+
return response.payload;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
if (subcommand === 'add-manual') {
|
|
1059
|
+
const body = await readJsonInput(parsed);
|
|
1060
|
+
const response = await requestAgentApi(parsed, {
|
|
1061
|
+
method: 'POST',
|
|
1062
|
+
path: `/products/${productId}/linking/targets/manual`,
|
|
1063
|
+
body,
|
|
1064
|
+
});
|
|
1065
|
+
return response.payload;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
if (subcommand === 'upsert') {
|
|
1069
|
+
const body = await readJsonInput(parsed);
|
|
1070
|
+
const response = await requestAgentApi(parsed, {
|
|
1071
|
+
method: 'POST',
|
|
1072
|
+
path: `/products/${productId}/linking/targets/upsert`,
|
|
1073
|
+
body,
|
|
1074
|
+
});
|
|
1075
|
+
return response.payload;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
if (subcommand === 'clear-customizations') {
|
|
1079
|
+
const targetId = getRequiredPositional(parsed, 1, '<target-id>');
|
|
1080
|
+
const response = await requestAgentApi(parsed, {
|
|
1081
|
+
method: 'PATCH',
|
|
1082
|
+
path: `/products/${productId}/linking/targets/${targetId}`,
|
|
1083
|
+
});
|
|
1084
|
+
return response.payload;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
if (subcommand === 'delete') {
|
|
1088
|
+
const targetId = getRequiredPositional(parsed, 1, '<target-id>');
|
|
1089
|
+
const response = await requestAgentApi(parsed, {
|
|
1090
|
+
method: 'DELETE',
|
|
1091
|
+
path: `/products/${productId}/linking/targets/${targetId}`,
|
|
1092
|
+
});
|
|
1093
|
+
return response.payload;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
throw Object.assign(new Error(`Unknown products linking targets subcommand: ${subcommand}.`), {
|
|
1097
|
+
cliCode: 'CLI_INVALID_USAGE',
|
|
1098
|
+
guidance: 'Run `outrank-cli products linking targets --help` to see available linking-target commands.',
|
|
1099
|
+
});
|
|
1100
|
+
};
|
|
1101
|
+
|
|
1102
|
+
const handleLinking = async (argv) => {
|
|
1103
|
+
const subcommand = argv[0];
|
|
1104
|
+
const parsed = parseArgs(argv.slice(1));
|
|
1105
|
+
const wantsHelp = !subcommand || subcommand === '--help' || subcommand === '-h' || subcommand === 'help';
|
|
1106
|
+
|
|
1107
|
+
if (wantsHelp) {
|
|
1108
|
+
return cliSuccess(
|
|
1109
|
+
{
|
|
1110
|
+
usage: `Usage:
|
|
1111
|
+
outrank-cli products linking sources get <product-id>
|
|
1112
|
+
outrank-cli products linking sources update <product-id> --json <object>
|
|
1113
|
+
outrank-cli products linking detect <product-id>
|
|
1114
|
+
outrank-cli products linking target-config get <product-id>
|
|
1115
|
+
outrank-cli products linking target-config update <product-id> --json <object>
|
|
1116
|
+
outrank-cli products linking targets list <product-id>
|
|
1117
|
+
outrank-cli products linking targets add-manual <product-id> --json <object>
|
|
1118
|
+
outrank-cli products linking targets upsert <product-id> --json <object>
|
|
1119
|
+
outrank-cli products linking targets clear-customizations <product-id> <target-id>
|
|
1120
|
+
outrank-cli products linking targets delete <product-id> <target-id>`,
|
|
1121
|
+
},
|
|
1122
|
+
'Use `sources` for source selection, `detect` for discovery, `targets` for saved URLs, and `target-config` for product-wide rules.',
|
|
1123
|
+
);
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
if (subcommand === 'sources') {
|
|
1127
|
+
return handleLinkingSources(argv.slice(1));
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
if (subcommand === 'detect') {
|
|
1131
|
+
const productId = getRequiredPositional(parsed, 0, '<product-id>');
|
|
1132
|
+
const response = await requestAgentApi(parsed, {
|
|
1133
|
+
method: 'POST',
|
|
1134
|
+
path: `/products/${productId}/linking/detect`,
|
|
1135
|
+
});
|
|
1136
|
+
return response.payload;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
if (subcommand === 'target-config') {
|
|
1140
|
+
return handleLinkingTargetConfig(argv.slice(1));
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
if (subcommand === 'targets') {
|
|
1144
|
+
return handleLinkingTargets(argv.slice(1));
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
throw Object.assign(new Error(`Unknown products linking subcommand: ${subcommand}.`), {
|
|
1148
|
+
cliCode: 'CLI_INVALID_USAGE',
|
|
1149
|
+
guidance: 'Run `outrank-cli products linking --help` to see available linking commands.',
|
|
1150
|
+
});
|
|
1151
|
+
};
|
|
1152
|
+
|
|
447
1153
|
export const handleProductsCommand = async (argv) => {
|
|
448
1154
|
const subcommand = argv[0];
|
|
449
1155
|
const parsed = parseArgs(argv.slice(1));
|
|
@@ -454,7 +1160,7 @@ export const handleProductsCommand = async (argv) => {
|
|
|
454
1160
|
{
|
|
455
1161
|
usage: PRODUCTS_HELP.trim(),
|
|
456
1162
|
},
|
|
457
|
-
'Choose one of: list, get, create, update, pause, resume, output-settings, target-audiences, competitors, gsc.',
|
|
1163
|
+
'Choose one of: list, get, create, update, pause, resume, integrations, output-settings, target-audiences, competitors, gsc, linking, backlinks.',
|
|
458
1164
|
);
|
|
459
1165
|
}
|
|
460
1166
|
|
|
@@ -464,10 +1170,13 @@ export const handleProductsCommand = async (argv) => {
|
|
|
464
1170
|
if (subcommand === 'update') return handleProductsUpdate(parsed);
|
|
465
1171
|
if (subcommand === 'pause') return handleProductsPause(parsed);
|
|
466
1172
|
if (subcommand === 'resume') return handleProductsResume(parsed);
|
|
1173
|
+
if (subcommand === 'integrations') return handleIntegrations(argv.slice(1));
|
|
467
1174
|
if (subcommand === 'output-settings') return handleOutputSettings(argv.slice(1));
|
|
468
1175
|
if (subcommand === 'target-audiences') return handleTargetAudiences(argv.slice(1));
|
|
469
1176
|
if (subcommand === 'competitors') return handleCompetitors(argv.slice(1));
|
|
470
1177
|
if (subcommand === 'gsc') return handleGoogleSearchConsole(argv.slice(1));
|
|
1178
|
+
if (subcommand === 'linking') return handleLinking(argv.slice(1));
|
|
1179
|
+
if (subcommand === 'backlinks') return handleBacklinks(argv.slice(1));
|
|
471
1180
|
|
|
472
1181
|
throw Object.assign(new Error(`Unknown products subcommand: ${subcommand}.`), {
|
|
473
1182
|
cliCode: 'CLI_INVALID_USAGE',
|