outrank-cli 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +317 -20
- package/SKILL.md +20 -2
- package/bin/outrank-mcp.js +3 -0
- package/package.json +21 -5
- package/src/adapters/cli/jsonInput.js +42 -0
- package/src/adapters/cli/parse.js +124 -0
- package/src/adapters/cli/run.js +139 -0
- package/src/adapters/mcp/constants.js +49 -0
- package/src/adapters/mcp/descriptions.js +87 -0
- package/src/adapters/mcp/help.js +34 -0
- package/src/adapters/mcp/input.js +195 -0
- package/src/adapters/mcp/instructions.js +139 -0
- package/src/adapters/mcp/logger.js +45 -0
- package/src/adapters/mcp/main.js +36 -0
- package/src/adapters/mcp/options.js +118 -0
- package/src/adapters/mcp/result.js +33 -0
- package/src/adapters/mcp/server.js +174 -0
- package/src/adapters/mcp/session.js +274 -0
- package/src/adapters/mcp/toolNotes.js +426 -0
- package/src/adapters/mcp/toolsets.js +106 -0
- package/src/commands/articles.js +285 -301
- package/src/commands/auth.js +204 -86
- package/src/commands/billing.js +102 -104
- package/src/commands/improvements.js +319 -211
- package/src/commands/index.js +22 -0
- package/src/commands/keywords.js +266 -249
- package/src/commands/marketplace/buyer.js +386 -0
- package/src/commands/marketplace/index.js +83 -0
- package/src/commands/marketplace/notifications.js +60 -0
- package/src/commands/marketplace/seller.js +595 -0
- package/src/commands/marketplace/shared.js +27 -0
- package/src/commands/products/backlinks.js +181 -0
- package/src/commands/products/core.js +149 -0
- package/src/commands/products/gsc.js +250 -0
- package/src/commands/products/index.js +174 -0
- package/src/commands/products/integrations.js +343 -0
- package/src/commands/products/linking.js +192 -0
- package/src/commands/products/shared.js +8 -0
- package/src/commands/registry.js +272 -0
- package/src/commands/subscription.js +20 -30
- package/src/commands/usage.js +17 -30
- package/src/core/config.js +243 -62
- package/src/core/constants.js +101 -0
- package/src/core/context.js +311 -0
- package/src/core/errors.js +39 -0
- package/src/core/execute.js +71 -0
- package/src/core/files.js +186 -0
- package/src/core/http.js +393 -0
- package/src/core/profileSelection.js +7 -0
- package/src/core/query.js +11 -0
- package/src/core/redact.js +27 -0
- package/src/core/schema.js +222 -0
- package/src/core/version.js +12 -0
- package/src/index.js +30 -58
- package/src/commands/marketplace.js +0 -740
- package/src/commands/products.js +0 -1185
- package/src/core/api.js +0 -82
- package/src/core/args.js +0 -144
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { defineGroup } from '../registry.js';
|
|
2
|
+
import { backlinksGroup } from './backlinks.js';
|
|
3
|
+
import {
|
|
4
|
+
competitorsGroup,
|
|
5
|
+
outputSettingsGroup,
|
|
6
|
+
productsCreate,
|
|
7
|
+
productsGet,
|
|
8
|
+
productsList,
|
|
9
|
+
productsPause,
|
|
10
|
+
productsResume,
|
|
11
|
+
productsUpdate,
|
|
12
|
+
targetAudiencesGroup,
|
|
13
|
+
} from './core.js';
|
|
14
|
+
import { gscGroup } from './gsc.js';
|
|
15
|
+
import { integrationsGroup } from './integrations.js';
|
|
16
|
+
import { linkingGroup } from './linking.js';
|
|
17
|
+
|
|
18
|
+
export const PRODUCTS_HELP = `Usage:
|
|
19
|
+
outrank-cli products list [--status active|disabled] [--limit <n>] [--cursor <iso-timestamp>]
|
|
20
|
+
outrank-cli products get <product-id>
|
|
21
|
+
outrank-cli products create --json <object>
|
|
22
|
+
outrank-cli products update <product-id> --json <object>
|
|
23
|
+
outrank-cli products pause <product-id>
|
|
24
|
+
outrank-cli products resume <product-id>
|
|
25
|
+
outrank-cli products integrations providers
|
|
26
|
+
outrank-cli products integrations prepare <provider> <product-id> [--json <object>]
|
|
27
|
+
outrank-cli products integrations connect <provider> <product-id> [--json <object>]
|
|
28
|
+
outrank-cli products integrations poll <provider> <product-id> --attempt-id <attempt-id>
|
|
29
|
+
outrank-cli products integrations notion databases <product-id> <integration-id>
|
|
30
|
+
outrank-cli products integrations notion schema <product-id> <integration-id> <database-id>
|
|
31
|
+
outrank-cli products integrations framer collections --json <object>
|
|
32
|
+
outrank-cli products integrations framer fields --json <object>
|
|
33
|
+
outrank-cli products integrations webflow sites --json <object>
|
|
34
|
+
outrank-cli products integrations webflow collections --json <object>
|
|
35
|
+
outrank-cli products integrations webflow fields --json <object>
|
|
36
|
+
outrank-cli products integrations webflow items --json <object>
|
|
37
|
+
outrank-cli products integrations validate <provider> --json <object>
|
|
38
|
+
outrank-cli products integrations list <product-id>
|
|
39
|
+
outrank-cli products integrations create <product-id> --json <object>
|
|
40
|
+
outrank-cli products integrations get <product-id> <integration-id>
|
|
41
|
+
outrank-cli products integrations update <product-id> <integration-id> --json <object>
|
|
42
|
+
outrank-cli products integrations test <product-id> <integration-id>
|
|
43
|
+
outrank-cli products integrations delete <product-id> <integration-id>
|
|
44
|
+
outrank-cli products output-settings get <product-id>
|
|
45
|
+
outrank-cli products output-settings update <product-id> --json <object>
|
|
46
|
+
outrank-cli products target-audiences list <product-id>
|
|
47
|
+
outrank-cli products target-audiences replace <product-id> --json <object>
|
|
48
|
+
outrank-cli products competitors list <product-id>
|
|
49
|
+
outrank-cli products competitors replace <product-id> --json <object>
|
|
50
|
+
outrank-cli products gsc status <product-id>
|
|
51
|
+
outrank-cli products gsc connect <product-id>
|
|
52
|
+
outrank-cli products gsc poll <product-id> --attempt-id <attempt-id>
|
|
53
|
+
outrank-cli products gsc reconnect <product-id>
|
|
54
|
+
outrank-cli products gsc disconnect <product-id>
|
|
55
|
+
outrank-cli products gsc accounts <product-id>
|
|
56
|
+
outrank-cli products gsc properties <product-id> --email <email>
|
|
57
|
+
outrank-cli products gsc set-property <product-id> --email <email> --site-url <url>
|
|
58
|
+
outrank-cli products gsc performance <product-id> [--months <n>]
|
|
59
|
+
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>]
|
|
60
|
+
outrank-cli products gsc inspect-url <product-id> --url <url> [--language-code <code>]
|
|
61
|
+
outrank-cli products gsc cannibalization <product-id>
|
|
62
|
+
outrank-cli products linking sources get <product-id>
|
|
63
|
+
outrank-cli products linking sources update <product-id> --json <object>
|
|
64
|
+
outrank-cli products linking detect <product-id>
|
|
65
|
+
outrank-cli products linking target-config get <product-id>
|
|
66
|
+
outrank-cli products linking target-config update <product-id> --json <object>
|
|
67
|
+
outrank-cli products linking targets list <product-id>
|
|
68
|
+
outrank-cli products linking targets add-manual <product-id> --json <object>
|
|
69
|
+
outrank-cli products linking targets upsert <product-id> --json <object>
|
|
70
|
+
outrank-cli products linking targets clear-customizations <product-id> <target-id>
|
|
71
|
+
outrank-cli products linking targets delete <product-id> <target-id>
|
|
72
|
+
outrank-cli products backlinks get <product-id>
|
|
73
|
+
outrank-cli products backlinks update <product-id> --json <object>
|
|
74
|
+
outrank-cli products backlinks credits <product-id>
|
|
75
|
+
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]
|
|
76
|
+
outrank-cli products backlinks unverified <product-id> [--limit <n>] [--cursor <iso>] [--status <status>] [--sort-by created_at|verification_error_reason] [--sort-order desc]
|
|
77
|
+
outrank-cli products backlinks check-removal <product-id> <backlink-id>
|
|
78
|
+
outrank-cli products backlinks reverify <product-id> <backlink-id>
|
|
79
|
+
outrank-cli products backlinks subscription <product-id>
|
|
80
|
+
outrank-cli products backlinks subscribe-quote <product-id> --plan premium|enterprise
|
|
81
|
+
|
|
82
|
+
JSON Input:
|
|
83
|
+
Use one of: --json '<object>', --json-file payload.json, or --json-stdin
|
|
84
|
+
|
|
85
|
+
Examples:
|
|
86
|
+
outrank-cli products list --limit 20
|
|
87
|
+
outrank-cli products create --json-file product.json
|
|
88
|
+
outrank-cli products update 00000000-0000-0000-0000-000000000000 --json '{"daily_article_count":2}'
|
|
89
|
+
outrank-cli products integrations providers
|
|
90
|
+
outrank-cli products integrations prepare wordpress-plugin 00000000-0000-0000-0000-000000000000
|
|
91
|
+
outrank-cli products integrations connect wordpress-com 00000000-0000-0000-0000-000000000000 --json '{"name":"Main WordPress.com","publish_status":"publish"}'
|
|
92
|
+
outrank-cli products integrations connect notion 00000000-0000-0000-0000-000000000000
|
|
93
|
+
outrank-cli products integrations poll wordpress-com 00000000-0000-0000-0000-000000000000 --attempt-id <attempt-id>
|
|
94
|
+
outrank-cli products integrations notion databases 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111
|
|
95
|
+
outrank-cli products integrations notion schema 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111 aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
|
|
96
|
+
outrank-cli products integrations framer collections --json '{"project_url":"https://framer.com/projects/your-project","server_api_key":"framer_server_key"}'
|
|
97
|
+
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"}'
|
|
98
|
+
outrank-cli products integrations webflow sites --json-file webflow-sites.json
|
|
99
|
+
outrank-cli products integrations webflow collections --json '{"api_token":"wf_token","site_id":"site_id"}'
|
|
100
|
+
outrank-cli products integrations webflow fields --json '{"api_token":"wf_token","collection_id":"collection_id"}'
|
|
101
|
+
outrank-cli products integrations webflow items --json '{"api_token":"wf_token","collection_id":"collection_id"}'
|
|
102
|
+
outrank-cli products integrations validate webhook --json '{"endpoint":"https://api.example.com/webhook","access_token":"secret"}'
|
|
103
|
+
outrank-cli products integrations validate framer --json '{"project_url":"https://framer.com/projects/your-project","server_api_key":"framer_server_key"}'
|
|
104
|
+
outrank-cli products integrations validate webflow --json '{"api_token":"wf_token"}'
|
|
105
|
+
outrank-cli products integrations validate shopify --json '{"access_token":"shpat_xxx"}'
|
|
106
|
+
outrank-cli products integrations validate wix --json '{"site_id":"00000000-0000-0000-0000-000000000000","api_key":"wix_api_key"}'
|
|
107
|
+
outrank-cli products integrations validate wordpress-plugin --json '{"domain":"https://example.com","api_key":"secret-token"}'
|
|
108
|
+
outrank-cli products integrations list 00000000-0000-0000-0000-000000000000
|
|
109
|
+
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"}'
|
|
110
|
+
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"}'
|
|
111
|
+
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"}'
|
|
112
|
+
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"}'
|
|
113
|
+
outrank-cli products integrations create 00000000-0000-0000-0000-000000000000 --json-file webflow-create.json
|
|
114
|
+
outrank-cli products integrations create 00000000-0000-0000-0000-000000000000 --json '{"provider":"wordpress-plugin","domain":"https://example.com","api_key":"secret-token"}'
|
|
115
|
+
outrank-cli products integrations create 00000000-0000-0000-0000-000000000000 --json '{"provider":"nextjs-blog"}'
|
|
116
|
+
outrank-cli products integrations get 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111
|
|
117
|
+
outrank-cli products integrations update 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111 --json '{"endpoint":"https://api.example.com/webhook-v2"}'
|
|
118
|
+
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"}'
|
|
119
|
+
outrank-cli products integrations update 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111 --json '{"publish_status":"draft"}'
|
|
120
|
+
outrank-cli products integrations update 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111 --json-file webflow-update.json
|
|
121
|
+
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"}'
|
|
122
|
+
outrank-cli products integrations test 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111
|
|
123
|
+
outrank-cli products output-settings update 00000000-0000-0000-0000-000000000000 --json-file output-settings.json
|
|
124
|
+
outrank-cli products target-audiences replace 00000000-0000-0000-0000-000000000000 --json-file target-audiences.json
|
|
125
|
+
outrank-cli products competitors replace 00000000-0000-0000-0000-000000000000 --json-file competitors.json
|
|
126
|
+
outrank-cli products gsc connect 00000000-0000-0000-0000-000000000000
|
|
127
|
+
outrank-cli products gsc properties 00000000-0000-0000-0000-000000000000 --email owner@example.com
|
|
128
|
+
outrank-cli products gsc performance 00000000-0000-0000-0000-000000000000 --months 3
|
|
129
|
+
outrank-cli products gsc inspect-url 00000000-0000-0000-0000-000000000000 --url https://example.com/blog/post
|
|
130
|
+
outrank-cli products linking sources get 00000000-0000-0000-0000-000000000000
|
|
131
|
+
outrank-cli products linking sources update 00000000-0000-0000-0000-000000000000 --json-file linking-sources.json
|
|
132
|
+
outrank-cli products linking detect 00000000-0000-0000-0000-000000000000
|
|
133
|
+
outrank-cli products linking target-config update 00000000-0000-0000-0000-000000000000 --json '{"only_prioritized":true}'
|
|
134
|
+
outrank-cli products linking targets add-manual 00000000-0000-0000-0000-000000000000 --json-file manual-target.json
|
|
135
|
+
outrank-cli products linking targets clear-customizations 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111
|
|
136
|
+
outrank-cli products backlinks get 00000000-0000-0000-0000-000000000000
|
|
137
|
+
outrank-cli products backlinks update 00000000-0000-0000-0000-000000000000 --json '{"enabled":true,"min_domain_rating":10}'
|
|
138
|
+
outrank-cli products backlinks credits 00000000-0000-0000-0000-000000000000
|
|
139
|
+
outrank-cli products backlinks earned 00000000-0000-0000-0000-000000000000 --limit 20 --include-article
|
|
140
|
+
outrank-cli products backlinks unverified 00000000-0000-0000-0000-000000000000 --limit 20
|
|
141
|
+
outrank-cli products backlinks check-removal 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111
|
|
142
|
+
outrank-cli products backlinks reverify 00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111
|
|
143
|
+
outrank-cli products backlinks subscription 00000000-0000-0000-0000-000000000000
|
|
144
|
+
outrank-cli products backlinks subscribe-quote 00000000-0000-0000-0000-000000000000 --plan premium
|
|
145
|
+
`;
|
|
146
|
+
|
|
147
|
+
export const productsGroup = defineGroup({
|
|
148
|
+
id: 'products',
|
|
149
|
+
help: {
|
|
150
|
+
usage: PRODUCTS_HELP,
|
|
151
|
+
guidance:
|
|
152
|
+
'Choose one of: list, get, create, update, pause, resume, integrations, output-settings, target-audiences, ' +
|
|
153
|
+
'competitors, gsc, linking, backlinks.',
|
|
154
|
+
},
|
|
155
|
+
unknown: (word) => ({
|
|
156
|
+
message: `Unknown products subcommand: ${word}.`,
|
|
157
|
+
guidance: 'Run `outrank-cli products --help` to see available product commands.',
|
|
158
|
+
}),
|
|
159
|
+
children: [
|
|
160
|
+
productsList,
|
|
161
|
+
productsGet,
|
|
162
|
+
productsCreate,
|
|
163
|
+
productsUpdate,
|
|
164
|
+
productsPause,
|
|
165
|
+
productsResume,
|
|
166
|
+
integrationsGroup,
|
|
167
|
+
outputSettingsGroup,
|
|
168
|
+
targetAudiencesGroup,
|
|
169
|
+
competitorsGroup,
|
|
170
|
+
gscGroup,
|
|
171
|
+
linkingGroup,
|
|
172
|
+
backlinksGroup,
|
|
173
|
+
],
|
|
174
|
+
});
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { usageError } from '../../core/errors.js';
|
|
2
|
+
import { oneOf, string, uuid } from '../../core/schema.js';
|
|
3
|
+
import { BodyMode, CommandKind, Toolset, defineCommand, defineGroup } from '../registry.js';
|
|
4
|
+
import { PRODUCT_CLI, productInput, productParams, withProduct } from './shared.js';
|
|
5
|
+
|
|
6
|
+
const INTEGRATIONS_USAGE = `Usage:
|
|
7
|
+
outrank-cli products integrations providers
|
|
8
|
+
outrank-cli products integrations prepare <provider> <product-id> [--json <object>]
|
|
9
|
+
outrank-cli products integrations connect <provider> <product-id> [--json <object>]
|
|
10
|
+
outrank-cli products integrations poll <provider> <product-id> --attempt-id <attempt-id>
|
|
11
|
+
outrank-cli products integrations notion databases <product-id> <integration-id>
|
|
12
|
+
outrank-cli products integrations notion schema <product-id> <integration-id> <database-id>
|
|
13
|
+
outrank-cli products integrations framer collections --json <object>
|
|
14
|
+
outrank-cli products integrations framer fields --json <object>
|
|
15
|
+
outrank-cli products integrations webflow sites --json <object>
|
|
16
|
+
outrank-cli products integrations webflow collections --json <object>
|
|
17
|
+
outrank-cli products integrations webflow fields --json <object>
|
|
18
|
+
outrank-cli products integrations webflow items --json <object>
|
|
19
|
+
outrank-cli products integrations validate <provider> --json <object>
|
|
20
|
+
outrank-cli products integrations list <product-id>
|
|
21
|
+
outrank-cli products integrations create <product-id> --json <object>
|
|
22
|
+
outrank-cli products integrations get <product-id> <integration-id>
|
|
23
|
+
outrank-cli products integrations update <product-id> <integration-id> --json <object>
|
|
24
|
+
outrank-cli products integrations test <product-id> <integration-id>
|
|
25
|
+
outrank-cli products integrations delete <product-id> <integration-id>`;
|
|
26
|
+
|
|
27
|
+
const INTEGRATIONS_GUIDANCE =
|
|
28
|
+
'Use `providers` to inspect known provider availability, auth method, required connect/create fields, and ' +
|
|
29
|
+
'provider notes. For WordPress Plugin, start with `prepare` to generate the integration token, then validate ' +
|
|
30
|
+
'and create. For WordPress.com and Notion, use `connect`, have the human open the returned connect_url, then ' +
|
|
31
|
+
'`poll` until the OAuth handoff completes. Notion exposes `notion databases` and `notion schema` discovery ' +
|
|
32
|
+
'commands. Framer exposes `framer collections` and `framer fields` discovery for the newer Server API-based ' +
|
|
33
|
+
'setup, not the older plugin flow. Webflow exposes `webflow sites`, `webflow collections`, `webflow fields`, ' +
|
|
34
|
+
'and `webflow items` discovery before create or update. Shopify validation returns the resolved `shop_name` ' +
|
|
35
|
+
'plus available blogs, so the human should only need to provide the access token before blog selection. Wix ' +
|
|
36
|
+
'is a direct validate/create/update/test flow using `site_id`, `api_key`, and `publish_status`. Next.js Blog ' +
|
|
37
|
+
'is a one-step create flow that returns a one-time API key in the response. V2 products are intended to keep ' +
|
|
38
|
+
'one publishing integration; multiple rows usually indicate legacy cleanup work.';
|
|
39
|
+
|
|
40
|
+
const NOTION_USAGE = `Usage:
|
|
41
|
+
outrank-cli products integrations notion databases <product-id> <integration-id>
|
|
42
|
+
outrank-cli products integrations notion schema <product-id> <integration-id> <database-id>`;
|
|
43
|
+
const NOTION_GUIDANCE =
|
|
44
|
+
'Use `databases` after Notion OAuth completes to list accessible Notion databases, then use `schema` to ' +
|
|
45
|
+
'inspect one database before PATCHing the saved integration row with name, database_id, and fields.';
|
|
46
|
+
|
|
47
|
+
const WEBFLOW_USAGE = `Usage:
|
|
48
|
+
outrank-cli products integrations webflow sites --json <object>
|
|
49
|
+
outrank-cli products integrations webflow collections --json <object>
|
|
50
|
+
outrank-cli products integrations webflow fields --json <object>
|
|
51
|
+
outrank-cli products integrations webflow items --json <object>`;
|
|
52
|
+
const WEBFLOW_GUIDANCE =
|
|
53
|
+
'Use `sites` first to validate a Webflow token and list accessible sites, then `collections`, `fields`, and ' +
|
|
54
|
+
'`items` to build the exact create or update payload for the chosen CMS collection.';
|
|
55
|
+
|
|
56
|
+
const FRAMER_USAGE = `Usage:
|
|
57
|
+
outrank-cli products integrations framer collections --json <object>
|
|
58
|
+
outrank-cli products integrations framer fields --json <object>`;
|
|
59
|
+
const FRAMER_GUIDANCE =
|
|
60
|
+
'Use `collections` first with the Framer project URL and Server API key. Then use `fields` for one chosen ' +
|
|
61
|
+
'collection if you want `collection_setup_mode: "existing"` instead of auto-creating a new collection.';
|
|
62
|
+
|
|
63
|
+
const TOOLSETS = [Toolset.INTEGRATIONS];
|
|
64
|
+
const NOTION_ID_PATTERN = /^[A-Za-z0-9-]{1,64}$/;
|
|
65
|
+
const NOTION_ID_GUIDANCE = 'Use a database id returned by the Notion databases command.';
|
|
66
|
+
const PROVIDERS = Object.freeze([
|
|
67
|
+
'wordpress-plugin',
|
|
68
|
+
'wordpress-com',
|
|
69
|
+
'notion',
|
|
70
|
+
'framer',
|
|
71
|
+
'webflow',
|
|
72
|
+
'shopify',
|
|
73
|
+
'wix',
|
|
74
|
+
'webhook',
|
|
75
|
+
'nextjs-blog',
|
|
76
|
+
]);
|
|
77
|
+
const WEBFLOW_RESOURCES = Object.freeze(['sites', 'collections', 'fields', 'items']);
|
|
78
|
+
const FRAMER_RESOURCES = Object.freeze(['collections', 'fields']);
|
|
79
|
+
const INTEGRATION_PATH = '/products/{productId}/integrations/{integrationId}';
|
|
80
|
+
const CONNECT_PATH = '/products/{productId}/integrations/connect/{provider}';
|
|
81
|
+
const PROVIDER_PRODUCT_CLI = { positionals: ['provider', 'productId'] };
|
|
82
|
+
const INTEGRATION_CLI = withProduct(['integrationId']);
|
|
83
|
+
const ATTEMPT_ID_PLACEHOLDERS = { attemptId: '<attempt-id>' };
|
|
84
|
+
const ATTEMPT_ID_REQUIRED = {
|
|
85
|
+
guidance:
|
|
86
|
+
'Run `outrank-cli products integrations poll <provider> <product-id> --attempt-id <attempt-id>` after ' +
|
|
87
|
+
'connect starts.',
|
|
88
|
+
};
|
|
89
|
+
const SENSITIVE_BODY = { sensitiveBody: true };
|
|
90
|
+
|
|
91
|
+
const providerInput = () => ({ provider: oneOf(PROVIDERS).required(), ...productInput() });
|
|
92
|
+
const providerParams = (input) => ({ productId: input.productId, provider: input.provider });
|
|
93
|
+
const integrationInput = () => ({ ...productInput(), integrationId: uuid().required() });
|
|
94
|
+
const integrationParams = (input) => ({ productId: input.productId, integrationId: input.integrationId });
|
|
95
|
+
|
|
96
|
+
const providers = defineCommand({
|
|
97
|
+
id: 'products.integrations.providers',
|
|
98
|
+
summary: 'List known integration providers with their auth method and required fields.',
|
|
99
|
+
kind: CommandKind.READ,
|
|
100
|
+
toolsets: TOOLSETS,
|
|
101
|
+
run: (_input, ctx) => ctx.http({ method: 'GET', path: '/integration-providers' }),
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const prepare = defineCommand({
|
|
105
|
+
id: 'products.integrations.prepare',
|
|
106
|
+
summary: 'Prepare a provider integration for a product (e.g. generate the WordPress plugin token).',
|
|
107
|
+
kind: CommandKind.WRITE,
|
|
108
|
+
toolsets: TOOLSETS,
|
|
109
|
+
input: providerInput(),
|
|
110
|
+
cli: PROVIDER_PRODUCT_CLI,
|
|
111
|
+
body: BodyMode.OPTIONAL_JSON,
|
|
112
|
+
run: (input, ctx) =>
|
|
113
|
+
ctx.http({
|
|
114
|
+
method: 'POST',
|
|
115
|
+
path: '/products/{productId}/integrations/prepare/{provider}',
|
|
116
|
+
pathParams: providerParams(input),
|
|
117
|
+
body: input.body,
|
|
118
|
+
}),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const connect = defineCommand({
|
|
122
|
+
id: 'products.integrations.connect',
|
|
123
|
+
summary: 'Start an OAuth connect flow for a provider and return the connect URL.',
|
|
124
|
+
kind: CommandKind.WRITE,
|
|
125
|
+
toolsets: TOOLSETS,
|
|
126
|
+
input: providerInput(),
|
|
127
|
+
cli: PROVIDER_PRODUCT_CLI,
|
|
128
|
+
body: BodyMode.OPTIONAL_JSON,
|
|
129
|
+
run: (input, ctx) =>
|
|
130
|
+
ctx.http({ method: 'POST', path: CONNECT_PATH, pathParams: providerParams(input), body: input.body }),
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const poll = defineCommand({
|
|
134
|
+
id: 'products.integrations.poll',
|
|
135
|
+
summary: 'Poll an OAuth connect attempt until the handoff completes.',
|
|
136
|
+
kind: CommandKind.READ,
|
|
137
|
+
toolsets: TOOLSETS,
|
|
138
|
+
input: { ...providerInput(), attemptId: string().required(ATTEMPT_ID_REQUIRED) },
|
|
139
|
+
cli: { ...PROVIDER_PRODUCT_CLI, placeholders: ATTEMPT_ID_PLACEHOLDERS },
|
|
140
|
+
run: (input, ctx) =>
|
|
141
|
+
ctx.http({
|
|
142
|
+
method: 'GET',
|
|
143
|
+
path: CONNECT_PATH,
|
|
144
|
+
pathParams: providerParams(input),
|
|
145
|
+
query: { attempt_id: input.attemptId },
|
|
146
|
+
}),
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const notionDatabases = defineCommand({
|
|
150
|
+
id: 'products.integrations.notion.databases',
|
|
151
|
+
summary: 'List the Notion databases accessible to a connected integration.',
|
|
152
|
+
kind: CommandKind.READ,
|
|
153
|
+
toolsets: TOOLSETS,
|
|
154
|
+
input: integrationInput(),
|
|
155
|
+
cli: INTEGRATION_CLI,
|
|
156
|
+
run: (input, ctx) =>
|
|
157
|
+
ctx.http({ method: 'GET', path: `${INTEGRATION_PATH}/notion/databases`, pathParams: integrationParams(input) }),
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const notionSchema = defineCommand({
|
|
161
|
+
id: 'products.integrations.notion.schema',
|
|
162
|
+
summary: 'Inspect the schema of one Notion database.',
|
|
163
|
+
kind: CommandKind.READ,
|
|
164
|
+
toolsets: TOOLSETS,
|
|
165
|
+
input: { ...integrationInput(), databaseId: string().required() },
|
|
166
|
+
cli: withProduct(['integrationId', 'databaseId']),
|
|
167
|
+
refine: (input) => {
|
|
168
|
+
if (!NOTION_ID_PATTERN.test(input.databaseId)) {
|
|
169
|
+
throw usageError('A Notion database id contains only letters, digits and dashes.', NOTION_ID_GUIDANCE);
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
run: (input, ctx) =>
|
|
173
|
+
ctx.http({
|
|
174
|
+
method: 'GET',
|
|
175
|
+
path: `${INTEGRATION_PATH}/notion/databases/{databaseId}/schema`,
|
|
176
|
+
pathParams: { ...integrationParams(input), databaseId: input.databaseId },
|
|
177
|
+
}),
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const discoverCommand = (provider, resource) =>
|
|
181
|
+
defineCommand({
|
|
182
|
+
id: `products.integrations.${provider}.${resource}`,
|
|
183
|
+
summary: `Discover ${provider} ${resource} with the credentials in the JSON body.`,
|
|
184
|
+
kind: CommandKind.READ,
|
|
185
|
+
toolsets: TOOLSETS,
|
|
186
|
+
mcp: SENSITIVE_BODY,
|
|
187
|
+
body: BodyMode.JSON,
|
|
188
|
+
run: (input, ctx) =>
|
|
189
|
+
ctx.http({
|
|
190
|
+
method: 'POST',
|
|
191
|
+
path: '/integration-providers/{provider}/discover/{resource}',
|
|
192
|
+
pathParams: { provider, resource },
|
|
193
|
+
body: input.body,
|
|
194
|
+
}),
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const discoverGroup = ({ provider, usage, guidance, resources, label }) =>
|
|
198
|
+
defineGroup({
|
|
199
|
+
id: `products.integrations.${provider}`,
|
|
200
|
+
help: { usage, guidance },
|
|
201
|
+
unknown: (word) => ({
|
|
202
|
+
message: `Unknown products integrations ${provider} subcommand: ${word}.`,
|
|
203
|
+
guidance: `Run \`outrank-cli products integrations ${provider} --help\` to see the available ${label} commands.`,
|
|
204
|
+
}),
|
|
205
|
+
children: resources.map((resource) => discoverCommand(provider, resource)),
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const validate = defineCommand({
|
|
209
|
+
id: 'products.integrations.validate',
|
|
210
|
+
summary: 'Validate provider credentials from a JSON body without saving them.',
|
|
211
|
+
kind: CommandKind.READ,
|
|
212
|
+
toolsets: TOOLSETS,
|
|
213
|
+
input: { provider: oneOf(PROVIDERS).required() },
|
|
214
|
+
cli: { positionals: ['provider'] },
|
|
215
|
+
mcp: SENSITIVE_BODY,
|
|
216
|
+
body: BodyMode.JSON,
|
|
217
|
+
run: (input, ctx) =>
|
|
218
|
+
ctx.http({
|
|
219
|
+
method: 'POST',
|
|
220
|
+
path: '/integration-providers/{provider}/validate',
|
|
221
|
+
pathParams: { provider: input.provider },
|
|
222
|
+
body: input.body,
|
|
223
|
+
}),
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const list = defineCommand({
|
|
227
|
+
id: 'products.integrations.list',
|
|
228
|
+
summary: 'List the publishing integrations of a product.',
|
|
229
|
+
kind: CommandKind.READ,
|
|
230
|
+
toolsets: TOOLSETS,
|
|
231
|
+
input: productInput(),
|
|
232
|
+
cli: PRODUCT_CLI,
|
|
233
|
+
run: (input, ctx) =>
|
|
234
|
+
ctx.http({ method: 'GET', path: '/products/{productId}/integrations', pathParams: productParams(input) }),
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const create = defineCommand({
|
|
238
|
+
id: 'products.integrations.create',
|
|
239
|
+
summary: 'Create a publishing integration for a product from a JSON body.',
|
|
240
|
+
kind: CommandKind.WRITE,
|
|
241
|
+
toolsets: TOOLSETS,
|
|
242
|
+
mcp: SENSITIVE_BODY,
|
|
243
|
+
input: productInput(),
|
|
244
|
+
cli: PRODUCT_CLI,
|
|
245
|
+
body: BodyMode.JSON,
|
|
246
|
+
run: (input, ctx) =>
|
|
247
|
+
ctx.http({
|
|
248
|
+
method: 'POST',
|
|
249
|
+
path: '/products/{productId}/integrations',
|
|
250
|
+
pathParams: productParams(input),
|
|
251
|
+
body: input.body,
|
|
252
|
+
}),
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
const get = defineCommand({
|
|
256
|
+
id: 'products.integrations.get',
|
|
257
|
+
summary: 'Get one integration of a product.',
|
|
258
|
+
kind: CommandKind.READ,
|
|
259
|
+
toolsets: TOOLSETS,
|
|
260
|
+
input: integrationInput(),
|
|
261
|
+
cli: INTEGRATION_CLI,
|
|
262
|
+
run: (input, ctx) => ctx.http({ method: 'GET', path: INTEGRATION_PATH, pathParams: integrationParams(input) }),
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
const update = defineCommand({
|
|
266
|
+
id: 'products.integrations.update',
|
|
267
|
+
summary: 'Update one integration of a product from a JSON body.',
|
|
268
|
+
kind: CommandKind.WRITE,
|
|
269
|
+
toolsets: TOOLSETS,
|
|
270
|
+
mcp: SENSITIVE_BODY,
|
|
271
|
+
input: integrationInput(),
|
|
272
|
+
cli: INTEGRATION_CLI,
|
|
273
|
+
body: BodyMode.JSON,
|
|
274
|
+
run: (input, ctx) =>
|
|
275
|
+
ctx.http({ method: 'PATCH', path: INTEGRATION_PATH, pathParams: integrationParams(input), body: input.body }),
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
const testCommand = defineCommand({
|
|
279
|
+
id: 'products.integrations.test',
|
|
280
|
+
summary: 'Test one integration of a product.',
|
|
281
|
+
kind: CommandKind.WRITE,
|
|
282
|
+
toolsets: TOOLSETS,
|
|
283
|
+
input: integrationInput(),
|
|
284
|
+
cli: INTEGRATION_CLI,
|
|
285
|
+
run: (input, ctx) =>
|
|
286
|
+
ctx.http({ method: 'POST', path: `${INTEGRATION_PATH}/test`, pathParams: integrationParams(input) }),
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
const remove = defineCommand({
|
|
290
|
+
id: 'products.integrations.delete',
|
|
291
|
+
summary: 'Delete one integration of a product.',
|
|
292
|
+
kind: CommandKind.DESTRUCTIVE,
|
|
293
|
+
toolsets: TOOLSETS,
|
|
294
|
+
input: integrationInput(),
|
|
295
|
+
cli: INTEGRATION_CLI,
|
|
296
|
+
run: (input, ctx) => ctx.http({ method: 'DELETE', path: INTEGRATION_PATH, pathParams: integrationParams(input) }),
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
export const integrationsGroup = defineGroup({
|
|
300
|
+
id: 'products.integrations',
|
|
301
|
+
help: { usage: INTEGRATIONS_USAGE, guidance: INTEGRATIONS_GUIDANCE },
|
|
302
|
+
unknown: (word) => ({
|
|
303
|
+
message: `Unknown products integrations subcommand: ${word}.`,
|
|
304
|
+
guidance: 'Run `outrank-cli products integrations --help` to see the available integration commands.',
|
|
305
|
+
}),
|
|
306
|
+
children: [
|
|
307
|
+
providers,
|
|
308
|
+
prepare,
|
|
309
|
+
connect,
|
|
310
|
+
poll,
|
|
311
|
+
defineGroup({
|
|
312
|
+
id: 'products.integrations.notion',
|
|
313
|
+
help: { usage: NOTION_USAGE, guidance: NOTION_GUIDANCE },
|
|
314
|
+
unknown: (word) => ({
|
|
315
|
+
message: `Unknown products integrations notion subcommand: ${word}.`,
|
|
316
|
+
guidance:
|
|
317
|
+
'Run `outrank-cli products integrations notion --help` to see the available Notion discovery commands.',
|
|
318
|
+
}),
|
|
319
|
+
children: [notionDatabases, notionSchema],
|
|
320
|
+
}),
|
|
321
|
+
discoverGroup({
|
|
322
|
+
provider: 'webflow',
|
|
323
|
+
usage: WEBFLOW_USAGE,
|
|
324
|
+
guidance: WEBFLOW_GUIDANCE,
|
|
325
|
+
resources: WEBFLOW_RESOURCES,
|
|
326
|
+
label: 'Webflow discovery',
|
|
327
|
+
}),
|
|
328
|
+
discoverGroup({
|
|
329
|
+
provider: 'framer',
|
|
330
|
+
usage: FRAMER_USAGE,
|
|
331
|
+
guidance: FRAMER_GUIDANCE,
|
|
332
|
+
resources: FRAMER_RESOURCES,
|
|
333
|
+
label: 'Framer discovery',
|
|
334
|
+
}),
|
|
335
|
+
validate,
|
|
336
|
+
list,
|
|
337
|
+
create,
|
|
338
|
+
get,
|
|
339
|
+
update,
|
|
340
|
+
testCommand,
|
|
341
|
+
remove,
|
|
342
|
+
],
|
|
343
|
+
});
|