sv-cli 0.3.0__tar.gz → 0.5.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.5.0}/AGENT.md +24 -18
- {sv_cli-0.3.0 → sv_cli-0.5.0}/PKG-INFO +13 -7
- {sv_cli-0.3.0 → sv_cli-0.5.0}/README.md +11 -5
- {sv_cli-0.3.0 → sv_cli-0.5.0}/pyproject.toml +1 -1
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/__init__.py +1 -1
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/adapters.py +24 -4
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/api_client.py +8 -3
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/executor.py +2 -1
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/main.py +189 -30
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/resolver.py +34 -0
- sv_cli-0.5.0/tests/fixtures/ranklens_definitions.json +16 -0
- sv_cli-0.5.0/tests/fixtures/seogpt2_definitions.json +15 -0
- sv_cli-0.5.0/tests/test_executor.py +150 -0
- sv_cli-0.5.0/tests/test_help_filtering.py +80 -0
- {sv_cli-0.3.0 → sv_cli-0.5.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.5.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/.github/workflows/publish.yml +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/.github/workflows/release.yml +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/.github/workflows/test.yml +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/.gitignore +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/CHANGELOG.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/CODE_OF_CONDUCT.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/CONTRIBUTING.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/LICENSE +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/PULL_REQUEST_TEMPLATE.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/SECURITY.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/docs/agent-usage.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/docs/api-definition-format.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/docs/authentication.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/docs/commands.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/docs/examples.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/docs/installation.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/docs/troubleshooting.md +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/__init__.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/auth.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/better_keywords.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/call.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/config.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/content_quality.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/content_transformer.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/core_analysis.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/definitions.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/geo_audit.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/insight_igniter.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/marketplace_services.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/options.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/preliminary_audit.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/ranklens.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/seo_image.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/seo_mapping.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/seogpt.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/seogpt2.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/seogpt_compare.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/top_competitors.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/commands/topical_authority.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/config.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/definitions.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/errors.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/formatter.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/renderers/__init__.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/renderers/csv_renderer.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/renderers/json_renderer.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/renderers/markdown_renderer.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/renderers/table_renderer.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/renderers/text_renderer.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/schemas/__init__.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/schemas/api_response.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/schemas/config.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/schemas/tool_definition.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/tasks.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/src/sv_cli/utils.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/tests/fixtures/api_root.json +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/tests/fixtures/better_keywords_response.json +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/tests/fixtures/seogpt_definitions.json +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/tests/test_config.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.0}/tests/test_definitions.py +0 -0
- {sv_cli-0.3.0 → sv_cli-0.5.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 |
|
|
@@ -87,7 +87,7 @@ seogpt: generate, raw
|
|
|
87
87
|
seogpt2: create-task, get-task-status, get-result, raw
|
|
88
88
|
seogpt-compare: create-task, get-task-status, get-result, raw
|
|
89
89
|
seo-mapping: create-task, get-task-status, get-result, raw
|
|
90
|
-
topical-authority: topics,
|
|
90
|
+
topical-authority: topics, raw
|
|
91
91
|
top-competitors: analyze, raw
|
|
92
92
|
marketplace-services: search, raw
|
|
93
93
|
content-quality: analyze, raw
|
|
@@ -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
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: sv-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.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={
|
|
@@ -222,7 +242,7 @@ TOOL_ADAPTERS: dict[str, ToolAdapter] = {
|
|
|
222
242
|
command="topical-authority",
|
|
223
243
|
aliases=("topical",),
|
|
224
244
|
default_action="topics",
|
|
225
|
-
actions=("topics", "
|
|
245
|
+
actions=("topics", "raw"),
|
|
226
246
|
field_aliases=COMMON_FIELD_ALIASES,
|
|
227
247
|
option_aliases={
|
|
228
248
|
"modes": ("topicmode", "topic_mode", "mode", "modes"),
|
|
@@ -24,9 +24,13 @@ class APIResponse:
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class APIClient:
|
|
27
|
-
def __init__(self, *, debug: bool = False, console: Console | None = None) -> None:
|
|
27
|
+
def __init__(self, *, debug: bool = False, console: Console | None = None, client_type: str | None = None) -> None:
|
|
28
28
|
self.debug = debug
|
|
29
29
|
self.console = console or Console(stderr=True)
|
|
30
|
+
# Identifies this process to the SV API as "cli" or "mcp" (X-SV-Client header)
|
|
31
|
+
# for usage tracking. None means the caller didn't specify one - the header is
|
|
32
|
+
# simply omitted, and the API buckets the call as generic "api" usage.
|
|
33
|
+
self.client_type = client_type
|
|
30
34
|
|
|
31
35
|
def request_tool(
|
|
32
36
|
self,
|
|
@@ -35,13 +39,14 @@ class APIClient:
|
|
|
35
39
|
payload: dict[str, Any],
|
|
36
40
|
api_key: str | None,
|
|
37
41
|
method: str = "POST",
|
|
38
|
-
timeout: float =
|
|
42
|
+
timeout: float = 300.0,
|
|
39
43
|
) -> APIResponse:
|
|
40
44
|
final_payload = dict(payload)
|
|
41
45
|
if api_key and not any(key in final_payload for key in ("k", "api_key", "apikey", "key")):
|
|
42
46
|
final_payload["k"] = api_key
|
|
43
47
|
|
|
44
48
|
method = method.upper()
|
|
49
|
+
headers = {"X-SV-Client": self.client_type} if self.client_type else None
|
|
45
50
|
if self.debug:
|
|
46
51
|
parsed = urlparse(endpoint)
|
|
47
52
|
shown_path = parsed.path or endpoint
|
|
@@ -50,7 +55,7 @@ class APIClient:
|
|
|
50
55
|
|
|
51
56
|
started = time.perf_counter()
|
|
52
57
|
try:
|
|
53
|
-
with httpx.Client(timeout=timeout, follow_redirects=True) as client:
|
|
58
|
+
with httpx.Client(timeout=timeout, follow_redirects=True, headers=headers) as client:
|
|
54
59
|
if method == "GET":
|
|
55
60
|
response = client.get(endpoint, params=final_payload)
|
|
56
61
|
elif method in {"POST", "PUT", "PATCH"}:
|
|
@@ -120,6 +120,7 @@ def execute_tool(
|
|
|
120
120
|
raw_payload: dict[str, Any] | None = None,
|
|
121
121
|
method: str = "POST",
|
|
122
122
|
console: Console | None = None,
|
|
123
|
+
client_type: str | None = None,
|
|
123
124
|
) -> Any:
|
|
124
125
|
console = console or Console()
|
|
125
126
|
definitions = DefinitionsManager(runtime.base_url)
|
|
@@ -149,7 +150,7 @@ def execute_tool(
|
|
|
149
150
|
non_interactive=runtime.non_interactive,
|
|
150
151
|
)
|
|
151
152
|
|
|
152
|
-
client = APIClient(debug=runtime.debug, console=Console(stderr=True))
|
|
153
|
+
client = APIClient(debug=runtime.debug, console=Console(stderr=True), client_type=client_type)
|
|
153
154
|
response = client.request_tool(endpoint=str(endpoint), payload=payload, api_key=api_key, method=method)
|
|
154
155
|
data = response.data
|
|
155
156
|
|