direct-cli 0.3.8__tar.gz → 0.3.9__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.
- direct_cli-0.3.9/CHANGELOG.md +110 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/CLAUDE.md +9 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/PKG-INFO +136 -9
- {direct_cli-0.3.8 → direct_cli-0.3.9}/README.md +135 -8
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/cli.py +14 -1
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/adgroups.py +53 -9
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/ads.py +271 -39
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/bidmodifiers.py +93 -45
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/bids.py +5 -4
- direct_cli-0.3.9/direct_cli/commands/campaigns.py +1061 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/dynamicads.py +4 -4
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/keywordbids.py +6 -0
- direct_cli-0.3.9/direct_cli/commands/keywords.py +626 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/strategies.py +198 -24
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/output.py +72 -2
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/utils.py +45 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/wsdl_coverage.py +31 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli.egg-info/PKG-INFO +136 -9
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli.egg-info/SOURCES.txt +4 -0
- direct_cli-0.3.9/docs/audits/issue-198-mutating-wsdl-audit.md +148 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/pyproject.toml +1 -1
- direct_cli-0.3.9/scripts/sandbox_write_audit.py +198 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/scripts/sandbox_write_live.py +143 -14
- {direct_cli-0.3.8 → direct_cli-0.3.9}/scripts/test_sandbox_write.sh +7 -1
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/api_coverage_payloads.py +120 -0
- direct_cli-0.3.9/tests/cassettes/test_integration_write/TestWriteFeeds.test_add_update_delete.yaml +166 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteSmartAdTargets.test_add_update_delete.yaml +16 -16
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/conftest.py +38 -9
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_api_coverage.py +31 -2
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_cli.py +85 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_dry_run.py +1567 -93
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_integration_write.py +39 -21
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_low_coverage_payloads.py +129 -13
- direct_cli-0.3.9/tests/test_sandbox_write_audit.py +67 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_smoke_matrix.py +202 -0
- direct_cli-0.3.9/tests/test_wsdl_parity_gate.py +569 -0
- direct_cli-0.3.8/CHANGELOG.md +0 -43
- direct_cli-0.3.8/direct_cli/commands/campaigns.py +0 -558
- direct_cli-0.3.8/direct_cli/commands/keywords.py +0 -322
- direct_cli-0.3.8/tests/cassettes/test_integration_write/TestWriteFeeds.test_add_update_delete.yaml +0 -58
- {direct_cli-0.3.8 → direct_cli-0.3.9}/.env.example +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/.github/copilot-instructions.md +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/.github/workflows/api-coverage.yml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/.github/workflows/claude.yml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/.github/workflows/quality.yml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/.gitignore +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/AGENTS.md +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/MANIFEST.in +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/__init__.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/_deprecated.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/_smoke_probes.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/_vendor/__init__.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/_vendor/tapi_yandex_direct/__init__.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/_vendor/tapi_yandex_direct/endpoints.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/_vendor/tapi_yandex_direct/exceptions.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/_vendor/tapi_yandex_direct/resource_mapping.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.pyi +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/_vendor/tapi_yandex_direct/v4/__init__.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.pyi +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/_vendor/tapi_yandex_direct/v4/resource_mapping.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/api.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/auth.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/__init__.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/adextensions.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/adimages.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/advideos.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/agencyclients.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/audiencetargets.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/auth.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/balance.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/businesses.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/changes.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/clients.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/creatives.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/dictionaries.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/dynamicfeedadtargets.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/feeds.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/keywordsresearch.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/leads.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/negativekeywordsharedsets.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/reports.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/retargeting.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/sitelinks.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/smartadtargets.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/turbopages.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/v4account.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/v4events.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/v4finance.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/v4forecast.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/v4goals.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/v4shells.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/v4tags.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/v4wordstat.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/commands/vcards.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/reports_coverage.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/smoke_matrix.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/v4/__init__.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/v4/money.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli/v4_contracts.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli.egg-info/dependency_links.txt +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli.egg-info/entry_points.txt +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli.egg-info/requires.txt +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/direct_cli.egg-info/top_level.txt +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/docs/superpowers/plans/2026-04-12-issue-32-completion.md +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/docs/superpowers/specs/2026-04-23-vendor-tapi-yandex-direct-design.md +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/scripts/anonymize_cassettes.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/scripts/build_api_coverage_checklist.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/scripts/build_api_coverage_report.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/scripts/check_reports_drift.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/scripts/check_wsdl_drift.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/scripts/patch_vendor_imports.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/scripts/refresh_reports_cache.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/scripts/refresh_wsdl_cache.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/scripts/release_pypi.sh +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/scripts/test_dangerous_commands.sh +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/scripts/test_safe_commands.sh +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/scripts/update_vendor.sh +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/setup.cfg +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/setup.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/API_COVERAGE.md +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/API_ISSUE_AUDIT.md +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/MANUAL_COVERAGE.md +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/__init__.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/_orphan_store.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteAdExtensions.test_add_delete.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteAdGroups.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteAdImages.test_add_delete.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteAds.test_add_text_ad_update_delete.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteAudienceTargets.test_add_delete.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteBidModifiersAdd.test_add_delete_mobile.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteBidModifiersSet.test_set_without_id_is_rejected.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteBids.test_set_bid.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteBidsRead.test_bids_get.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteBidsRead.test_bids_set_auto.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteCampaignDraftLifecycle.test_draft_create_get_delete.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteCampaigns.test_campaign_lifecycle.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteDynamicAds.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteKeywordBids.test_set_keyword_bid.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteKeywords.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteNegativeKeywordSharedSets.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteRetargeting.test_add_delete.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteRetargetingUpdate.test_retargeting_update.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteSitelinks.test_add_delete.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteStrategies.test_strategies_lifecycle.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_integration_write/TestWriteVCards.test_add_delete.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_v5_live_write/test_v5_live_draft_adgroups_add_update_delete.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_v5_live_write/test_v5_live_draft_adimages_add_get_delete.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_v5_live_write/test_v5_live_draft_ads_add_update_delete.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_v5_live_write/test_v5_live_draft_ads_suspend_resume_archive_unarchive.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_v5_live_write/test_v5_live_draft_advideos_add_get.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_add_delete.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_suspend_resume.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_v5_live_write/test_v5_live_draft_bids_set.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_v5_live_write/test_v5_live_draft_campaign_create_get_delete.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_v5_live_write/test_v5_live_draft_creatives_chain_advideo_to_creative.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_v5_live_write/test_v5_live_draft_dynamicads_add_delete.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_v5_live_write/test_v5_live_draft_dynamicads_suspend_resume.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywordbids_set.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywords_add_update_delete.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywords_suspend_resume.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_v5_live_write/test_v5_live_draft_sitelinks_add_get_delete.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_v5_live_write/test_v5_live_draft_smartadtargets_add_update_delete.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/cassettes/test_v5_live_write/test_v5_live_draft_smartadtargets_suspend_resume.yaml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/fixtures/test-video.mp4 +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/reports_cache/raw/fields-list.html +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/reports_cache/raw/headers.html +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/reports_cache/raw/period.html +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/reports_cache/raw/spec.html +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/reports_cache/raw/type.html +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/reports_cache/spec.json +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_auth_bw.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_auth_oauth.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_auth_op.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_balance.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_cli_contract.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_comprehensive.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_integration.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_reports_drift.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_reports_parsing.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_transport_contract.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_v4_contracts.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_v4_foundation.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_v4_live_contracts.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_v4_safety.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_v4account.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_v4events.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_v4finance_money.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_v4finance_read.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_v4forecast.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_v4goals.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_v4tags.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_v4wordstat.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_v5_live_write.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/test_vendor_imports.py +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/adextensions.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/adgroups.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/adimages.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/ads.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/advideos.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/agencyclients.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/audiencetargets.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/bidmodifiers.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/bids.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/businesses.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/campaigns.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/changes.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/clients.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/creatives.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/dictionaries.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/dynamicfeedadtargets.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/dynamictextadtargets.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/feeds.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/imports/adextensiontypes.xsd +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/imports/general.xsd +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/imports/generalclients.xsd +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/keywordbids.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/keywords.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/keywordsresearch.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/leads.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/negativekeywordsharedsets.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/retargetinglists.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/sitelinks.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/smartadtargets.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/strategies.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/turbopages.xml +0 -0
- {direct_cli-0.3.8 → direct_cli-0.3.9}/tests/wsdl_cache/vcards.xml +0 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.9
|
|
4
|
+
|
|
5
|
+
**Added:**
|
|
6
|
+
|
|
7
|
+
- `direct keywords add` now supports batch mode via `--from-file PATH`
|
|
8
|
+
(JSONL, one keyword object per line) or `--keywords-json '[…]'`
|
|
9
|
+
(inline JSON array). The CLI splits input into chunks of 10 — the
|
|
10
|
+
Yandex Direct API limit for `keywords.add` documented at
|
|
11
|
+
https://yandex.ru/dev/direct/doc/dg/objects/keyword.html — preserves
|
|
12
|
+
input order, and merges `AddResults` from every chunk into a single
|
|
13
|
+
response. Item-level errors do not abort the batch. If a chunk-level
|
|
14
|
+
exception breaks the loop, already-created Ids are printed to stderr
|
|
15
|
+
with a "Partial success before failure" header so a retry doesn't
|
|
16
|
+
duplicate them. Pre-flight warning when any AdGroupId in the input
|
|
17
|
+
exceeds the per-ad-group limit of 200 keywords (the API rejects the
|
|
18
|
+
excess with per-item errors; warning surfaces this before any chunk
|
|
19
|
+
is sent). Row keys use WSDL CamelCase (`Keyword`, `AdGroupId`,
|
|
20
|
+
`Bid`, `ContextBid`, `UserParam1`, `UserParam2`); unknown keys are
|
|
21
|
+
rejected with the row number, and JSON booleans are explicitly
|
|
22
|
+
rejected to prevent silent `True → 1` coercion. `--adgroup-id` is
|
|
23
|
+
optional in batch mode and acts as a default, overridable per row.
|
|
24
|
+
`--dry-run` prints the first chunk's payload alongside
|
|
25
|
+
`{chunks, totalItems, chunkSize}`. Single-item mode (`--keyword`)
|
|
26
|
+
is unchanged (#203).
|
|
27
|
+
- `direct campaigns add` typed flags for CPA strategies and
|
|
28
|
+
cross-cutting `CampaignAddItem` fields: `--goal-id` (single
|
|
29
|
+
Metrika goal), `--crr` (CRR percentage for
|
|
30
|
+
`PAY_FOR_CONVERSION_CRR`),
|
|
31
|
+
`--priority-goals goal_id:value,…` (multi-goal CPA via
|
|
32
|
+
WSDL `PriorityGoalsArray`), `--average-cpa MICRO_RUBLES`,
|
|
33
|
+
`--bid-ceiling MICRO_RUBLES`, `--counter-ids`
|
|
34
|
+
(TextCampaign/DynamicTextCampaign), `--notification JSON`
|
|
35
|
+
(`CampaignBase.Notification` with `SmsSettings`/`EmailSettings`
|
|
36
|
+
shape validation), `--time-targeting JSON`
|
|
37
|
+
(`CampaignAddItem.TimeTargeting` with `HolidaysSchedule`
|
|
38
|
+
shape validation). Strategy-subtype compatibility is enforced
|
|
39
|
+
via `UsageError` at CLI level both ways: WSDL-incompatible flags
|
|
40
|
+
are rejected (e.g. `--average-cpa` for `HIGHEST_POSITION`,
|
|
41
|
+
`--crr` outside `PAY_FOR_CONVERSION_CRR`,
|
|
42
|
+
`--bid-ceiling` for `PayForConversionCrr` /
|
|
43
|
+
`PayForConversionMultipleGoals`), and WSDL `minOccurs=1`
|
|
44
|
+
fields are demanded up-front (e.g. picking `AVERAGE_CPA`
|
|
45
|
+
without `--average-cpa`+`--goal-id`, or `PAY_FOR_CONVERSION_CRR`
|
|
46
|
+
without `--crr`+`--goal-id`, or `*_MULTIPLE_GOALS` without
|
|
47
|
+
`--priority-goals`, all fail at the CLI instead of the API).
|
|
48
|
+
Closes #204.
|
|
49
|
+
|
|
50
|
+
**Notes:**
|
|
51
|
+
|
|
52
|
+
- Issue #204 also requested `--goals` (array) and
|
|
53
|
+
`--network-settings`; both were dropped after WSDL audit. Yandex
|
|
54
|
+
`Strategy*Add` complex types declare only scalar `GoalId`, so
|
|
55
|
+
multi-goal CPA is shipped through `--priority-goals` instead
|
|
56
|
+
(correct WSDL path: `TextCampaign.PriorityGoals.Items[].GoalId/Value`).
|
|
57
|
+
No `NetworkSettings` field exists on `CampaignAddItem` /
|
|
58
|
+
`TextCampaignAddItem` / `DynamicTextCampaignAddItem` /
|
|
59
|
+
`SmartCampaignAddItem` in the current `campaigns.xml` WSDL.
|
|
60
|
+
|
|
61
|
+
**Fixed:**
|
|
62
|
+
|
|
63
|
+
- Refreshed `TestWriteFeeds` and `TestWriteSmartAdTargets` VCR cassettes against a real sandbox, dropped the `_FEED_REGRESSION_PATTERNS` skip workaround, and updated `sandbox_feed` / `sandbox_smart_adgroup` fixtures to pass the now-WSDL-required `--business-type RETAIL` (FeedAddItem) and `--counter-id` (SmartCampaignAddItem). Tests now skip only on genuine sandbox limitations, not on the missing-option proxy that the workaround papered over (#206, fallout from #201). Test invocation now also passes `--login` and prefers env vars over an active `direct auth` profile, matching the inversion documented in CLAUDE.md.
|
|
64
|
+
- WSDL parity gate now fails fast when `COMMAND_WSDL_MAP` points at a container that does not exist in the WSDL request schema. The previous skip-on-empty-required-list silently masked typo'd container names (#206, Copilot follow-up from #205).
|
|
65
|
+
- `WSDL_FIELD_TO_CLI_OPTION` no longer references the non-existent generic `--file` flag. `SourceType` maps to `{--url}` and `ImageData` maps to `{--image-data, --image-file}`, matching the real CLI surface (#206, Copilot follow-up from #205).
|
|
66
|
+
- `direct bidmodifiers set --help` no longer advertises the rejected `--campaign-id`/`--type` legacy path; the rejection now happens via an eager Click callback (same pattern as deprecated `keywords update` options), preserving the existing `UsageError` message for regression coverage (#206, Copilot follow-up from #214).
|
|
67
|
+
|
|
68
|
+
**Refs:** Closes issues #122, #138, #198, #202, #203, #204, #206, #207.
|
|
69
|
+
|
|
70
|
+
## 0.3.8
|
|
71
|
+
|
|
72
|
+
**BREAKING CHANGES:**
|
|
73
|
+
|
|
74
|
+
- `direct ads update` now requires `--type {TEXT_AD,TEXT_IMAGE_AD,MOBILE_APP_AD}`. Scripts that called `ads update` with only field flags will fail with `Missing option '--type'`. Mirrors the WSDL one-of choice between TextAd/TextImageAd/MobileAppAd update subtypes (PR #197).
|
|
75
|
+
- `direct ads add --type TEXT_IMAGE_AD` rejects `--title/--text` (TEXT_IMAGE_AD has no such WSDL fields). `direct ads update --status` rejected — use `ads suspend/resume/archive/unarchive` for status changes (PR #190).
|
|
76
|
+
- `direct ads add --type MOBILE_APP_AD --href` rejected — MobileAppAd uses `--tracking-url`, not `--href` (PR #196).
|
|
77
|
+
- `direct feeds add` now requires `--business-type {RETAIL,HOTELS,REALTY,AUTOMOBILES,FLIGHTS,OTHER}`. Mirrors WSDL FeedAddItem.BusinessType (minOccurs=1) (PR #201).
|
|
78
|
+
|
|
79
|
+
**Schema gate — mutating ops parity:**
|
|
80
|
+
|
|
81
|
+
- Extended the WSDL `*FieldNames` schema gate (introduced for `get` in 0.3.7) to mutating operations (`add/update/set/setBids/lifecycle`). Added per-operation waiver granularity via `SCHEMA_GATE_OPERATION_WAIVERS` (PR #181).
|
|
82
|
+
- Promoted dynamicads, bidmodifiers add/set, adimages/advideos/vcards add (media payloads), adextensions/retargeting/feeds.add typed fixtures to `PAYLOAD_CASES` (PRs #184, #185, #187, #188).
|
|
83
|
+
- Added MOBILE_APP_AD branch to `ads add` mirroring WSDL `MobileAppAdAdd` (PR #190).
|
|
84
|
+
- `bidmodifiers.delete` correctly classified as a real destructive WSDL operation and added to schema gate (PR #194); the earlier "Helper/legacy surface" rationale was a mis-classification — see post-mortem in #199 / PR #200.
|
|
85
|
+
|
|
86
|
+
**Strict WSDL parity policy:**
|
|
87
|
+
|
|
88
|
+
- Documented "Strict WSDL parity" principle in `CLAUDE.md`: `DRY_RUN_PAYLOAD_EXCLUSIONS` may only contain entries from five legitimate categories (read-path `*.get`, runtime-deprecated, v4-not-in-v5-wsdl, custom non-RPC endpoints, methods covered by `tests/test_dry_run.py`). New guard test `test_dry_run_exclusions_have_no_helper_or_legacy_rationale` fails CI if any rationale uses banned phrases (PR #200).
|
|
89
|
+
|
|
90
|
+
**Integration test coverage:**
|
|
91
|
+
|
|
92
|
+
- Added read-only sandbox integration tests for `changes`, `keywordsresearch`, `balance` (PR #186).
|
|
93
|
+
- Added v5 write integration coverage for `strategies` lifecycle, `retargeting update`, `bids get/set-auto`, plus `auth status/list` read-only tests (PR #189).
|
|
94
|
+
- Re-recorded TestWriteBidsRead cassettes against live API and rewrote host to sandbox so the bids endpoints get real coverage in replay mode (PR #193).
|
|
95
|
+
|
|
96
|
+
**CI infrastructure:**
|
|
97
|
+
|
|
98
|
+
- Switched Claude code-review GitHub Action from default (Sonnet 4.5) to Claude Opus 4.7 for deeper PR review (PR #192).
|
|
99
|
+
|
|
100
|
+
**Refs:** Closes issues #118, #136, #137, #175, #176, #180, #183, #191, #199.
|
|
101
|
+
|
|
102
|
+
## 0.3.3
|
|
103
|
+
|
|
104
|
+
**BREAKING CHANGE:** OAuth profiles created before 0.3.3 (without `refresh_token` and `expires_at`) are no longer accepted. Any such profile will fail immediately with an "incomplete profile" error. Run `direct auth login --profile <name>` to re-authenticate and create a valid 0.3.3 profile.
|
|
105
|
+
|
|
106
|
+
- Added refresh token persistence for OAuth profiles.
|
|
107
|
+
- Added automatic OAuth access token refresh before expiry.
|
|
108
|
+
- Added `expires_in` details to `direct auth status`.
|
|
109
|
+
- Added JSON output for `direct auth status`.
|
|
110
|
+
- Kept `direct auth login --oauth-token` as a manual access-token import without auto-refresh.
|
|
@@ -52,6 +52,15 @@ Click group-of-groups. Each Yandex Direct API resource = one file in `direct_cli
|
|
|
52
52
|
|
|
53
53
|
A guard in `tests/test_api_coverage.py::test_dry_run_exclusions_have_no_helper_or_legacy_rationale` enforces this — any rationale outside those five categories that uses the banned phrasing is a mis-classification: write a `PAYLOAD_CASES` fixture instead. See post-mortem in issue #199.
|
|
54
54
|
|
|
55
|
+
**WSDL parity gate:** `tests/test_wsdl_parity_gate.py` runs four invariant checks across every `add`/`update`/`set` command in `WRITE_SANDBOX`:
|
|
56
|
+
|
|
57
|
+
1. *Empty subtype no-op* — a mutating command with only the resource ID must refuse to send the payload (no silent no-op on the live API).
|
|
58
|
+
2. *Silent data loss* — a typed flag that does not belong to the chosen `--type` must raise `UsageError`, not be dropped.
|
|
59
|
+
3. *WSDL `minOccurs=1` not validated* — every required WSDL item field must be enforced either via Click `required=True` *or* a documented `UsageError` body check (listed in `INTERNAL_VALIDATION`).
|
|
60
|
+
4. *Strategy enum drift* — `STRATEGY_TYPES` (`direct_cli/commands/strategies.py`) must equal the subtype-of-one field names in `StrategyAddItem`.
|
|
61
|
+
|
|
62
|
+
Adding a new mutating command requires extending `COMMAND_WSDL_MAP` in `tests/test_wsdl_parity_gate.py` (the coverage test fails otherwise) and, if the WSDL request has a non-mechanical field name, also `WSDL_FIELD_TO_CLI_OPTION`. Tracked in issue #198.
|
|
63
|
+
|
|
55
64
|
**SelectionCriteria:** Resources like `adgroups`, `ads`, `keywords` require at least one of `Ids`, `CampaignIds`, or `AdGroupIds` — otherwise API error 4001.
|
|
56
65
|
|
|
57
66
|
**Error handling:** All commands wrap API calls in `try/except Exception` → `print_error(str(e))` + `raise click.Abort()`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: direct-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.9
|
|
4
4
|
Summary: Command-line interface for Yandex Direct API
|
|
5
5
|
Author: axisrow
|
|
6
6
|
License: MIT
|
|
@@ -384,6 +384,15 @@ direct campaigns add --name "My Campaign" --start-date 2024-02-01 --type TEXT_CA
|
|
|
384
384
|
direct campaigns add --name "Dynamic Campaign" --start-date 2024-02-01 --type DYNAMIC_TEXT_CAMPAIGN --setting ADD_METRICA_TAG=NO --search-strategy HIGHEST_POSITION --network-strategy SERVING_OFF --dry-run
|
|
385
385
|
direct campaigns add --name "Smart Campaign" --start-date 2024-02-01 --type SMART_CAMPAIGN --network-strategy AVERAGE_CPC_PER_FILTER --filter-average-cpc 1000000 --counter-id 123 --dry-run
|
|
386
386
|
|
|
387
|
+
# CPA strategy (single goal): --goal-id required, --average-cpa / --bid-ceiling are micro-rubles
|
|
388
|
+
direct campaigns add --name "CPA Campaign" --start-date 2026-06-01 --type TEXT_CAMPAIGN --search-strategy AVERAGE_CPA --network-strategy SERVING_OFF --goal-id 1234567 --average-cpa 500000000 --bid-ceiling 1000000000 --counter-ids 111,222 --dry-run
|
|
389
|
+
|
|
390
|
+
# Multi-goal CPA via PriorityGoals (goal_id:value pairs, WSDL PriorityGoalsItem)
|
|
391
|
+
direct campaigns add --name "Multi-Goal CPA" --start-date 2026-06-01 --type TEXT_CAMPAIGN --search-strategy AVERAGE_CPA_MULTIPLE_GOALS --network-strategy SERVING_OFF --priority-goals 1234567:80,9876543:20 --bid-ceiling 1000000000 --dry-run
|
|
392
|
+
|
|
393
|
+
# Notification (Sms/Email) and TimeTargeting accept JSON with WSDL CamelCase keys
|
|
394
|
+
direct campaigns add --name "Notify+Schedule" --start-date 2026-06-01 --type TEXT_CAMPAIGN --search-strategy HIGHEST_POSITION --network-strategy SERVING_OFF --notification '{"EmailSettings":{"Email":"ops@example.com","SendWarnings":"YES"}}' --time-targeting '{"Schedule":["1A0123456789ABCDEFGHIJKL"],"ConsiderWorkingWeekends":"YES"}' --dry-run
|
|
395
|
+
|
|
387
396
|
# Update / lifecycle
|
|
388
397
|
direct campaigns update --id 12345 --name "New Name" --status SUSPENDED --budget 100000000 --start-date 2024-02-10 --end-date 2024-03-01
|
|
389
398
|
direct campaigns suspend --id 12345
|
|
@@ -410,11 +419,21 @@ direct adgroups delete --id 67890
|
|
|
410
419
|
direct ads get --campaign-ids 1,2,3
|
|
411
420
|
direct ads get --adgroup-ids 45678 --format table
|
|
412
421
|
direct ads add --adgroup-id 12345 --type TEXT_AD --title "Title" --text "Ad text" --href "https://example.com" --dry-run
|
|
413
|
-
direct ads add --adgroup-id 12345 --type
|
|
414
|
-
direct ads
|
|
422
|
+
direct ads add --adgroup-id 12345 --type TEXT_AD --title "Title" --text "Ad text" --href "https://example.com" --title2 "Second headline" --display-url-path "deals" --mobile YES --vcard-id 111 --sitelink-set-id 222 --turbo-page-id 333 --ad-extensions "444,555" --dry-run
|
|
423
|
+
direct ads add --adgroup-id 12345 --type TEXT_IMAGE_AD --image-hash abcdefghijklmnopqrst --href "https://example.com" --turbo-page-id 555 --dry-run
|
|
424
|
+
direct ads update --id 99999 --type TEXT_AD --title "New Title" --text "New text" --href "https://example.com"
|
|
425
|
+
direct ads update --id 99999 --type TEXT_AD --image-hash abcdefghijklmnopqrst
|
|
426
|
+
direct ads update --id 99999 --type TEXT_AD --title2 "New second headline" --vcard-id 222
|
|
415
427
|
direct ads delete --id 99999
|
|
416
428
|
```
|
|
417
429
|
|
|
430
|
+
Available TEXT_AD typed flags for `ads add` / `ads update`: `--title`, `--text`,
|
|
431
|
+
`--href`, `--image-hash`, `--title2`, `--display-url-path`, `--vcard-id`,
|
|
432
|
+
`--sitelink-set-id`, `--turbo-page-id`. `--mobile` (default `NO`) and
|
|
433
|
+
`--ad-extensions` are `ads add`-only — `TextAdUpdate` does not contain `Mobile`,
|
|
434
|
+
and ad-extension updates go through the `CalloutSetting` WSDL field, which is
|
|
435
|
+
not yet exposed by the CLI. TEXT_IMAGE_AD additionally accepts `--turbo-page-id`.
|
|
436
|
+
|
|
418
437
|
#### Keywords
|
|
419
438
|
|
|
420
439
|
```bash
|
|
@@ -424,6 +443,33 @@ direct keywords update --id 88888 --keyword "updated keyword text"
|
|
|
424
443
|
direct keywords delete --id 88888
|
|
425
444
|
```
|
|
426
445
|
|
|
446
|
+
**Batch keyword upload** (CLI auto-chunks to the API limit of 10 per request):
|
|
447
|
+
|
|
448
|
+
```bash
|
|
449
|
+
# From a JSONL file (one keyword object per line)
|
|
450
|
+
direct keywords add --adgroup-id 12345 --from-file keywords.jsonl
|
|
451
|
+
|
|
452
|
+
# Inline JSON array
|
|
453
|
+
direct keywords add --keywords-json '[{"Keyword":"buy laptop","Bid":10000000},{"Keyword":"buy desktop"}]'
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
Example `keywords.jsonl`:
|
|
457
|
+
|
|
458
|
+
```jsonl
|
|
459
|
+
{"Keyword":"buy laptop","Bid":10000000,"UserParam1":"src=ad1"}
|
|
460
|
+
{"Keyword":"buy desktop","ContextBid":5000000}
|
|
461
|
+
{"Keyword":"купить ноутбук","AdGroupId":99999}
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
- Row keys use WSDL CamelCase: `Keyword`, `AdGroupId`, `Bid`, `ContextBid`, `UserParam1`, `UserParam2`.
|
|
465
|
+
- `--adgroup-id` provides the default group ID; rows can override it via per-row `AdGroupId`.
|
|
466
|
+
- Each effective row must resolve `Keyword` and `AdGroupId`; unknown fields are rejected with the row number.
|
|
467
|
+
- API limit: 10 items per `keywords.add` request — see [Yandex Direct docs](https://yandex.ru/dev/direct/doc/dg/objects/keyword.html). The CLI sends as many chunks as needed and merges `AddResults`.
|
|
468
|
+
- API limit: 200 keywords per ad group. The CLI prints a warning if any `AdGroupId` in the input exceeds it; the API rejects the excess as per-item errors.
|
|
469
|
+
- Item-level errors from the API do not abort the batch; the merged output includes successes and per-item errors.
|
|
470
|
+
- If a chunk fails with a network-level error mid-batch, already-created Ids are printed to stderr (`Partial success before failure`) so a retry doesn't duplicate them.
|
|
471
|
+
- `--dry-run` shows the first chunk's payload plus `{chunks, totalItems, chunkSize}`.
|
|
472
|
+
|
|
427
473
|
#### Reports
|
|
428
474
|
|
|
429
475
|
```bash
|
|
@@ -480,8 +526,8 @@ direct dynamicads set-bids --id 789 --bid 12500000 --context-bid 9000000 --prior
|
|
|
480
526
|
|
|
481
527
|
# Shared bidding strategies
|
|
482
528
|
direct strategies get --limit 5
|
|
483
|
-
direct strategies add --name "Shared Clicks" --type WbMaximumClicks --spend-limit 1000000000 --
|
|
484
|
-
direct strategies update --id 42 --type WbMaximumClicks --
|
|
529
|
+
direct strategies add --name "Shared Clicks" --type WbMaximumClicks --weekly-spend-limit 1000000000 --bid-ceiling 30000000 --dry-run
|
|
530
|
+
direct strategies update --id 42 --type WbMaximumClicks --weekly-spend-limit 35000000 --dry-run
|
|
485
531
|
direct strategies archive --id 42 --dry-run
|
|
486
532
|
|
|
487
533
|
# Dynamic feed ad targets
|
|
@@ -558,6 +604,17 @@ Use `--dry-run` on `add` / `update` commands to preview the API request before s
|
|
|
558
604
|
direct campaigns add --name "Test" --start-date 2024-01-01 --dry-run
|
|
559
605
|
```
|
|
560
606
|
|
|
607
|
+
### API Errors
|
|
608
|
+
|
|
609
|
+
Yandex Direct can return a successful HTTP response that still contains
|
|
610
|
+
item-level `Errors` for one object. Direct CLI treats those responses as
|
|
611
|
+
failed operations: it exits non-zero and prints the error code, message, and
|
|
612
|
+
details.
|
|
613
|
+
|
|
614
|
+
Code `8800` with `Object not found` usually means the object is not available
|
|
615
|
+
under the current `Client-Login` or account. Check the selected `--login`,
|
|
616
|
+
`YANDEX_DIRECT_LOGIN`, or auth profile before retrying.
|
|
617
|
+
|
|
561
618
|
### Testing
|
|
562
619
|
|
|
563
620
|
Four tiers of tests live under `tests/`:
|
|
@@ -666,6 +723,12 @@ For `v4account` sandbox smoke, `enable-shared-account` uses
|
|
|
666
723
|
`account-management` requires `YANDEX_DIRECT_V4ACCOUNT_ACCOUNT_ID`; without it
|
|
667
724
|
the runner reports `NOT_COVERED` for that command.
|
|
668
725
|
|
|
726
|
+
`clients.update` is opt-in because it mutates client-level account metadata.
|
|
727
|
+
Set `YANDEX_DIRECT_CLIENTS_UPDATE_LOGIN` to an expendable sandbox
|
|
728
|
+
`Client-Login`; the runner passes it through `--login` and updates only
|
|
729
|
+
`ClientInfo` with a unique smoke marker. Without that variable, the runner
|
|
730
|
+
reports `NOT_COVERED` for `clients.update`.
|
|
731
|
+
|
|
669
732
|
#### Re-recording write cassettes
|
|
670
733
|
|
|
671
734
|
The `integration_write` pytest tier still replays stored write-test traffic
|
|
@@ -1011,6 +1074,15 @@ direct campaigns add --name "Моя кампания" --start-date 2024-02-01 --
|
|
|
1011
1074
|
direct campaigns add --name "Динамическая кампания" --start-date 2024-02-01 --type DYNAMIC_TEXT_CAMPAIGN --setting ADD_METRICA_TAG=NO --search-strategy HIGHEST_POSITION --network-strategy SERVING_OFF --dry-run
|
|
1012
1075
|
direct campaigns add --name "Смарт-кампания" --start-date 2024-02-01 --type SMART_CAMPAIGN --network-strategy AVERAGE_CPC_PER_FILTER --filter-average-cpc 1000000 --counter-id 123 --dry-run
|
|
1013
1076
|
|
|
1077
|
+
# CPA-стратегия (одна цель): --goal-id обязателен, --average-cpa/--bid-ceiling — micro-рубли
|
|
1078
|
+
direct campaigns add --name "CPA-кампания" --start-date 2026-06-01 --type TEXT_CAMPAIGN --search-strategy AVERAGE_CPA --network-strategy SERVING_OFF --goal-id 1234567 --average-cpa 500000000 --bid-ceiling 1000000000 --counter-ids 111,222 --dry-run
|
|
1079
|
+
|
|
1080
|
+
# Мульти-целевой CPA через PriorityGoals (пары goal_id:value, WSDL PriorityGoalsItem)
|
|
1081
|
+
direct campaigns add --name "Мульти-целевой CPA" --start-date 2026-06-01 --type TEXT_CAMPAIGN --search-strategy AVERAGE_CPA_MULTIPLE_GOALS --network-strategy SERVING_OFF --priority-goals 1234567:80,9876543:20 --bid-ceiling 1000000000 --dry-run
|
|
1082
|
+
|
|
1083
|
+
# Notification (Sms/Email) и TimeTargeting принимают JSON с CamelCase ключами WSDL
|
|
1084
|
+
direct campaigns add --name "Уведомления+Расписание" --start-date 2026-06-01 --type TEXT_CAMPAIGN --search-strategy HIGHEST_POSITION --network-strategy SERVING_OFF --notification '{"EmailSettings":{"Email":"ops@example.com","SendWarnings":"YES"}}' --time-targeting '{"Schedule":["1A0123456789ABCDEFGHIJKL"],"ConsiderWorkingWeekends":"YES"}' --dry-run
|
|
1085
|
+
|
|
1014
1086
|
# Обновление и управление статусом
|
|
1015
1087
|
direct campaigns update --id 12345 --name "Новое название" --status SUSPENDED --budget 100000000 --start-date 2024-02-10 --end-date 2024-03-01
|
|
1016
1088
|
direct campaigns suspend --id 12345
|
|
@@ -1037,11 +1109,22 @@ direct adgroups delete --id 67890
|
|
|
1037
1109
|
direct ads get --campaign-ids 1,2,3
|
|
1038
1110
|
direct ads get --adgroup-ids 45678 --format table
|
|
1039
1111
|
direct ads add --adgroup-id 12345 --type TEXT_AD --title "Заголовок" --text "Текст объявления" --href "https://example.com" --dry-run
|
|
1040
|
-
direct ads add --adgroup-id 12345 --type
|
|
1041
|
-
direct ads
|
|
1112
|
+
direct ads add --adgroup-id 12345 --type TEXT_AD --title "Заголовок" --text "Текст" --href "https://example.com" --title2 "Второй заголовок" --display-url-path "deals" --mobile YES --vcard-id 111 --sitelink-set-id 222 --turbo-page-id 333 --ad-extensions "444,555" --dry-run
|
|
1113
|
+
direct ads add --adgroup-id 12345 --type TEXT_IMAGE_AD --image-hash abcdefghijklmnopqrst --href "https://example.com" --turbo-page-id 555 --dry-run
|
|
1114
|
+
direct ads update --id 99999 --type TEXT_AD --title "Новый заголовок" --text "Новый текст" --href "https://example.com"
|
|
1115
|
+
direct ads update --id 99999 --type TEXT_AD --image-hash abcdefghijklmnopqrst
|
|
1116
|
+
direct ads update --id 99999 --type TEXT_AD --title2 "Новый второй заголовок" --vcard-id 222
|
|
1042
1117
|
direct ads delete --id 99999
|
|
1043
1118
|
```
|
|
1044
1119
|
|
|
1120
|
+
Доступные типизированные флаги TEXT_AD для `ads add` / `ads update`:
|
|
1121
|
+
`--title`, `--text`, `--href`, `--image-hash`, `--title2`, `--display-url-path`,
|
|
1122
|
+
`--vcard-id`, `--sitelink-set-id`, `--turbo-page-id`. `--mobile`
|
|
1123
|
+
(default `NO`) и `--ad-extensions` доступны только в `ads add` — WSDL
|
|
1124
|
+
`TextAdUpdate` не содержит `Mobile`, а обновление расширений идёт через поле
|
|
1125
|
+
`CalloutSetting`, которое пока не покрыто CLI. Для TEXT_IMAGE_AD дополнительно
|
|
1126
|
+
доступен `--turbo-page-id`.
|
|
1127
|
+
|
|
1045
1128
|
#### Ключевые слова
|
|
1046
1129
|
|
|
1047
1130
|
```bash
|
|
@@ -1051,6 +1134,33 @@ direct keywords update --id 88888 --keyword "updated keyword text"
|
|
|
1051
1134
|
direct keywords delete --id 88888
|
|
1052
1135
|
```
|
|
1053
1136
|
|
|
1137
|
+
**Пакетная загрузка ключевых слов** (CLI автоматически режет на куски по API-лимиту 10/запрос):
|
|
1138
|
+
|
|
1139
|
+
```bash
|
|
1140
|
+
# Из JSONL-файла (по одному объекту ключевого слова на строку)
|
|
1141
|
+
direct keywords add --adgroup-id 12345 --from-file keywords.jsonl
|
|
1142
|
+
|
|
1143
|
+
# Inline JSON-массив
|
|
1144
|
+
direct keywords add --keywords-json '[{"Keyword":"купить ноутбук","Bid":10000000},{"Keyword":"купить ПК"}]'
|
|
1145
|
+
```
|
|
1146
|
+
|
|
1147
|
+
Пример `keywords.jsonl`:
|
|
1148
|
+
|
|
1149
|
+
```jsonl
|
|
1150
|
+
{"Keyword":"купить ноутбук","Bid":10000000,"UserParam1":"src=ad1"}
|
|
1151
|
+
{"Keyword":"купить ПК","ContextBid":5000000}
|
|
1152
|
+
{"Keyword":"buy laptop","AdGroupId":99999}
|
|
1153
|
+
```
|
|
1154
|
+
|
|
1155
|
+
- Ключи строки — WSDL CamelCase: `Keyword`, `AdGroupId`, `Bid`, `ContextBid`, `UserParam1`, `UserParam2`.
|
|
1156
|
+
- `--adgroup-id` задаёт значение по умолчанию; в строке можно переопределить через `AdGroupId`.
|
|
1157
|
+
- В каждой строке должны разрешаться `Keyword` и `AdGroupId`; неизвестные поля отклоняются с указанием номера строки.
|
|
1158
|
+
- API-лимит: 10 элементов на запрос `keywords.add` — см. [документацию Yandex Direct](https://yandex.ru/dev/direct/doc/dg/objects/keyword.html). CLI отправит нужное число чанков и склеит `AddResults`.
|
|
1159
|
+
- API-лимит: 200 ключевых слов на одну группу объявлений. CLI печатает предупреждение, если в каком-то `AdGroupId` во входе их больше; API отклонит излишек item-level ошибками.
|
|
1160
|
+
- Item-level ошибки от API не прерывают batch; объединённый вывод содержит и успешные Id, и ошибки.
|
|
1161
|
+
- При сетевой ошибке в середине batch уже созданные Id выводятся в stderr (`Partial success before failure`), чтобы при retry не возникли дубли.
|
|
1162
|
+
- `--dry-run` показывает payload первого чанка плюс `{chunks, totalItems, chunkSize}`.
|
|
1163
|
+
|
|
1054
1164
|
#### Отчёты
|
|
1055
1165
|
|
|
1056
1166
|
```bash
|
|
@@ -1107,8 +1217,8 @@ direct dynamicads set-bids --id 789 --bid 12500000 --context-bid 9000000 --prior
|
|
|
1107
1217
|
|
|
1108
1218
|
# Общие стратегии ставок
|
|
1109
1219
|
direct strategies get --limit 5
|
|
1110
|
-
direct strategies add --name "Общая стратегия" --type WbMaximumClicks --spend-limit 1000000000 --
|
|
1111
|
-
direct strategies update --id 42 --type WbMaximumClicks --
|
|
1220
|
+
direct strategies add --name "Общая стратегия" --type WbMaximumClicks --weekly-spend-limit 1000000000 --bid-ceiling 30000000 --dry-run
|
|
1221
|
+
direct strategies update --id 42 --type WbMaximumClicks --weekly-spend-limit 35000000 --dry-run
|
|
1112
1222
|
direct strategies archive --id 42 --dry-run
|
|
1113
1223
|
|
|
1114
1224
|
# Динамические таргеты по фиду
|
|
@@ -1186,6 +1296,17 @@ direct campaigns get --fetch-all # все страницы
|
|
|
1186
1296
|
direct campaigns add --name "Тест" --start-date 2024-01-01 --dry-run
|
|
1187
1297
|
```
|
|
1188
1298
|
|
|
1299
|
+
### Ошибки API
|
|
1300
|
+
|
|
1301
|
+
Яндекс Директ может вернуть успешный HTTP-ответ, внутри которого есть
|
|
1302
|
+
item-level `Errors` для конкретного объекта. Direct CLI считает такой ответ
|
|
1303
|
+
ошибкой операции: команда завершается с ненулевым кодом и печатает код ошибки,
|
|
1304
|
+
сообщение и детали.
|
|
1305
|
+
|
|
1306
|
+
Код `8800` с `Object not found` обычно означает, что объект недоступен в
|
|
1307
|
+
текущем `Client-Login` или аккаунте. Перед повтором проверьте выбранный
|
|
1308
|
+
`--login`, `YANDEX_DIRECT_LOGIN` или auth profile.
|
|
1309
|
+
|
|
1189
1310
|
### Тестирование
|
|
1190
1311
|
|
|
1191
1312
|
В `tests/` четыре уровня тестов:
|
|
@@ -1262,6 +1383,12 @@ sandbox-токен не нужен.
|
|
|
1262
1383
|
Для `account-management` нужна переменная
|
|
1263
1384
|
`YANDEX_DIRECT_V4ACCOUNT_ACCOUNT_ID`; без неё runner покажет `NOT_COVERED`.
|
|
1264
1385
|
|
|
1386
|
+
`clients.update` включается только явно, потому что меняет client-level
|
|
1387
|
+
metadata аккаунта. Укажите `YANDEX_DIRECT_CLIENTS_UPDATE_LOGIN` с disposable
|
|
1388
|
+
sandbox `Client-Login`; runner передаст его через `--login` и изменит только
|
|
1389
|
+
`ClientInfo` на уникальный smoke marker. Без этой переменной runner покажет
|
|
1390
|
+
`NOT_COVERED` для `clients.update`.
|
|
1391
|
+
|
|
1265
1392
|
#### Перезапись write-кассет
|
|
1266
1393
|
|
|
1267
1394
|
Уровень `integration_write` в pytest всё ещё воспроизводит сохранённый
|
|
@@ -341,6 +341,15 @@ direct campaigns add --name "My Campaign" --start-date 2024-02-01 --type TEXT_CA
|
|
|
341
341
|
direct campaigns add --name "Dynamic Campaign" --start-date 2024-02-01 --type DYNAMIC_TEXT_CAMPAIGN --setting ADD_METRICA_TAG=NO --search-strategy HIGHEST_POSITION --network-strategy SERVING_OFF --dry-run
|
|
342
342
|
direct campaigns add --name "Smart Campaign" --start-date 2024-02-01 --type SMART_CAMPAIGN --network-strategy AVERAGE_CPC_PER_FILTER --filter-average-cpc 1000000 --counter-id 123 --dry-run
|
|
343
343
|
|
|
344
|
+
# CPA strategy (single goal): --goal-id required, --average-cpa / --bid-ceiling are micro-rubles
|
|
345
|
+
direct campaigns add --name "CPA Campaign" --start-date 2026-06-01 --type TEXT_CAMPAIGN --search-strategy AVERAGE_CPA --network-strategy SERVING_OFF --goal-id 1234567 --average-cpa 500000000 --bid-ceiling 1000000000 --counter-ids 111,222 --dry-run
|
|
346
|
+
|
|
347
|
+
# Multi-goal CPA via PriorityGoals (goal_id:value pairs, WSDL PriorityGoalsItem)
|
|
348
|
+
direct campaigns add --name "Multi-Goal CPA" --start-date 2026-06-01 --type TEXT_CAMPAIGN --search-strategy AVERAGE_CPA_MULTIPLE_GOALS --network-strategy SERVING_OFF --priority-goals 1234567:80,9876543:20 --bid-ceiling 1000000000 --dry-run
|
|
349
|
+
|
|
350
|
+
# Notification (Sms/Email) and TimeTargeting accept JSON with WSDL CamelCase keys
|
|
351
|
+
direct campaigns add --name "Notify+Schedule" --start-date 2026-06-01 --type TEXT_CAMPAIGN --search-strategy HIGHEST_POSITION --network-strategy SERVING_OFF --notification '{"EmailSettings":{"Email":"ops@example.com","SendWarnings":"YES"}}' --time-targeting '{"Schedule":["1A0123456789ABCDEFGHIJKL"],"ConsiderWorkingWeekends":"YES"}' --dry-run
|
|
352
|
+
|
|
344
353
|
# Update / lifecycle
|
|
345
354
|
direct campaigns update --id 12345 --name "New Name" --status SUSPENDED --budget 100000000 --start-date 2024-02-10 --end-date 2024-03-01
|
|
346
355
|
direct campaigns suspend --id 12345
|
|
@@ -367,11 +376,21 @@ direct adgroups delete --id 67890
|
|
|
367
376
|
direct ads get --campaign-ids 1,2,3
|
|
368
377
|
direct ads get --adgroup-ids 45678 --format table
|
|
369
378
|
direct ads add --adgroup-id 12345 --type TEXT_AD --title "Title" --text "Ad text" --href "https://example.com" --dry-run
|
|
370
|
-
direct ads add --adgroup-id 12345 --type
|
|
371
|
-
direct ads
|
|
379
|
+
direct ads add --adgroup-id 12345 --type TEXT_AD --title "Title" --text "Ad text" --href "https://example.com" --title2 "Second headline" --display-url-path "deals" --mobile YES --vcard-id 111 --sitelink-set-id 222 --turbo-page-id 333 --ad-extensions "444,555" --dry-run
|
|
380
|
+
direct ads add --adgroup-id 12345 --type TEXT_IMAGE_AD --image-hash abcdefghijklmnopqrst --href "https://example.com" --turbo-page-id 555 --dry-run
|
|
381
|
+
direct ads update --id 99999 --type TEXT_AD --title "New Title" --text "New text" --href "https://example.com"
|
|
382
|
+
direct ads update --id 99999 --type TEXT_AD --image-hash abcdefghijklmnopqrst
|
|
383
|
+
direct ads update --id 99999 --type TEXT_AD --title2 "New second headline" --vcard-id 222
|
|
372
384
|
direct ads delete --id 99999
|
|
373
385
|
```
|
|
374
386
|
|
|
387
|
+
Available TEXT_AD typed flags for `ads add` / `ads update`: `--title`, `--text`,
|
|
388
|
+
`--href`, `--image-hash`, `--title2`, `--display-url-path`, `--vcard-id`,
|
|
389
|
+
`--sitelink-set-id`, `--turbo-page-id`. `--mobile` (default `NO`) and
|
|
390
|
+
`--ad-extensions` are `ads add`-only — `TextAdUpdate` does not contain `Mobile`,
|
|
391
|
+
and ad-extension updates go through the `CalloutSetting` WSDL field, which is
|
|
392
|
+
not yet exposed by the CLI. TEXT_IMAGE_AD additionally accepts `--turbo-page-id`.
|
|
393
|
+
|
|
375
394
|
#### Keywords
|
|
376
395
|
|
|
377
396
|
```bash
|
|
@@ -381,6 +400,33 @@ direct keywords update --id 88888 --keyword "updated keyword text"
|
|
|
381
400
|
direct keywords delete --id 88888
|
|
382
401
|
```
|
|
383
402
|
|
|
403
|
+
**Batch keyword upload** (CLI auto-chunks to the API limit of 10 per request):
|
|
404
|
+
|
|
405
|
+
```bash
|
|
406
|
+
# From a JSONL file (one keyword object per line)
|
|
407
|
+
direct keywords add --adgroup-id 12345 --from-file keywords.jsonl
|
|
408
|
+
|
|
409
|
+
# Inline JSON array
|
|
410
|
+
direct keywords add --keywords-json '[{"Keyword":"buy laptop","Bid":10000000},{"Keyword":"buy desktop"}]'
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
Example `keywords.jsonl`:
|
|
414
|
+
|
|
415
|
+
```jsonl
|
|
416
|
+
{"Keyword":"buy laptop","Bid":10000000,"UserParam1":"src=ad1"}
|
|
417
|
+
{"Keyword":"buy desktop","ContextBid":5000000}
|
|
418
|
+
{"Keyword":"купить ноутбук","AdGroupId":99999}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
- Row keys use WSDL CamelCase: `Keyword`, `AdGroupId`, `Bid`, `ContextBid`, `UserParam1`, `UserParam2`.
|
|
422
|
+
- `--adgroup-id` provides the default group ID; rows can override it via per-row `AdGroupId`.
|
|
423
|
+
- Each effective row must resolve `Keyword` and `AdGroupId`; unknown fields are rejected with the row number.
|
|
424
|
+
- API limit: 10 items per `keywords.add` request — see [Yandex Direct docs](https://yandex.ru/dev/direct/doc/dg/objects/keyword.html). The CLI sends as many chunks as needed and merges `AddResults`.
|
|
425
|
+
- API limit: 200 keywords per ad group. The CLI prints a warning if any `AdGroupId` in the input exceeds it; the API rejects the excess as per-item errors.
|
|
426
|
+
- Item-level errors from the API do not abort the batch; the merged output includes successes and per-item errors.
|
|
427
|
+
- If a chunk fails with a network-level error mid-batch, already-created Ids are printed to stderr (`Partial success before failure`) so a retry doesn't duplicate them.
|
|
428
|
+
- `--dry-run` shows the first chunk's payload plus `{chunks, totalItems, chunkSize}`.
|
|
429
|
+
|
|
384
430
|
#### Reports
|
|
385
431
|
|
|
386
432
|
```bash
|
|
@@ -437,8 +483,8 @@ direct dynamicads set-bids --id 789 --bid 12500000 --context-bid 9000000 --prior
|
|
|
437
483
|
|
|
438
484
|
# Shared bidding strategies
|
|
439
485
|
direct strategies get --limit 5
|
|
440
|
-
direct strategies add --name "Shared Clicks" --type WbMaximumClicks --spend-limit 1000000000 --
|
|
441
|
-
direct strategies update --id 42 --type WbMaximumClicks --
|
|
486
|
+
direct strategies add --name "Shared Clicks" --type WbMaximumClicks --weekly-spend-limit 1000000000 --bid-ceiling 30000000 --dry-run
|
|
487
|
+
direct strategies update --id 42 --type WbMaximumClicks --weekly-spend-limit 35000000 --dry-run
|
|
442
488
|
direct strategies archive --id 42 --dry-run
|
|
443
489
|
|
|
444
490
|
# Dynamic feed ad targets
|
|
@@ -515,6 +561,17 @@ Use `--dry-run` on `add` / `update` commands to preview the API request before s
|
|
|
515
561
|
direct campaigns add --name "Test" --start-date 2024-01-01 --dry-run
|
|
516
562
|
```
|
|
517
563
|
|
|
564
|
+
### API Errors
|
|
565
|
+
|
|
566
|
+
Yandex Direct can return a successful HTTP response that still contains
|
|
567
|
+
item-level `Errors` for one object. Direct CLI treats those responses as
|
|
568
|
+
failed operations: it exits non-zero and prints the error code, message, and
|
|
569
|
+
details.
|
|
570
|
+
|
|
571
|
+
Code `8800` with `Object not found` usually means the object is not available
|
|
572
|
+
under the current `Client-Login` or account. Check the selected `--login`,
|
|
573
|
+
`YANDEX_DIRECT_LOGIN`, or auth profile before retrying.
|
|
574
|
+
|
|
518
575
|
### Testing
|
|
519
576
|
|
|
520
577
|
Four tiers of tests live under `tests/`:
|
|
@@ -623,6 +680,12 @@ For `v4account` sandbox smoke, `enable-shared-account` uses
|
|
|
623
680
|
`account-management` requires `YANDEX_DIRECT_V4ACCOUNT_ACCOUNT_ID`; without it
|
|
624
681
|
the runner reports `NOT_COVERED` for that command.
|
|
625
682
|
|
|
683
|
+
`clients.update` is opt-in because it mutates client-level account metadata.
|
|
684
|
+
Set `YANDEX_DIRECT_CLIENTS_UPDATE_LOGIN` to an expendable sandbox
|
|
685
|
+
`Client-Login`; the runner passes it through `--login` and updates only
|
|
686
|
+
`ClientInfo` with a unique smoke marker. Without that variable, the runner
|
|
687
|
+
reports `NOT_COVERED` for `clients.update`.
|
|
688
|
+
|
|
626
689
|
#### Re-recording write cassettes
|
|
627
690
|
|
|
628
691
|
The `integration_write` pytest tier still replays stored write-test traffic
|
|
@@ -968,6 +1031,15 @@ direct campaigns add --name "Моя кампания" --start-date 2024-02-01 --
|
|
|
968
1031
|
direct campaigns add --name "Динамическая кампания" --start-date 2024-02-01 --type DYNAMIC_TEXT_CAMPAIGN --setting ADD_METRICA_TAG=NO --search-strategy HIGHEST_POSITION --network-strategy SERVING_OFF --dry-run
|
|
969
1032
|
direct campaigns add --name "Смарт-кампания" --start-date 2024-02-01 --type SMART_CAMPAIGN --network-strategy AVERAGE_CPC_PER_FILTER --filter-average-cpc 1000000 --counter-id 123 --dry-run
|
|
970
1033
|
|
|
1034
|
+
# CPA-стратегия (одна цель): --goal-id обязателен, --average-cpa/--bid-ceiling — micro-рубли
|
|
1035
|
+
direct campaigns add --name "CPA-кампания" --start-date 2026-06-01 --type TEXT_CAMPAIGN --search-strategy AVERAGE_CPA --network-strategy SERVING_OFF --goal-id 1234567 --average-cpa 500000000 --bid-ceiling 1000000000 --counter-ids 111,222 --dry-run
|
|
1036
|
+
|
|
1037
|
+
# Мульти-целевой CPA через PriorityGoals (пары goal_id:value, WSDL PriorityGoalsItem)
|
|
1038
|
+
direct campaigns add --name "Мульти-целевой CPA" --start-date 2026-06-01 --type TEXT_CAMPAIGN --search-strategy AVERAGE_CPA_MULTIPLE_GOALS --network-strategy SERVING_OFF --priority-goals 1234567:80,9876543:20 --bid-ceiling 1000000000 --dry-run
|
|
1039
|
+
|
|
1040
|
+
# Notification (Sms/Email) и TimeTargeting принимают JSON с CamelCase ключами WSDL
|
|
1041
|
+
direct campaigns add --name "Уведомления+Расписание" --start-date 2026-06-01 --type TEXT_CAMPAIGN --search-strategy HIGHEST_POSITION --network-strategy SERVING_OFF --notification '{"EmailSettings":{"Email":"ops@example.com","SendWarnings":"YES"}}' --time-targeting '{"Schedule":["1A0123456789ABCDEFGHIJKL"],"ConsiderWorkingWeekends":"YES"}' --dry-run
|
|
1042
|
+
|
|
971
1043
|
# Обновление и управление статусом
|
|
972
1044
|
direct campaigns update --id 12345 --name "Новое название" --status SUSPENDED --budget 100000000 --start-date 2024-02-10 --end-date 2024-03-01
|
|
973
1045
|
direct campaigns suspend --id 12345
|
|
@@ -994,11 +1066,22 @@ direct adgroups delete --id 67890
|
|
|
994
1066
|
direct ads get --campaign-ids 1,2,3
|
|
995
1067
|
direct ads get --adgroup-ids 45678 --format table
|
|
996
1068
|
direct ads add --adgroup-id 12345 --type TEXT_AD --title "Заголовок" --text "Текст объявления" --href "https://example.com" --dry-run
|
|
997
|
-
direct ads add --adgroup-id 12345 --type
|
|
998
|
-
direct ads
|
|
1069
|
+
direct ads add --adgroup-id 12345 --type TEXT_AD --title "Заголовок" --text "Текст" --href "https://example.com" --title2 "Второй заголовок" --display-url-path "deals" --mobile YES --vcard-id 111 --sitelink-set-id 222 --turbo-page-id 333 --ad-extensions "444,555" --dry-run
|
|
1070
|
+
direct ads add --adgroup-id 12345 --type TEXT_IMAGE_AD --image-hash abcdefghijklmnopqrst --href "https://example.com" --turbo-page-id 555 --dry-run
|
|
1071
|
+
direct ads update --id 99999 --type TEXT_AD --title "Новый заголовок" --text "Новый текст" --href "https://example.com"
|
|
1072
|
+
direct ads update --id 99999 --type TEXT_AD --image-hash abcdefghijklmnopqrst
|
|
1073
|
+
direct ads update --id 99999 --type TEXT_AD --title2 "Новый второй заголовок" --vcard-id 222
|
|
999
1074
|
direct ads delete --id 99999
|
|
1000
1075
|
```
|
|
1001
1076
|
|
|
1077
|
+
Доступные типизированные флаги TEXT_AD для `ads add` / `ads update`:
|
|
1078
|
+
`--title`, `--text`, `--href`, `--image-hash`, `--title2`, `--display-url-path`,
|
|
1079
|
+
`--vcard-id`, `--sitelink-set-id`, `--turbo-page-id`. `--mobile`
|
|
1080
|
+
(default `NO`) и `--ad-extensions` доступны только в `ads add` — WSDL
|
|
1081
|
+
`TextAdUpdate` не содержит `Mobile`, а обновление расширений идёт через поле
|
|
1082
|
+
`CalloutSetting`, которое пока не покрыто CLI. Для TEXT_IMAGE_AD дополнительно
|
|
1083
|
+
доступен `--turbo-page-id`.
|
|
1084
|
+
|
|
1002
1085
|
#### Ключевые слова
|
|
1003
1086
|
|
|
1004
1087
|
```bash
|
|
@@ -1008,6 +1091,33 @@ direct keywords update --id 88888 --keyword "updated keyword text"
|
|
|
1008
1091
|
direct keywords delete --id 88888
|
|
1009
1092
|
```
|
|
1010
1093
|
|
|
1094
|
+
**Пакетная загрузка ключевых слов** (CLI автоматически режет на куски по API-лимиту 10/запрос):
|
|
1095
|
+
|
|
1096
|
+
```bash
|
|
1097
|
+
# Из JSONL-файла (по одному объекту ключевого слова на строку)
|
|
1098
|
+
direct keywords add --adgroup-id 12345 --from-file keywords.jsonl
|
|
1099
|
+
|
|
1100
|
+
# Inline JSON-массив
|
|
1101
|
+
direct keywords add --keywords-json '[{"Keyword":"купить ноутбук","Bid":10000000},{"Keyword":"купить ПК"}]'
|
|
1102
|
+
```
|
|
1103
|
+
|
|
1104
|
+
Пример `keywords.jsonl`:
|
|
1105
|
+
|
|
1106
|
+
```jsonl
|
|
1107
|
+
{"Keyword":"купить ноутбук","Bid":10000000,"UserParam1":"src=ad1"}
|
|
1108
|
+
{"Keyword":"купить ПК","ContextBid":5000000}
|
|
1109
|
+
{"Keyword":"buy laptop","AdGroupId":99999}
|
|
1110
|
+
```
|
|
1111
|
+
|
|
1112
|
+
- Ключи строки — WSDL CamelCase: `Keyword`, `AdGroupId`, `Bid`, `ContextBid`, `UserParam1`, `UserParam2`.
|
|
1113
|
+
- `--adgroup-id` задаёт значение по умолчанию; в строке можно переопределить через `AdGroupId`.
|
|
1114
|
+
- В каждой строке должны разрешаться `Keyword` и `AdGroupId`; неизвестные поля отклоняются с указанием номера строки.
|
|
1115
|
+
- API-лимит: 10 элементов на запрос `keywords.add` — см. [документацию Yandex Direct](https://yandex.ru/dev/direct/doc/dg/objects/keyword.html). CLI отправит нужное число чанков и склеит `AddResults`.
|
|
1116
|
+
- API-лимит: 200 ключевых слов на одну группу объявлений. CLI печатает предупреждение, если в каком-то `AdGroupId` во входе их больше; API отклонит излишек item-level ошибками.
|
|
1117
|
+
- Item-level ошибки от API не прерывают batch; объединённый вывод содержит и успешные Id, и ошибки.
|
|
1118
|
+
- При сетевой ошибке в середине batch уже созданные Id выводятся в stderr (`Partial success before failure`), чтобы при retry не возникли дубли.
|
|
1119
|
+
- `--dry-run` показывает payload первого чанка плюс `{chunks, totalItems, chunkSize}`.
|
|
1120
|
+
|
|
1011
1121
|
#### Отчёты
|
|
1012
1122
|
|
|
1013
1123
|
```bash
|
|
@@ -1064,8 +1174,8 @@ direct dynamicads set-bids --id 789 --bid 12500000 --context-bid 9000000 --prior
|
|
|
1064
1174
|
|
|
1065
1175
|
# Общие стратегии ставок
|
|
1066
1176
|
direct strategies get --limit 5
|
|
1067
|
-
direct strategies add --name "Общая стратегия" --type WbMaximumClicks --spend-limit 1000000000 --
|
|
1068
|
-
direct strategies update --id 42 --type WbMaximumClicks --
|
|
1177
|
+
direct strategies add --name "Общая стратегия" --type WbMaximumClicks --weekly-spend-limit 1000000000 --bid-ceiling 30000000 --dry-run
|
|
1178
|
+
direct strategies update --id 42 --type WbMaximumClicks --weekly-spend-limit 35000000 --dry-run
|
|
1069
1179
|
direct strategies archive --id 42 --dry-run
|
|
1070
1180
|
|
|
1071
1181
|
# Динамические таргеты по фиду
|
|
@@ -1143,6 +1253,17 @@ direct campaigns get --fetch-all # все страницы
|
|
|
1143
1253
|
direct campaigns add --name "Тест" --start-date 2024-01-01 --dry-run
|
|
1144
1254
|
```
|
|
1145
1255
|
|
|
1256
|
+
### Ошибки API
|
|
1257
|
+
|
|
1258
|
+
Яндекс Директ может вернуть успешный HTTP-ответ, внутри которого есть
|
|
1259
|
+
item-level `Errors` для конкретного объекта. Direct CLI считает такой ответ
|
|
1260
|
+
ошибкой операции: команда завершается с ненулевым кодом и печатает код ошибки,
|
|
1261
|
+
сообщение и детали.
|
|
1262
|
+
|
|
1263
|
+
Код `8800` с `Object not found` обычно означает, что объект недоступен в
|
|
1264
|
+
текущем `Client-Login` или аккаунте. Перед повтором проверьте выбранный
|
|
1265
|
+
`--login`, `YANDEX_DIRECT_LOGIN` или auth profile.
|
|
1266
|
+
|
|
1146
1267
|
### Тестирование
|
|
1147
1268
|
|
|
1148
1269
|
В `tests/` четыре уровня тестов:
|
|
@@ -1219,6 +1340,12 @@ sandbox-токен не нужен.
|
|
|
1219
1340
|
Для `account-management` нужна переменная
|
|
1220
1341
|
`YANDEX_DIRECT_V4ACCOUNT_ACCOUNT_ID`; без неё runner покажет `NOT_COVERED`.
|
|
1221
1342
|
|
|
1343
|
+
`clients.update` включается только явно, потому что меняет client-level
|
|
1344
|
+
metadata аккаунта. Укажите `YANDEX_DIRECT_CLIENTS_UPDATE_LOGIN` с disposable
|
|
1345
|
+
sandbox `Client-Login`; runner передаст его через `--login` и изменит только
|
|
1346
|
+
`ClientInfo` на уникальный smoke marker. Без этой переменной runner покажет
|
|
1347
|
+
`NOT_COVERED` для `clients.update`.
|
|
1348
|
+
|
|
1222
1349
|
#### Перезапись write-кассет
|
|
1223
1350
|
|
|
1224
1351
|
Уровень `integration_write` в pytest всё ещё воспроизводит сохранённый
|
|
@@ -55,7 +55,20 @@ from .commands.v4wordstat import v4wordstat
|
|
|
55
55
|
load_dotenv()
|
|
56
56
|
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
CLI_EPILOG = """\b
|
|
59
|
+
Credential context:
|
|
60
|
+
--login / YANDEX_DIRECT_LOGIN selects the Yandex Direct Client-Login.
|
|
61
|
+
Use direct auth status to inspect the selected OAuth profile.
|
|
62
|
+
|
|
63
|
+
\b
|
|
64
|
+
API errors:
|
|
65
|
+
Item-level Yandex Direct Errors are reported as command failures.
|
|
66
|
+
Error 8800 usually means the object is not available under the current
|
|
67
|
+
Client-Login/account.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@click.group(name="direct", epilog=CLI_EPILOG)
|
|
59
72
|
@click.version_option(__version__, prog_name="direct")
|
|
60
73
|
@click.option("--token", envvar="YANDEX_DIRECT_TOKEN", help="API access token")
|
|
61
74
|
@click.option("--login", envvar="YANDEX_DIRECT_LOGIN", help="Client login")
|