sv-cli 0.3.0__tar.gz → 0.4.0__tar.gz
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.
- {sv_cli-0.3.0 → sv_cli-0.4.0}/AGENT.md +23 -17
- {sv_cli-0.3.0 → sv_cli-0.4.0}/PKG-INFO +12 -6
- {sv_cli-0.3.0 → sv_cli-0.4.0}/README.md +11 -5
- {sv_cli-0.3.0 → sv_cli-0.4.0}/pyproject.toml +1 -1
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/adapters.py +23 -3
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/api_client.py +1 -1
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/main.py +175 -28
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/resolver.py +34 -0
- sv_cli-0.4.0/tests/fixtures/ranklens_definitions.json +16 -0
- sv_cli-0.4.0/tests/fixtures/seogpt2_definitions.json +15 -0
- sv_cli-0.4.0/tests/test_executor.py +150 -0
- sv_cli-0.4.0/tests/test_help_filtering.py +80 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/tests/test_resolver.py +48 -0
- sv_cli-0.3.0/tests/test_executor.py +0 -76
- {sv_cli-0.3.0 → sv_cli-0.4.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/.github/workflows/publish.yml +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/.github/workflows/release.yml +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/.github/workflows/test.yml +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/.gitignore +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/CHANGELOG.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/CODE_OF_CONDUCT.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/CONTRIBUTING.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/LICENSE +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/PULL_REQUEST_TEMPLATE.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/SECURITY.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/docs/agent-usage.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/docs/api-definition-format.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/docs/authentication.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/docs/commands.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/docs/examples.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/docs/installation.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/docs/troubleshooting.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/__init__.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/__init__.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/auth.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/better_keywords.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/call.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/config.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/content_quality.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/content_transformer.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/core_analysis.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/definitions.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/geo_audit.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/insight_igniter.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/marketplace_services.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/options.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/preliminary_audit.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/ranklens.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/seo_image.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/seo_mapping.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/seogpt.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/seogpt2.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/seogpt_compare.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/top_competitors.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/commands/topical_authority.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/config.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/definitions.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/errors.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/executor.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/formatter.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/renderers/__init__.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/renderers/csv_renderer.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/renderers/json_renderer.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/renderers/markdown_renderer.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/renderers/table_renderer.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/renderers/text_renderer.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/schemas/__init__.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/schemas/api_response.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/schemas/config.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/schemas/tool_definition.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/tasks.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/src/sv_cli/utils.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/tests/fixtures/api_root.json +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/tests/fixtures/better_keywords_response.json +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/tests/fixtures/seogpt_definitions.json +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/tests/test_config.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/tests/test_definitions.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.4.0}/tests/test_formatter.py +0 -0
|
@@ -55,16 +55,16 @@ All 16 available tools. Use exact command names — aliases are human shortcuts.
|
|
|
55
55
|
|
|
56
56
|
| Command | Alias | Default Action | API Required Fields | Async |
|
|
57
57
|
|---|---|---|---|---|
|
|
58
|
-
| `better-keywords` | `keywords` | `research` / `filter` | `--keyword` (`filter`
|
|
58
|
+
| `better-keywords` | `keywords` | `research` / `filter` | `--keyword` (`filter` needs a JSON `data` array — raw call only) | No |
|
|
59
59
|
| `content-transformer` | `transform` | `rewrite` | `--text` | No |
|
|
60
60
|
| `core-analysis` | `core` | `analyze` | *(none required)* | No |
|
|
61
61
|
| `geo-audit` | `audit` | `create-task` | `--url` `--keyword` | Yes |
|
|
62
62
|
| `insight-igniter` | `insights` | `entities` | `--url` | No |
|
|
63
63
|
| `preliminary-audit` | `prelim-audit` | `analyze` | `--url` | No |
|
|
64
|
-
| `ranklens` | — | `rank` | `--
|
|
64
|
+
| `ranklens` | — | `rank` | `--entity` `--url` | No |
|
|
65
65
|
| `seo-image` | `image` | `generate` | `--keyword` | No |
|
|
66
66
|
| `seogpt` | `seo-gpt` | `generate` | `--keyword` `--type` | No |
|
|
67
|
-
| `seogpt2` | `seo-gpt2` | `create-task` | `--
|
|
67
|
+
| `seogpt2` | `seo-gpt2` | `create-task` | `--topic` *(`--keyword`/`--kw` is a separate, optional field)* | Yes |
|
|
68
68
|
| `seogpt-compare` | `compare` | `create-task` | `--url` `--keyword` | Yes |
|
|
69
69
|
| `seo-mapping` | `mapping` | `create-task` | `--url` `--keyword` | Yes |
|
|
70
70
|
| `topical-authority` | `topical` | `topics` | `--keyword` | No |
|
|
@@ -112,6 +112,7 @@ sv options seogpt type --search meta
|
|
|
112
112
|
# Shorthand subcommands (where available)
|
|
113
113
|
sv seogpt types
|
|
114
114
|
sv seogpt types --search meta
|
|
115
|
+
sv seogpt lengths
|
|
115
116
|
sv seogpt languages
|
|
116
117
|
sv seogpt engines
|
|
117
118
|
sv image types
|
|
@@ -128,6 +129,7 @@ sv seo-mapping types
|
|
|
128
129
|
sv better-keywords types
|
|
129
130
|
sv better-keywords languages
|
|
130
131
|
sv content-transformer types
|
|
132
|
+
sv content-transformer lengths
|
|
131
133
|
sv content-transformer languages
|
|
132
134
|
sv ranklens languages
|
|
133
135
|
sv ranklens engines
|
|
@@ -241,8 +243,8 @@ sv --format json task result TASK_ID --tool geo-audit
|
|
|
241
243
|
### Pattern 3: Direct tool polling
|
|
242
244
|
|
|
243
245
|
```bash
|
|
244
|
-
sv geo-audit get-task-status --
|
|
245
|
-
sv geo-audit get-result --
|
|
246
|
+
sv geo-audit get-task-status --task-id TASK_ID --format json
|
|
247
|
+
sv geo-audit get-result --task-id TASK_ID --format json
|
|
246
248
|
```
|
|
247
249
|
|
|
248
250
|
Same for `seogpt2`, `seogpt-compare`, `seo-mapping` — replace `geo-audit` with the tool name.
|
|
@@ -253,14 +255,17 @@ Same for `seogpt2`, `seogpt-compare`, `seo-mapping` — replace `geo-audit` with
|
|
|
253
255
|
|
|
254
256
|
These differ from the standard pattern — get them wrong and the call fails.
|
|
255
257
|
|
|
256
|
-
### seogpt2 — `--
|
|
258
|
+
### seogpt2 — `--topic` maps to `Topic` (required), `--keyword`/`--kw` is a separate field
|
|
257
259
|
|
|
258
260
|
```bash
|
|
259
|
-
# CORRECT — --
|
|
260
|
-
sv seogpt2 create-task --
|
|
261
|
+
# CORRECT — --topic sends value as the required "Topic" API field
|
|
262
|
+
sv seogpt2 create-task --topic "White Label SEO for Agencies" --type on-page-blog-article --wait --strict --no-fuzzy --non-interactive --format json
|
|
261
263
|
|
|
262
|
-
#
|
|
263
|
-
sv seogpt2 create-task --
|
|
264
|
+
# --title is an alias for --topic (same field)
|
|
265
|
+
sv seogpt2 create-task --title "White Label SEO for Agencies" --type on-page-blog-article --wait --strict --no-fuzzy --non-interactive --format json
|
|
266
|
+
|
|
267
|
+
# --keyword/--kw maps to the separate, optional KW field — it does NOT set Topic
|
|
268
|
+
sv seogpt2 create-task --topic "White Label SEO for Agencies" --keyword "white label seo" --type on-page-blog-article --wait --strict --no-fuzzy --non-interactive --format json
|
|
264
269
|
```
|
|
265
270
|
|
|
266
271
|
### better-keywords `filter` — requires `data` array from prior `research` call
|
|
@@ -288,11 +293,12 @@ No CLI flag exists for `mgptid`. Must use raw call:
|
|
|
288
293
|
|
|
289
294
|
```bash
|
|
290
295
|
# Step 1 — get MGPTID from rank response
|
|
291
|
-
sv ranklens rank --
|
|
296
|
+
sv ranklens rank --entity "white label seo" --url https://example.com --format json
|
|
292
297
|
# → parse response["data"]["MGPTID"]
|
|
298
|
+
# response["data"] always uses "entity", never "keyword" — --keyword/--kw still works as an input alias, but the response field name is always "entity"
|
|
293
299
|
|
|
294
300
|
# Step 2 — pass MGPTID via raw call
|
|
295
|
-
sv --format json call ranklens --json '{"action":"competitors","mgptid":"MGPTID_VALUE","web":"https://example.com","
|
|
301
|
+
sv --format json call ranklens --json '{"action":"competitors","mgptid":"MGPTID_VALUE","web":"https://example.com","entity":"white label seo"}'
|
|
296
302
|
```
|
|
297
303
|
|
|
298
304
|
### `sv task status/result` — `--format` must be global
|
|
@@ -313,7 +319,7 @@ sv task status TASK_ID --tool geo-audit --format json
|
|
|
313
319
|
|---|---|---|
|
|
314
320
|
| `Could not resolve --type "X" in strict mode` | Value is not a valid slug or ID | Run `sv options TOOL type` → use `id` or `slug` column |
|
|
315
321
|
| `Could not resolve --type "X"` (no strict) | No match found at any level | Run `sv options TOOL type --search X` to find closest match |
|
|
316
|
-
| `Topic is required` | seogpt2 called without `--
|
|
322
|
+
| `Topic is required` | seogpt2 called without `--topic` | Add `--topic "your topic"` |
|
|
317
323
|
| `task_id is invalid` | Task has expired on the server | Create a new task |
|
|
318
324
|
| `No local tool mapping found for task` | Task not in `~/.sv/tasks.json` | Add `--tool TOOL_NAME` explicitly |
|
|
319
325
|
| `API authentication failed: HTTP 401` | Bad or missing API key | Check `SV_API_KEY` environment variable |
|
|
@@ -331,7 +337,7 @@ When friendly flags are insufficient, send the payload directly. The CLI injects
|
|
|
331
337
|
```bash
|
|
332
338
|
sv --format json call seogpt --json '{"action":"generate","kw":"white label seo","type":18}'
|
|
333
339
|
sv --format json call geo-audit --json '{"action":"createTask","kw":"white label seo","URL":"https://example.com"}'
|
|
334
|
-
sv --format json call ranklens --json '{"action":"competitors","mgptid":"...","web":"https://example.com","
|
|
340
|
+
sv --format json call ranklens --json '{"action":"competitors","mgptid":"...","web":"https://example.com","entity":"white label seo"}'
|
|
335
341
|
```
|
|
336
342
|
|
|
337
343
|
From a file:
|
|
@@ -370,12 +376,12 @@ sv --format json call better-keywords --json '{"action":"filter","kw":"white lab
|
|
|
370
376
|
sv content-transformer rewrite --text "SEO services for agencies" --keyword "white label seo" --strict --no-fuzzy --non-interactive --format json
|
|
371
377
|
sv core-analysis analyze --url https://example.com --keyword "white label seo" --strict --no-fuzzy --non-interactive --format json
|
|
372
378
|
sv geo-audit create-task --url https://example.com --keyword "white label seo" --wait --strict --no-fuzzy --non-interactive --format json
|
|
373
|
-
sv insight-igniter entities --url https://example.com --
|
|
379
|
+
sv insight-igniter entities --url https://example.com --strict --no-fuzzy --non-interactive --format json
|
|
374
380
|
sv preliminary-audit analyze --url https://example.com --strict --no-fuzzy --non-interactive --format json
|
|
375
|
-
sv ranklens rank --
|
|
381
|
+
sv ranklens rank --entity "white label seo" --url https://example.com --strict --no-fuzzy --non-interactive --format json
|
|
376
382
|
sv seo-image generate --keyword "white label seo" --type 33 --strict --no-fuzzy --non-interactive --format json
|
|
377
383
|
sv seogpt generate --keyword "white label seo" --type 18 --strict --no-fuzzy --non-interactive --format json
|
|
378
|
-
sv seogpt2 create-task --
|
|
384
|
+
sv seogpt2 create-task --topic "White Label SEO for Agencies" --type 0 --wait --strict --no-fuzzy --non-interactive --format json
|
|
379
385
|
sv seogpt-compare create-task --url https://example.com --keyword "white label seo" --wait --strict --no-fuzzy --non-interactive --format json
|
|
380
386
|
sv seo-mapping create-task --url https://example.com --keyword "white label seo" --wait --strict --no-fuzzy --non-interactive --format json
|
|
381
387
|
sv topical-authority topics --keyword "white label seo" --strict --no-fuzzy --non-interactive --format json
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sv-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Open-source, definition-driven command-line client for SV AI API tools.
|
|
5
5
|
Project-URL: Homepage, https://github.com/seovendorco/sv-cli
|
|
6
6
|
Project-URL: Issues, https://github.com/seovendorco/sv-cli/issues
|
|
@@ -175,7 +175,7 @@ The canonical API tool keys are discovered from the live API root; local aliases
|
|
|
175
175
|
|
|
176
176
|
## Enum resolution
|
|
177
177
|
|
|
178
|
-
For enum-heavy fields such as content type, language, engine, image type, theme, background, color, and size, the CLI resolves values in this order:
|
|
178
|
+
For enum-heavy fields such as content type, content length, language, engine, image type, theme, background, color, and size, the CLI resolves values in this order:
|
|
179
179
|
|
|
180
180
|
1. Numeric ID exact match
|
|
181
181
|
2. Exact slug match
|
|
@@ -196,6 +196,12 @@ Examples that all resolve to Meta Description (id 18):
|
|
|
196
196
|
--type "meta desc"
|
|
197
197
|
```
|
|
198
198
|
|
|
199
|
+
`--length`/`--contentlength` (content length by ID, slug, or label — run `sv TOOL lengths` for valid values) resolves the same way:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
sv seogpt generate --keyword "white label seo" --type 18 --length 300
|
|
203
|
+
```
|
|
204
|
+
|
|
199
205
|
> **Note:** Fuzzy matching is enabled by default. Use `--strict --no-fuzzy` in scripts to require exact ID or slug and avoid unintended matches.
|
|
200
206
|
|
|
201
207
|
Agent-safe mode:
|
|
@@ -246,10 +252,10 @@ sv task status TASK_ID --tool geo-audit
|
|
|
246
252
|
sv task result TASK_ID --tool geo-audit
|
|
247
253
|
```
|
|
248
254
|
|
|
249
|
-
`seogpt2` is another async tool. Its required field is `Topic` (a title or subject), mapped via `--keyword
|
|
255
|
+
`seogpt2` is another async tool. Its required field is `Topic` (a title or subject), mapped via `--topic` (`--title` is an alias for the same field). `--keyword`/`--kw` is a separate, optional field for additional keywords — it does not set the topic:
|
|
250
256
|
|
|
251
257
|
```bash
|
|
252
|
-
sv seogpt2 create-task --
|
|
258
|
+
sv seogpt2 create-task --topic "White Label SEO for Agencies" --type on-page-blog-article --wait
|
|
253
259
|
```
|
|
254
260
|
|
|
255
261
|
See available types with `sv seogpt2 types`, lengths with `sv seogpt2 lengths`, engines with `sv seogpt2 engines`.
|
|
@@ -258,8 +264,8 @@ Manual 3-step flow (without `--wait`):
|
|
|
258
264
|
|
|
259
265
|
```bash
|
|
260
266
|
sv geo-audit create-task --url https://example.com --keyword "seo agency,white label seo"
|
|
261
|
-
sv geo-audit get-task-status --
|
|
262
|
-
sv geo-audit get-result --
|
|
267
|
+
sv geo-audit get-task-status --task-id TASK_ID
|
|
268
|
+
sv geo-audit get-result --task-id TASK_ID
|
|
263
269
|
```
|
|
264
270
|
|
|
265
271
|
> **Note:** `--format` is not available on `sv task status` or `sv task result` directly. Place it before `task` as a global flag:
|
|
@@ -140,7 +140,7 @@ The canonical API tool keys are discovered from the live API root; local aliases
|
|
|
140
140
|
|
|
141
141
|
## Enum resolution
|
|
142
142
|
|
|
143
|
-
For enum-heavy fields such as content type, language, engine, image type, theme, background, color, and size, the CLI resolves values in this order:
|
|
143
|
+
For enum-heavy fields such as content type, content length, language, engine, image type, theme, background, color, and size, the CLI resolves values in this order:
|
|
144
144
|
|
|
145
145
|
1. Numeric ID exact match
|
|
146
146
|
2. Exact slug match
|
|
@@ -161,6 +161,12 @@ Examples that all resolve to Meta Description (id 18):
|
|
|
161
161
|
--type "meta desc"
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
+
`--length`/`--contentlength` (content length by ID, slug, or label — run `sv TOOL lengths` for valid values) resolves the same way:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
sv seogpt generate --keyword "white label seo" --type 18 --length 300
|
|
168
|
+
```
|
|
169
|
+
|
|
164
170
|
> **Note:** Fuzzy matching is enabled by default. Use `--strict --no-fuzzy` in scripts to require exact ID or slug and avoid unintended matches.
|
|
165
171
|
|
|
166
172
|
Agent-safe mode:
|
|
@@ -211,10 +217,10 @@ sv task status TASK_ID --tool geo-audit
|
|
|
211
217
|
sv task result TASK_ID --tool geo-audit
|
|
212
218
|
```
|
|
213
219
|
|
|
214
|
-
`seogpt2` is another async tool. Its required field is `Topic` (a title or subject), mapped via `--keyword
|
|
220
|
+
`seogpt2` is another async tool. Its required field is `Topic` (a title or subject), mapped via `--topic` (`--title` is an alias for the same field). `--keyword`/`--kw` is a separate, optional field for additional keywords — it does not set the topic:
|
|
215
221
|
|
|
216
222
|
```bash
|
|
217
|
-
sv seogpt2 create-task --
|
|
223
|
+
sv seogpt2 create-task --topic "White Label SEO for Agencies" --type on-page-blog-article --wait
|
|
218
224
|
```
|
|
219
225
|
|
|
220
226
|
See available types with `sv seogpt2 types`, lengths with `sv seogpt2 lengths`, engines with `sv seogpt2 engines`.
|
|
@@ -223,8 +229,8 @@ Manual 3-step flow (without `--wait`):
|
|
|
223
229
|
|
|
224
230
|
```bash
|
|
225
231
|
sv geo-audit create-task --url https://example.com --keyword "seo agency,white label seo"
|
|
226
|
-
sv geo-audit get-task-status --
|
|
227
|
-
sv geo-audit get-result --
|
|
232
|
+
sv geo-audit get-task-status --task-id TASK_ID
|
|
233
|
+
sv geo-audit get-result --task-id TASK_ID
|
|
228
234
|
```
|
|
229
235
|
|
|
230
236
|
> **Note:** `--format` is not available on `sv task status` or `sv task result` directly. Place it before `task` as a global flag:
|
|
@@ -31,11 +31,19 @@ class ToolAdapter:
|
|
|
31
31
|
COMMON_FIELD_ALIASES: dict[str, tuple[str, ...]] = {
|
|
32
32
|
"keyword": ("keyword", "kw", "query", "seed_keyword"),
|
|
33
33
|
"keywords": ("keywords", "kws", "keyword_list", "kwlist"),
|
|
34
|
+
"topic": ("topic", "title"),
|
|
35
|
+
# Deliberately narrow: no "kw"/"keyword" fallback here, unlike "keyword" above.
|
|
36
|
+
# Those are common to many unrelated tools (better-keywords, seogpt2, ...) and
|
|
37
|
+
# would make --entity spuriously "relevant" (and functional, just semantically
|
|
38
|
+
# wrong) for all of them. RankLens overrides this below with its own aliases.
|
|
39
|
+
"entity": ("entity", "entities"),
|
|
40
|
+
"task_id": ("task_id", "taskid", "task-id"),
|
|
34
41
|
"url": ("url", "domain", "page_url", "website"),
|
|
35
42
|
"url_a": ("url_a", "urla", "url1", "first_url", "competitor_url"),
|
|
36
43
|
"url_b": ("url_b", "urlb", "url2", "second_url", "comparison_url"),
|
|
37
44
|
"brand": ("brand", "brand_name", "company", "company_name"),
|
|
38
45
|
"type": ("contenttype", "content_type", "imagetype", "image_type", "type"),
|
|
46
|
+
"length": ("contentlength", "content_length", "length"),
|
|
39
47
|
"language": ("language", "lang", "language_id"),
|
|
40
48
|
"engine": ("engine", "ai_engine", "model"),
|
|
41
49
|
"country": ("country", "country_code", "location", "gl"),
|
|
@@ -127,7 +135,14 @@ TOOL_ADAPTERS: dict[str, ToolAdapter] = {
|
|
|
127
135
|
aliases=(),
|
|
128
136
|
default_action="rank",
|
|
129
137
|
actions=("rank", "competitors", "raw"),
|
|
130
|
-
field_aliases=
|
|
138
|
+
field_aliases={
|
|
139
|
+
**COMMON_FIELD_ALIASES,
|
|
140
|
+
# "entity" is RankLens's real, documented field. --keyword/--kw remain
|
|
141
|
+
# accepted as aliases (the API still honors kw/keyword input too), but
|
|
142
|
+
# should resolve onto entity now rather than the deprecated field names.
|
|
143
|
+
"keyword": ("entity", "kw", "keyword", "query", "seed_keyword"),
|
|
144
|
+
"entity": ("entity", "entities", "kw", "keyword", "query", "seed_keyword"),
|
|
145
|
+
},
|
|
131
146
|
option_aliases={
|
|
132
147
|
"languages": ("lang", "language", "languages"),
|
|
133
148
|
"engines": ("engine", "model", "engines"),
|
|
@@ -176,8 +191,13 @@ TOOL_ADAPTERS: dict[str, ToolAdapter] = {
|
|
|
176
191
|
actions=("create-task", "get-task-status", "get-result", "raw"),
|
|
177
192
|
field_aliases={
|
|
178
193
|
**COMMON_FIELD_ALIASES,
|
|
179
|
-
|
|
180
|
-
|
|
194
|
+
# The real backend (SEOB's seogpt2api.php) treats these as two distinct
|
|
195
|
+
# fields: Topic (required, 12-200 chars, the article subject) and KW
|
|
196
|
+
# (optional, up to 5 keywords). --keyword/--kw/--keywords must all land
|
|
197
|
+
# on KW, never on Topic - --topic/--title are the only way to set Topic.
|
|
198
|
+
"keyword": ("KW", "kw", "keywords", "keyword", "query", "seed_keyword"),
|
|
199
|
+
"keywords": ("KW", "kw", "keywords", "keyword", "keyword_list", "kwlist"),
|
|
200
|
+
"topic": ("Topic", "topic", "title"),
|
|
181
201
|
},
|
|
182
202
|
async_likely=True,
|
|
183
203
|
option_aliases={
|
|
@@ -35,7 +35,7 @@ class APIClient:
|
|
|
35
35
|
payload: dict[str, Any],
|
|
36
36
|
api_key: str | None,
|
|
37
37
|
method: str = "POST",
|
|
38
|
-
timeout: float =
|
|
38
|
+
timeout: float = 300.0,
|
|
39
39
|
) -> APIResponse:
|
|
40
40
|
final_payload = dict(payload)
|
|
41
41
|
if api_key and not any(key in final_payload for key in ("k", "api_key", "apikey", "key")):
|
|
@@ -31,7 +31,7 @@ from .definitions import DefinitionsManager
|
|
|
31
31
|
from .errors import CLIError, ConfigError, InvalidInputError
|
|
32
32
|
from .executor import RuntimeOptions, WaitOptions, execute_tool, load_json_payload
|
|
33
33
|
from .formatter import print_output
|
|
34
|
-
from .resolver import extract_option_sets, resolve_api_field, search_candidates
|
|
34
|
+
from .resolver import extract_option_sets, is_cli_field_relevant, resolve_api_field, search_candidates
|
|
35
35
|
from .tasks import get_task_tool, result_payload, status_payload
|
|
36
36
|
from .utils import coerce_jsonish, coerce_mapping_values, parse_key_value_args, read_text_source
|
|
37
37
|
|
|
@@ -39,6 +39,78 @@ console = Console()
|
|
|
39
39
|
err_console = Console(stderr=True)
|
|
40
40
|
COMMON_CONTEXT_SETTINGS = {"allow_extra_args": True, "ignore_unknown_options": True}
|
|
41
41
|
|
|
42
|
+
# ---------------------------------------------------------------------------
|
|
43
|
+
# Per-tool/per-action option visibility for --help.
|
|
44
|
+
#
|
|
45
|
+
# The generic option palette in make_action_command() below is shared by every
|
|
46
|
+
# tool+action, but most of its ~25 flags are irrelevant to any given one (e.g.
|
|
47
|
+
# --price/--theme/--color have nothing to do with seogpt2). This section hides
|
|
48
|
+
# irrelevant flags from --help using the live, cached API definitions - the
|
|
49
|
+
# same live_definition-driven approach sv_mcp's schemas.py already uses.
|
|
50
|
+
#
|
|
51
|
+
# --help must stay instant and work offline, so this reads the on-disk
|
|
52
|
+
# definitions cache directly (DefinitionsManager.load_cache()) and never
|
|
53
|
+
# triggers a network fetch. Before the cache is populated (fresh install, or
|
|
54
|
+
# offline), every option stays visible - identical to the old fully-generic
|
|
55
|
+
# behavior - until the user runs a command or `sv definitions refresh`.
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
_DEFINITIONS_CACHE: dict[str, Any] | None = None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _cached_definitions_cache() -> dict[str, Any]:
|
|
62
|
+
global _DEFINITIONS_CACHE
|
|
63
|
+
if _DEFINITIONS_CACHE is None:
|
|
64
|
+
try:
|
|
65
|
+
_DEFINITIONS_CACHE = DefinitionsManager().load_cache()
|
|
66
|
+
except Exception: # noqa: BLE001 - --help must never fail over a cache read
|
|
67
|
+
_DEFINITIONS_CACHE = {}
|
|
68
|
+
return _DEFINITIONS_CACHE
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _tool_definition(tool: str) -> dict[str, Any]:
|
|
72
|
+
cache = _cached_definitions_cache()
|
|
73
|
+
entry = cache.get("tools", {}).get(tool) or {}
|
|
74
|
+
return entry.get("definition") or {}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# create-task/get-task-status/get-result is the identical lifecycle used by every
|
|
78
|
+
# async tool (geogptaudit, seogpt2, seogptcompare, seogptmapping) - confirmed from
|
|
79
|
+
# each tool's index.php, not guessed. The status/result actions only ever need a
|
|
80
|
+
# task_id, regardless of what the create-task action itself accepts.
|
|
81
|
+
TASK_LOOKUP_ACTIONS = {"get-task-status", "get-result"}
|
|
82
|
+
|
|
83
|
+
# Friendly CLI field names in make_action_command()'s signature, in the order they're
|
|
84
|
+
# declared there. Keep in sync with that function's parameters.
|
|
85
|
+
GENERIC_CLI_FIELDS = (
|
|
86
|
+
"keyword", "keywords", "topic", "entity", "url", "url_a", "url_b", "brand", "type_value",
|
|
87
|
+
"length", "language", "engine", "country", "location", "text", "search", "query", "price",
|
|
88
|
+
"series", "category", "outline", "theme", "background", "color", "size",
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Maps a few typer parameter names to the field_aliases key they're actually resolved
|
|
92
|
+
# under (adapters.py), for the ones where they differ (e.g. "type_value" avoids
|
|
93
|
+
# shadowing the builtin `type`, but resolves as "type").
|
|
94
|
+
_CLI_FIELD_ALIAS_FOR = {"type_value": "type"}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _visible_cli_fields(tool: str, action: str) -> set[str] | None:
|
|
98
|
+
"""Friendly CLI field names relevant to this tool+action, or None for "show all".
|
|
99
|
+
|
|
100
|
+
None is the safe fallback: no live definition cached yet, so nothing is hidden.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
if action in TASK_LOOKUP_ACTIONS:
|
|
104
|
+
return set() # every generic content field is irrelevant; task_id is separate
|
|
105
|
+
definition = _tool_definition(tool)
|
|
106
|
+
if not definition:
|
|
107
|
+
return None
|
|
108
|
+
return {
|
|
109
|
+
field
|
|
110
|
+
for field in GENERIC_CLI_FIELDS
|
|
111
|
+
if is_cli_field_relevant(tool, _CLI_FIELD_ALIAS_FOR.get(field, field), definition)
|
|
112
|
+
}
|
|
113
|
+
|
|
42
114
|
app = typer.Typer(
|
|
43
115
|
name="sv",
|
|
44
116
|
help="Definition-driven CLI for SV AI API tools.",
|
|
@@ -585,11 +657,15 @@ def build_params(
|
|
|
585
657
|
*,
|
|
586
658
|
keyword: str | None,
|
|
587
659
|
keywords: str | None,
|
|
660
|
+
topic: str | None,
|
|
661
|
+
entity: str | None,
|
|
662
|
+
task_id: str | None,
|
|
588
663
|
url: str | None,
|
|
589
664
|
url_a: str | None,
|
|
590
665
|
url_b: str | None,
|
|
591
666
|
brand: str | None,
|
|
592
667
|
type_value: str | None,
|
|
668
|
+
length: str | None,
|
|
593
669
|
language: str | None,
|
|
594
670
|
engine: str | None,
|
|
595
671
|
country: str | None,
|
|
@@ -626,11 +702,15 @@ def build_params(
|
|
|
626
702
|
params: dict[str, Any] = {
|
|
627
703
|
"keyword": keyword,
|
|
628
704
|
"keywords": keywords,
|
|
705
|
+
"topic": topic,
|
|
706
|
+
"entity": entity,
|
|
707
|
+
"task_id": task_id,
|
|
629
708
|
"url": url,
|
|
630
709
|
"url_a": url_a,
|
|
631
710
|
"url_b": url_b,
|
|
632
711
|
"brand": brand,
|
|
633
712
|
"type": type_value,
|
|
713
|
+
"length": length,
|
|
634
714
|
"language": language,
|
|
635
715
|
"engine": engine,
|
|
636
716
|
"country": country,
|
|
@@ -672,32 +752,89 @@ def build_params(
|
|
|
672
752
|
|
|
673
753
|
|
|
674
754
|
def make_action_command(tool: str, action: str):
|
|
755
|
+
visible = _visible_cli_fields(tool, action)
|
|
756
|
+
|
|
757
|
+
def hidden(field: str) -> bool:
|
|
758
|
+
return visible is not None and field not in visible
|
|
759
|
+
|
|
760
|
+
show_task_id = action in TASK_LOOKUP_ACTIONS
|
|
761
|
+
|
|
675
762
|
def command(
|
|
676
763
|
ctx: typer.Context,
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
764
|
+
topic: str | None = typer.Option(None, "--topic", "--title", help="Article topic or title.", hidden=hidden("topic")),
|
|
765
|
+
entity: str | None = typer.Option(
|
|
766
|
+
None, "--entity", "--entities", help="Entity to analyze or discover.", hidden=hidden("entity")
|
|
767
|
+
),
|
|
768
|
+
keyword: str | None = typer.Option(
|
|
769
|
+
None, "--keyword", "--kw", help="Keyword text.", hidden=hidden("keyword")
|
|
770
|
+
),
|
|
771
|
+
keywords: str | None = typer.Option(
|
|
772
|
+
None, "--keywords", help="Comma-separated keywords or path to a keyword file.", hidden=hidden("keywords")
|
|
773
|
+
),
|
|
774
|
+
task_id: str | None = typer.Option(
|
|
775
|
+
None, "--task-id", help="Task ID from a prior create-task call.", hidden=not show_task_id
|
|
776
|
+
),
|
|
777
|
+
url: str | None = typer.Option(None, "--url", help="Target URL/domain.", hidden=hidden("url")),
|
|
778
|
+
url_a: str | None = typer.Option(
|
|
779
|
+
None, "--url-a", "--url1", help="First URL for comparison or URL 1.", hidden=hidden("url_a")
|
|
780
|
+
),
|
|
781
|
+
url_b: str | None = typer.Option(
|
|
782
|
+
None, "--url-b", "--url2", help="Second URL for comparison or URL 2.", hidden=hidden("url_b")
|
|
783
|
+
),
|
|
784
|
+
brand: str | None = typer.Option(None, "--brand", help="Brand or company name.", hidden=hidden("brand")),
|
|
785
|
+
type_value: str | None = typer.Option(
|
|
786
|
+
None, "--type", help="Type/content type/image type by ID, slug, label, or alias.", hidden=hidden("type_value")
|
|
787
|
+
),
|
|
788
|
+
length: str | None = typer.Option(
|
|
789
|
+
None, "--length", "--contentlength", help="Content length by ID, slug, label, or alias.", hidden=hidden("length")
|
|
790
|
+
),
|
|
791
|
+
language: str | None = typer.Option(
|
|
792
|
+
None, "--language", help="Language by ID, slug, label, or alias.", hidden=hidden("language")
|
|
793
|
+
),
|
|
794
|
+
engine: str | None = typer.Option(
|
|
795
|
+
None, "--engine", help="Engine/model by ID, slug, label, or alias.", hidden=hidden("engine")
|
|
796
|
+
),
|
|
797
|
+
country: str | None = typer.Option(
|
|
798
|
+
None, "--country", help="Country/market code where supported.", hidden=hidden("country")
|
|
799
|
+
),
|
|
800
|
+
location: str | None = typer.Option(
|
|
801
|
+
None, "--location", help="Location/market where supported.", hidden=hidden("location")
|
|
802
|
+
),
|
|
803
|
+
text: str | None = typer.Option(
|
|
804
|
+
None, "--text", help="Text input for transformer-style tools.", hidden=hidden("text")
|
|
805
|
+
),
|
|
806
|
+
file_path: str | None = typer.Option(None, "--file", help="Read text input from file.", hidden=hidden("text")),
|
|
807
|
+
stdin_flag: bool = typer.Option(False, "--stdin", help="Read text input from stdin.", hidden=hidden("text")),
|
|
808
|
+
search: str | None = typer.Option(
|
|
809
|
+
None, "--search", "--searchterm", help="Search term for marketplace/service-style tools.", hidden=hidden("search")
|
|
810
|
+
),
|
|
811
|
+
query: str | None = typer.Option(
|
|
812
|
+
None, "--query", help="Query/search term where supported.", hidden=hidden("query")
|
|
813
|
+
),
|
|
814
|
+
price: str | None = typer.Option(
|
|
815
|
+
None, "--price", help="Price ceiling or price filter where supported.", hidden=hidden("price")
|
|
816
|
+
),
|
|
817
|
+
series: str | None = typer.Option(
|
|
818
|
+
None, "--series", help="Service series filter where supported.", hidden=hidden("series")
|
|
819
|
+
),
|
|
820
|
+
category: str | None = typer.Option(
|
|
821
|
+
None, "--category", help="Service category filter where supported.", hidden=hidden("category")
|
|
822
|
+
),
|
|
823
|
+
outline: str | None = typer.Option(
|
|
824
|
+
None, "--outline", help="Outline text or path to outline file.", hidden=hidden("outline")
|
|
825
|
+
),
|
|
826
|
+
theme: str | None = typer.Option(
|
|
827
|
+
None, "--theme", help="Image theme by ID, slug, label, or alias.", hidden=hidden("theme")
|
|
828
|
+
),
|
|
829
|
+
background: str | None = typer.Option(
|
|
830
|
+
None, "--background", help="Image background by ID, slug, label, or alias.", hidden=hidden("background")
|
|
831
|
+
),
|
|
832
|
+
color: str | None = typer.Option(
|
|
833
|
+
None, "--color", help="Image color/palette by ID, slug, label, or alias.", hidden=hidden("color")
|
|
834
|
+
),
|
|
835
|
+
size: str | None = typer.Option(
|
|
836
|
+
None, "--size", help="Image size by ID, slug, label, or alias.", hidden=hidden("size")
|
|
837
|
+
),
|
|
701
838
|
wait: bool = typer.Option(False, "--wait", help="Wait for async task completion."),
|
|
702
839
|
timeout: int = typer.Option(600, "--timeout", help="Task wait timeout in seconds."),
|
|
703
840
|
poll_interval: int = typer.Option(5, "--poll-interval", help="Task poll interval in seconds."),
|
|
@@ -717,11 +854,15 @@ def make_action_command(tool: str, action: str):
|
|
|
717
854
|
params = build_params(
|
|
718
855
|
keyword=keyword,
|
|
719
856
|
keywords=keywords,
|
|
857
|
+
topic=topic,
|
|
858
|
+
entity=entity,
|
|
859
|
+
task_id=task_id,
|
|
720
860
|
url=url,
|
|
721
861
|
url_a=url_a,
|
|
722
862
|
url_b=url_b,
|
|
723
863
|
brand=brand,
|
|
724
864
|
type_value=type_value,
|
|
865
|
+
length=length,
|
|
725
866
|
language=language,
|
|
726
867
|
engine=engine,
|
|
727
868
|
country=country,
|
|
@@ -811,9 +952,11 @@ def make_tool_app(adapter: ToolAdapter) -> typer.Typer:
|
|
|
811
952
|
for action in adapter.actions:
|
|
812
953
|
if action == "raw":
|
|
813
954
|
continue
|
|
814
|
-
tool_app.command(action, context_settings=COMMON_CONTEXT_SETTINGS
|
|
955
|
+
tool_app.command(action, context_settings=COMMON_CONTEXT_SETTINGS, rich_help_panel="Actions")(
|
|
956
|
+
make_action_command(adapter.canonical, action)
|
|
957
|
+
)
|
|
815
958
|
|
|
816
|
-
@tool_app.command("raw")
|
|
959
|
+
@tool_app.command("raw", rich_help_panel="Actions")
|
|
817
960
|
def raw(
|
|
818
961
|
ctx: typer.Context,
|
|
819
962
|
json_payload: str | None = typer.Option(None, "--json", help="Raw JSON payload."),
|
|
@@ -839,7 +982,11 @@ def make_tool_app(adapter: ToolAdapter) -> typer.Typer:
|
|
|
839
982
|
fail(exc)
|
|
840
983
|
|
|
841
984
|
for command_name, fields in adapter.option_aliases.items():
|
|
842
|
-
tool_app.command(
|
|
985
|
+
tool_app.command(
|
|
986
|
+
command_name,
|
|
987
|
+
rich_help_panel="Option lookups (list valid values for a parameter, not an action)",
|
|
988
|
+
help=f"List valid values for --{fields[0]}.",
|
|
989
|
+
)(make_option_alias_command(adapter.canonical, fields))
|
|
843
990
|
|
|
844
991
|
return tool_app
|
|
845
992
|
|
|
@@ -333,6 +333,40 @@ def normalize_field(field: str) -> str:
|
|
|
333
333
|
return str(field).strip().replace("-", "_").replace(" ", "_").lower()
|
|
334
334
|
|
|
335
335
|
|
|
336
|
+
def real_field_names(definition: Any) -> set[str]:
|
|
337
|
+
"""Normalized API field names that genuinely appear in a tool's live api_input.
|
|
338
|
+
|
|
339
|
+
Used to decide which generic CLI options are relevant to a specific tool,
|
|
340
|
+
so --help doesn't advertise flags the tool's real API doesn't accept.
|
|
341
|
+
"""
|
|
342
|
+
|
|
343
|
+
names: set[str] = set()
|
|
344
|
+
items = definition.get("api_input") if isinstance(definition, dict) else None
|
|
345
|
+
if not isinstance(items, list):
|
|
346
|
+
return names
|
|
347
|
+
for item in items:
|
|
348
|
+
if isinstance(item, dict):
|
|
349
|
+
field = item.get("field")
|
|
350
|
+
if isinstance(field, str):
|
|
351
|
+
names.add(normalize_field(field))
|
|
352
|
+
return names
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def is_cli_field_relevant(tool: str, cli_field: str, definition: Any) -> bool:
|
|
356
|
+
"""True when a generic CLI field resolves to a real field on this tool's live definition.
|
|
357
|
+
|
|
358
|
+
When no live definition is cached yet (e.g. before the first `sv definitions
|
|
359
|
+
refresh`, or offline), returns True for everything - never hide options based
|
|
360
|
+
on missing data.
|
|
361
|
+
"""
|
|
362
|
+
|
|
363
|
+
if not definition:
|
|
364
|
+
return True
|
|
365
|
+
api_field, _candidates = resolve_api_field(tool, cli_field, definition)
|
|
366
|
+
target_fields = real_field_names(definition) | set(extract_option_sets(definition))
|
|
367
|
+
return api_field in target_fields
|
|
368
|
+
|
|
369
|
+
|
|
336
370
|
def resolve_api_field(tool: str, cli_field: str, definition: Any) -> tuple[str, list[Candidate] | None]:
|
|
337
371
|
"""Choose the best API field name for a friendly CLI field."""
|
|
338
372
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"api_input": [
|
|
3
|
+
{"field": "action", "type": "string", "required": "no", "description": ["rank: Generate RankLens report.", "competitors: Find RankLens competitors."]},
|
|
4
|
+
{"field": "web", "type": "string", "required": "yes", "description": "Website URL, max 300 characters."},
|
|
5
|
+
{"field": "entity", "type": "string", "required": "yes", "description": "Entity/keyword text, 4 to 80 characters."},
|
|
6
|
+
{"field": "kw", "type": "string", "required": "no", "description": "Deprecated alias of entity. Used only when entity is not sent."},
|
|
7
|
+
{"field": "keyword", "type": "string", "required": "no", "description": "Deprecated alias of entity. Used only when entity and kw are not sent."},
|
|
8
|
+
{"field": "brand", "type": "string", "required": "no", "description": "Brand text, 0 to 60 characters."},
|
|
9
|
+
{"field": "location", "type": "string", "required": "no", "description": "Location text, 0 to 100 characters."},
|
|
10
|
+
{"field": "lang", "type": "integer", "required": "no", "description": null},
|
|
11
|
+
{"field": "engine", "type": "integer", "required": "no", "description": null},
|
|
12
|
+
{"field": "mgptid", "type": "string", "required": "no", "description": "Encrypted MGPTID. Required for competitors action, not used for rank action."},
|
|
13
|
+
{"field": "k", "type": "string", "required": "yes", "description": "API key."}
|
|
14
|
+
],
|
|
15
|
+
"api_output": []
|
|
16
|
+
}
|