direct-cli 0.3.15__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {direct_cli-0.3.15 → direct_cli-0.4.0}/.github/workflows/claude.yml +1 -1
- {direct_cli-0.3.15 → direct_cli-0.4.0}/CHANGELOG.md +70 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/PKG-INFO +12 -4
- {direct_cli-0.3.15 → direct_cli-0.4.0}/README.md +11 -3
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.py +9 -3
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/cli.py +28 -0
- direct_cli-0.4.0/direct_cli/commands/v4events.py +232 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/v4finance.py +53 -21
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/v4forecast.py +52 -3
- direct_cli-0.4.0/direct_cli/i18n.py +107 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/v4/__init__.py +2 -2
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/v4_contracts.py +131 -42
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli.egg-info/PKG-INFO +12 -4
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli.egg-info/SOURCES.txt +47 -0
- direct_cli-0.4.0/docs/audits/API_COVERAGE.md +93 -0
- direct_cli-0.4.0/docs/audits/PROJECT_WIRE_SHAPE_AUDIT_2026-05-29.md +193 -0
- direct_cli-0.4.0/docs/audits/wire_shape.json +1394 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/pyproject.toml +1 -1
- direct_cli-0.4.0/scripts/audit_wire_shape.py +791 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/scripts/test_dangerous_commands.sh +2 -2
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/API_COVERAGE.md +11 -6
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[adextensions_get].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[adgroups_get].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[adimages_get].yaml +66 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[ads_get].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[advideos_get].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[audiencetargets_get].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[bidmodifiers_get].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[bids_get].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[businesses_get].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[campaigns_get].yaml +57 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[changes_check].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[changes_check_campaigns].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[changes_check_dictionaries].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[clients_get].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[creatives_get].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[dictionaries_get].yaml +11696 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[dynamicads_get].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[dynamicfeedadtargets_get].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[feeds_get].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[keywordbids_get].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[keywords_get].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[keywordsresearch_deduplicate].yaml +59 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[keywordsresearch_has_search_volume].yaml +58 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[leads_get].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[negativekeywordsharedsets_get].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[reports_get].yaml +64 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[retargeting_get].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[sitelinks_get].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[smartadtargets_get].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[strategies_get].yaml +66 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[turbopages_get].yaml +56 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[v4events_get_events_log].yaml +52 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[v4finance_get_clients_units].yaml +52 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[v4forecast_list].yaml +52 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[v4goals_get_stat_goals].yaml +52 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[v4tags_get_campaigns].yaml +52 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[v4wordstat_list_reports].yaml +52 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_read_command[vcards_get].yaml +57 -0
- direct_cli-0.4.0/tests/cassettes/test_read_cassettes/test_v4finance_check_payment_unknown_transaction.yaml +52 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/conftest.py +68 -17
- direct_cli-0.4.0/tests/test_audit_wire_shape.py +158 -0
- direct_cli-0.4.0/tests/test_i18n.py +118 -0
- direct_cli-0.4.0/tests/test_read_cassettes.py +252 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_v4_contracts.py +20 -5
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_v4_runtime_shape.py +29 -3
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_v4events.py +110 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_v4finance_money.py +81 -9
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_v4forecast.py +68 -1
- direct_cli-0.3.15/direct_cli/commands/v4events.py +0 -120
- {direct_cli-0.3.15 → direct_cli-0.4.0}/.env.example +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/.github/copilot-instructions.md +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/.github/workflows/api-coverage.yml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/.github/workflows/quality.yml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/.gitignore +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/AGENTS.md +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/CLAUDE.md +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/MANIFEST.in +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/__init__.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/_bidding_strategy.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/_deprecated.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/_smoke_probes.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/_vendor/__init__.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/_vendor/tapi_yandex_direct/__init__.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/_vendor/tapi_yandex_direct/endpoints.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/_vendor/tapi_yandex_direct/exceptions.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/_vendor/tapi_yandex_direct/resource_mapping.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.pyi +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/_vendor/tapi_yandex_direct/v4/__init__.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.pyi +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/_vendor/tapi_yandex_direct/v4/resource_mapping.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/api.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/auth.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/__init__.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/adextensions.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/adgroups.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/adimages.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/ads.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/advideos.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/agencyclients.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/audiencetargets.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/auth.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/balance.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/bidmodifiers.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/bids.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/businesses.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/campaigns.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/changes.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/clients.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/creatives.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/dictionaries.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/dynamicads.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/dynamicfeedadtargets.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/feeds.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/keywordbids.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/keywords.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/keywordsresearch.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/leads.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/negativekeywordsharedsets.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/reports.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/retargeting.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/sitelinks.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/smartadtargets.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/strategies.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/turbopages.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/v4account.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/v4goals.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/v4shells.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/v4tags.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/v4wordstat.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/commands/vcards.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/output.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/reports_coverage.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/smoke_matrix.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/utils.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/v4/money.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli/wsdl_coverage.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli.egg-info/dependency_links.txt +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli.egg-info/entry_points.txt +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli.egg-info/requires.txt +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/direct_cli.egg-info/top_level.txt +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/docs/audits/issue-198-mutating-wsdl-audit.md +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/docs/superpowers/plans/2026-04-12-issue-32-completion.md +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/docs/superpowers/specs/2026-04-23-vendor-tapi-yandex-direct-design.md +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/scripts/anonymize_cassettes.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/scripts/build_api_coverage_checklist.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/scripts/build_api_coverage_report.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/scripts/build_wsdl_optional_field_audit.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/scripts/check_all_docs_urls.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/scripts/check_reports_drift.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/scripts/check_wsdl_drift.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/scripts/patch_vendor_imports.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/scripts/preflight_check.sh +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/scripts/refresh_reports_cache.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/scripts/refresh_wsdl_cache.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/scripts/release_pypi.sh +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/scripts/sandbox_write_audit.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/scripts/sandbox_write_live.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/scripts/test_safe_commands.sh +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/scripts/test_sandbox_write.sh +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/scripts/update_vendor.sh +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/setup.cfg +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/setup.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/API_ISSUE_AUDIT.md +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/MANUAL_COVERAGE.md +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/WSDL_OPTIONAL_FIELD_AUDIT.md +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/__init__.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/_orphan_store.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/api_coverage_payloads.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteAdExtensions.test_add_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteAdGroups.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteAdImages.test_add_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteAds.test_add_text_ad_update_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteAudienceTargets.test_add_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteBidModifiersAdd.test_add_delete_mobile.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteBidModifiersSet.test_set_without_id_is_rejected.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteBids.test_set_bid.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteBidsRead.test_bids_get.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteBidsRead.test_bids_set_auto.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteCampaignDraftLifecycle.test_draft_create_get_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteCampaigns.test_campaign_lifecycle.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteDynamicAds.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteFeeds.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteKeywordBids.test_set_keyword_bid.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteKeywords.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteNegativeKeywordSharedSets.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteRetargeting.test_add_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteRetargetingUpdate.test_retargeting_update.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteSitelinks.test_add_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteSmartAdTargets.test_add_update_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteStrategies.test_strategies_lifecycle.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_integration_write/TestWriteVCards.test_add_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_v5_live_write/test_v5_live_draft_adgroups_add_update_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_v5_live_write/test_v5_live_draft_adimages_add_get_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_v5_live_write/test_v5_live_draft_ads_add_update_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_v5_live_write/test_v5_live_draft_ads_suspend_resume_archive_unarchive.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_v5_live_write/test_v5_live_draft_advideos_add_get.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_add_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_suspend_resume.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_v5_live_write/test_v5_live_draft_bids_set.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_v5_live_write/test_v5_live_draft_campaign_create_get_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_v5_live_write/test_v5_live_draft_creatives_chain_advideo_to_creative.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_v5_live_write/test_v5_live_draft_dynamicads_add_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_v5_live_write/test_v5_live_draft_dynamicads_suspend_resume.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywordbids_set.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywords_add_update_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywords_suspend_resume.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_v5_live_write/test_v5_live_draft_sitelinks_add_get_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_v5_live_write/test_v5_live_draft_smartadtargets_add_update_delete.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/cassettes/test_v5_live_write/test_v5_live_draft_smartadtargets_suspend_resume.yaml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/fixtures/test-video.mp4 +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/reports_cache/raw/fields-list.html +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/reports_cache/raw/headers.html +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/reports_cache/raw/period.html +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/reports_cache/raw/spec.html +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/reports_cache/raw/type.html +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/reports_cache/spec.json +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_api_coverage.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_auth_bw.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_auth_oauth.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_auth_op.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_auth_write_json.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_balance.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_changes.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_cli.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_cli_contract.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_comprehensive.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_dry_run.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_env_loading.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_integration.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_integration_write.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_low_coverage_payloads.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_reports_drift.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_reports_parsing.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_sandbox_write_audit.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_smoke_matrix.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_transport_contract.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_unknown_option_hints.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_v4_exit_codes.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_v4_foundation.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_v4_live_contracts.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_v4_safety.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_v4account.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_v4finance_read.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_v4goals.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_v4tags.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_v4wordstat.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_v5_live_write.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_vendor_imports.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/test_wsdl_parity_gate.py +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/adextensions.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/adgroups.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/adimages.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/ads.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/advideos.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/agencyclients.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/audiencetargets.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/bidmodifiers.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/bids.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/businesses.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/campaigns.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/changes.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/clients.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/creatives.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/dictionaries.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/dynamicfeedadtargets.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/dynamictextadtargets.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/feeds.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/imports/adextensiontypes.xsd +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/imports/general.xsd +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/imports/generalclients.xsd +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/keywordbids.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/keywords.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/keywordsresearch.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/leads.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/negativekeywordsharedsets.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/retargetinglists.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/sitelinks.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/smartadtargets.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/strategies.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/turbopages.xml +0 -0
- {direct_cli-0.3.15 → direct_cli-0.4.0}/tests/wsdl_cache/vcards.xml +0 -0
|
@@ -46,5 +46,5 @@ jobs:
|
|
|
46
46
|
# Optional: Add claude_args to customize behavior and configuration
|
|
47
47
|
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
|
48
48
|
# or https://code.claude.com/docs/en/cli-reference for available options
|
|
49
|
-
claude_args: "--model claude-opus-4-
|
|
49
|
+
claude_args: "--model claude-opus-4-8"
|
|
50
50
|
|
|
@@ -1,5 +1,75 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
Milestone release closing the 0.4.0 roadmap (#123): typed Yandex Direct
|
|
6
|
+
API **v4 Live** CLI support and completion of the post-0.3.0 write-command
|
|
7
|
+
coverage gates. All public v4 input stays typed and canonical — no `--json`
|
|
8
|
+
passthrough — and mirrors `dg-v4/live/*` wire shapes 1:1.
|
|
9
|
+
|
|
10
|
+
**Added — typed v4 Live CLI:**
|
|
11
|
+
|
|
12
|
+
- v4 Live command foundation with typed Click groups and a `--dry-run`
|
|
13
|
+
seam that prints the `{method, param}` body before token/locale
|
|
14
|
+
enrichment (#124, closes #111 — typed CLI, not raw JSON passthrough).
|
|
15
|
+
- `v4finance` and `v4account` typed finance and shared-account commands
|
|
16
|
+
(#125).
|
|
17
|
+
- `v4goals`, `v4events`, `v4wordstat`, and `v4forecast` typed commands
|
|
18
|
+
(#126).
|
|
19
|
+
- `v4events get-events-log` and `v4forecast create-new-forecast` now expose
|
|
20
|
+
every documented input field (#456).
|
|
21
|
+
- Russian-default CLI help with English opt-in, starting with `v4finance`
|
|
22
|
+
(#458).
|
|
23
|
+
|
|
24
|
+
**Changed — write-command coverage gates:**
|
|
25
|
+
|
|
26
|
+
- Extended the WSDL schema gate to mutating operations; `keywordbids.set`
|
|
27
|
+
is now enum-validated against its WSDL `*FieldEnum` (#118).
|
|
28
|
+
- Per-method `WRITE_SANDBOX` integration coverage completed — zero
|
|
29
|
+
unexplained `NOT_COVERED` commands in `direct_cli/smoke_matrix.py`
|
|
30
|
+
(#122).
|
|
31
|
+
- Closed the remaining mutating `DRY_RUN_PAYLOAD_EXCLUSIONS`; every
|
|
32
|
+
declared WSDL operation now has a `PAYLOAD_CASES` fixture or a
|
|
33
|
+
documented technical exclusion (#127).
|
|
34
|
+
|
|
35
|
+
**Tests / tooling:**
|
|
36
|
+
|
|
37
|
+
- Offline VCR cassettes for all v5 read-only commands (#455).
|
|
38
|
+
- v4 Live read cassettes and a fix for an unbounded retry-loop (#457,
|
|
39
|
+
closes #454).
|
|
40
|
+
- Docs/wire-shape scanner with the 2026-05-29 sweep (#451).
|
|
41
|
+
|
|
42
|
+
`strict_parity_ok`, `live_model_parity_ok`, and `schema_parity_ok` all
|
|
43
|
+
report `true` in `scripts/build_api_coverage_report.py`.
|
|
44
|
+
|
|
45
|
+
## 0.3.16
|
|
46
|
+
|
|
47
|
+
**BREAKING CHANGES (regression fix — reverts 0.3.15 wire-shape changes):**
|
|
48
|
+
|
|
49
|
+
- `direct v4finance transfer-money` now requires `--currency` again and
|
|
50
|
+
re-emits `Currency` on every `FromCampaigns` / `ToCampaigns` item.
|
|
51
|
+
The 0.3.15 removal verified against `dg-v4/reference/TransferMoney`
|
|
52
|
+
(legacy v4); the actual Live 4 docs at
|
|
53
|
+
`dg-v4/live/TransferMoney` define `PayCampElement` with
|
|
54
|
+
`CampaignID`, `Sum`, and `Currency`, and explicitly mark `Currency` as
|
|
55
|
+
obligatory in the Live 4 changelog. The CLI now matches the live
|
|
56
|
+
docs 1:1. See audit comment on #125 for the reproducible diff.
|
|
57
|
+
- `direct v4finance pay-campaigns` now requires `--currency` again and
|
|
58
|
+
re-emits `Currency` on every `Payments[]` item. Same root cause:
|
|
59
|
+
`dg-v4/reference/PayCampaigns` (legacy) lacks `Currency`,
|
|
60
|
+
`dg-v4/live/PayCampaigns` (Live 4) requires it.
|
|
61
|
+
- `direct v4finance pay-campaigns` accepts `--pay-method Overdraft`
|
|
62
|
+
again. The Live 4 changelog explicitly adds `Overdraft` for direct
|
|
63
|
+
advertisers (paired with `Bank` for agencies). Only `Bank` keeps the
|
|
64
|
+
`--contract-id` requirement.
|
|
65
|
+
- `direct v4finance create-invoice` now requires `--currency` again and
|
|
66
|
+
re-emits `Currency` on every `Payments[]` item, mirroring
|
|
67
|
+
`dg-v4/live/CreateInvoice`.
|
|
68
|
+
|
|
69
|
+
This release reverts the wire-shape changes shipped by PRs #441, #442,
|
|
70
|
+
#443 (which closed #432, #433, #434). The CLI lives in the `v4finance`
|
|
71
|
+
Live group and must mirror `dg-v4/live/*`, not `dg-v4/reference/*`.
|
|
72
|
+
|
|
3
73
|
## 0.3.15
|
|
4
74
|
|
|
5
75
|
**BREAKING CHANGES:**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: direct-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Command-line interface for Yandex Direct API
|
|
5
5
|
Author: axisrow
|
|
6
6
|
License: MIT
|
|
@@ -154,6 +154,12 @@ direct balance --logins client-login --dry-run
|
|
|
154
154
|
| `--login` | Direct client login |
|
|
155
155
|
| `--profile` | Credential profile name |
|
|
156
156
|
| `--sandbox` | Use sandbox API |
|
|
157
|
+
| `--locale` | Help/message language: `ru` (default) or `en`. Also set via `YANDEX_DIRECT_CLI_LOCALE`. Command and flag names are unchanged. |
|
|
158
|
+
|
|
159
|
+
Help text is shown in Russian by default. Switch to English with
|
|
160
|
+
`direct --locale en ...` or `export YANDEX_DIRECT_CLI_LOCALE=en`. For example,
|
|
161
|
+
`direct v4finance --help` shows the financial master-token setup steps using
|
|
162
|
+
the locale-appropriate Yandex Direct UI labels.
|
|
157
163
|
|
|
158
164
|
### V4 Live Goals
|
|
159
165
|
|
|
@@ -186,6 +192,7 @@ direct v4tags update-banners --banner-ids 2571700 --clear-tags --dry-run
|
|
|
186
192
|
```bash
|
|
187
193
|
direct v4events get-events-log --from 2026-04-14T00:00:00 --to 2026-04-15T00:00:00
|
|
188
194
|
direct v4events get-events-log --from 2026-04-14T00:00:00 --to 2026-04-15T00:00:00 --currency RUB --limit 100 --offset 0 --format table
|
|
195
|
+
direct v4events get-events-log --from 2026-04-14T00:00:00 --to 2026-04-15T00:00:00 --last-event-only Yes --with-text-description Yes --filter-campaign-ids 123,456 --filter-event-type MoneyOut,MoneyIn
|
|
189
196
|
```
|
|
190
197
|
|
|
191
198
|
### V4 Live Wordstat Reports
|
|
@@ -209,6 +216,7 @@ the forecast is ready.
|
|
|
209
216
|
|
|
210
217
|
```bash
|
|
211
218
|
direct v4forecast create --phrases "buy laptop,buy desktop" --geo-ids 213 --currency RUB
|
|
219
|
+
direct v4forecast create --phrases "buy laptop" --geo-ids 213 --auction-bids Yes --common-minus-words "used,broken"
|
|
212
220
|
direct v4forecast list --format table
|
|
213
221
|
direct v4forecast get --forecast-id 123 --format table
|
|
214
222
|
direct v4forecast delete --forecast-id 123
|
|
@@ -232,10 +240,10 @@ is omitted. Dry-run output masks the financial token.
|
|
|
232
240
|
```bash
|
|
233
241
|
direct v4finance get-clients-units --logins client-login,other-client --format table
|
|
234
242
|
direct v4finance get-credit-limits --master-token MASTER_TOKEN --operation-num 123 --finance-login agency-login
|
|
235
|
-
direct v4finance create-invoice --payment 123=100.50 --payment 456=25 --master-token MASTER_TOKEN --operation-num 124 --finance-login agency-login --dry-run
|
|
243
|
+
direct v4finance create-invoice --payment 123=100.50 --payment 456=25 --currency RUB --master-token MASTER_TOKEN --operation-num 124 --finance-login agency-login --dry-run
|
|
236
244
|
direct v4finance check-payment --custom-transaction-id A123456789012345678901234567890B
|
|
237
|
-
direct v4finance transfer-money --from-campaign-id 123 --to-campaign-id 456 --amount 100.50 --master-token MASTER_TOKEN --operation-num 123 --finance-login agency-login --dry-run
|
|
238
|
-
direct v4finance pay-campaigns --campaign-ids 123,456 --amount 100.50 --contract-id CONTRACT_ID --pay-method Bank --master-token MASTER_TOKEN --operation-num 123 --finance-login agency-login --dry-run
|
|
245
|
+
direct v4finance transfer-money --from-campaign-id 123 --to-campaign-id 456 --amount 100.50 --currency RUB --master-token MASTER_TOKEN --operation-num 123 --finance-login agency-login --dry-run
|
|
246
|
+
direct v4finance pay-campaigns --campaign-ids 123,456 --amount 100.50 --contract-id CONTRACT_ID --pay-method Bank --currency RUB --master-token MASTER_TOKEN --operation-num 123 --finance-login agency-login --dry-run
|
|
239
247
|
```
|
|
240
248
|
|
|
241
249
|
### V4 Live Shared Account
|
|
@@ -111,6 +111,12 @@ direct balance --logins client-login --dry-run
|
|
|
111
111
|
| `--login` | Direct client login |
|
|
112
112
|
| `--profile` | Credential profile name |
|
|
113
113
|
| `--sandbox` | Use sandbox API |
|
|
114
|
+
| `--locale` | Help/message language: `ru` (default) or `en`. Also set via `YANDEX_DIRECT_CLI_LOCALE`. Command and flag names are unchanged. |
|
|
115
|
+
|
|
116
|
+
Help text is shown in Russian by default. Switch to English with
|
|
117
|
+
`direct --locale en ...` or `export YANDEX_DIRECT_CLI_LOCALE=en`. For example,
|
|
118
|
+
`direct v4finance --help` shows the financial master-token setup steps using
|
|
119
|
+
the locale-appropriate Yandex Direct UI labels.
|
|
114
120
|
|
|
115
121
|
### V4 Live Goals
|
|
116
122
|
|
|
@@ -143,6 +149,7 @@ direct v4tags update-banners --banner-ids 2571700 --clear-tags --dry-run
|
|
|
143
149
|
```bash
|
|
144
150
|
direct v4events get-events-log --from 2026-04-14T00:00:00 --to 2026-04-15T00:00:00
|
|
145
151
|
direct v4events get-events-log --from 2026-04-14T00:00:00 --to 2026-04-15T00:00:00 --currency RUB --limit 100 --offset 0 --format table
|
|
152
|
+
direct v4events get-events-log --from 2026-04-14T00:00:00 --to 2026-04-15T00:00:00 --last-event-only Yes --with-text-description Yes --filter-campaign-ids 123,456 --filter-event-type MoneyOut,MoneyIn
|
|
146
153
|
```
|
|
147
154
|
|
|
148
155
|
### V4 Live Wordstat Reports
|
|
@@ -166,6 +173,7 @@ the forecast is ready.
|
|
|
166
173
|
|
|
167
174
|
```bash
|
|
168
175
|
direct v4forecast create --phrases "buy laptop,buy desktop" --geo-ids 213 --currency RUB
|
|
176
|
+
direct v4forecast create --phrases "buy laptop" --geo-ids 213 --auction-bids Yes --common-minus-words "used,broken"
|
|
169
177
|
direct v4forecast list --format table
|
|
170
178
|
direct v4forecast get --forecast-id 123 --format table
|
|
171
179
|
direct v4forecast delete --forecast-id 123
|
|
@@ -189,10 +197,10 @@ is omitted. Dry-run output masks the financial token.
|
|
|
189
197
|
```bash
|
|
190
198
|
direct v4finance get-clients-units --logins client-login,other-client --format table
|
|
191
199
|
direct v4finance get-credit-limits --master-token MASTER_TOKEN --operation-num 123 --finance-login agency-login
|
|
192
|
-
direct v4finance create-invoice --payment 123=100.50 --payment 456=25 --master-token MASTER_TOKEN --operation-num 124 --finance-login agency-login --dry-run
|
|
200
|
+
direct v4finance create-invoice --payment 123=100.50 --payment 456=25 --currency RUB --master-token MASTER_TOKEN --operation-num 124 --finance-login agency-login --dry-run
|
|
193
201
|
direct v4finance check-payment --custom-transaction-id A123456789012345678901234567890B
|
|
194
|
-
direct v4finance transfer-money --from-campaign-id 123 --to-campaign-id 456 --amount 100.50 --master-token MASTER_TOKEN --operation-num 123 --finance-login agency-login --dry-run
|
|
195
|
-
direct v4finance pay-campaigns --campaign-ids 123,456 --amount 100.50 --contract-id CONTRACT_ID --pay-method Bank --master-token MASTER_TOKEN --operation-num 123 --finance-login agency-login --dry-run
|
|
202
|
+
direct v4finance transfer-money --from-campaign-id 123 --to-campaign-id 456 --amount 100.50 --currency RUB --master-token MASTER_TOKEN --operation-num 123 --finance-login agency-login --dry-run
|
|
203
|
+
direct v4finance pay-campaigns --campaign-ids 123,456 --amount 100.50 --contract-id CONTRACT_ID --pay-method Bank --currency RUB --master-token MASTER_TOKEN --operation-num 123 --finance-login agency-login --dry-run
|
|
196
204
|
```
|
|
197
205
|
|
|
198
206
|
### V4 Live Shared Account
|
|
@@ -176,9 +176,15 @@ class V4LiveClientAdapter(JSONAdapterMixin, TapiAdapter):
|
|
|
176
176
|
code = 0
|
|
177
177
|
|
|
178
178
|
if code in (54, 55) and api_params.get("retry_if_exceeded_limit", True):
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
179
|
+
# Bound the limit-exceeded retries with the same attempt budget as
|
|
180
|
+
# the server-error branch below. Without this guard the loop is
|
|
181
|
+
# infinite (sleep 10s, retry, forever) whenever the API keeps
|
|
182
|
+
# returning code 54/55 — which hangs the caller indefinitely.
|
|
183
|
+
if repeat_number < api_params.get("retries_if_server_error", 5):
|
|
184
|
+
logger.warning("v4 Live limit exceeded (code=%s), retry in 10s", code)
|
|
185
|
+
time.sleep(10)
|
|
186
|
+
return True
|
|
187
|
+
return False
|
|
182
188
|
|
|
183
189
|
if code in (52, 1000, 1001, 1002) or response.status_code == 500:
|
|
184
190
|
if repeat_number < api_params.get("retries_if_server_error", 5):
|
|
@@ -8,6 +8,11 @@ from click.core import ParameterSource
|
|
|
8
8
|
|
|
9
9
|
from . import __version__
|
|
10
10
|
from .auth import get_active_profile, get_credentials, load_env_file
|
|
11
|
+
from .i18n import (
|
|
12
|
+
LOCALE_ENV_VAR,
|
|
13
|
+
resolve_locale,
|
|
14
|
+
t,
|
|
15
|
+
)
|
|
11
16
|
from .utils import get_docs_url
|
|
12
17
|
|
|
13
18
|
from .commands.campaigns import campaigns
|
|
@@ -155,6 +160,21 @@ class DirectCliGroup(_NoSuchOptionHintMixin, click.Group):
|
|
|
155
160
|
|
|
156
161
|
command_class = DirectCliCommand
|
|
157
162
|
|
|
163
|
+
def format_epilog(self, ctx, formatter):
|
|
164
|
+
"""Render a locale-aware epilog when the group opts into i18n.
|
|
165
|
+
|
|
166
|
+
A group sets ``localized_epilog_key`` (a key in ``i18n.CATALOG``) and,
|
|
167
|
+
optionally, ``localized_epilog_suffix`` (an already-built tail appended
|
|
168
|
+
verbatim so single-sourced text such as ``V4_EPILOG`` is not
|
|
169
|
+
duplicated). Groups without the attribute keep their static epilog.
|
|
170
|
+
"""
|
|
171
|
+
key = getattr(self, "localized_epilog_key", None)
|
|
172
|
+
if key:
|
|
173
|
+
text = t(key, resolve_locale(ctx))
|
|
174
|
+
suffix = getattr(self, "localized_epilog_suffix", None)
|
|
175
|
+
self.epilog = f"{text}\n\n{suffix}" if suffix else text
|
|
176
|
+
super().format_epilog(ctx, formatter)
|
|
177
|
+
|
|
158
178
|
|
|
159
179
|
def _apply_directcli_classes(command: click.Command) -> None:
|
|
160
180
|
"""Recursively retype *command* (and subcommands) so every node in the
|
|
@@ -182,6 +202,12 @@ def _apply_directcli_classes(command: click.Command) -> None:
|
|
|
182
202
|
@click.option("--login", envvar="YANDEX_DIRECT_LOGIN", help="Client login")
|
|
183
203
|
@click.option("--profile", help="Credential profile name")
|
|
184
204
|
@click.option("--sandbox", is_flag=True, help="Use sandbox API")
|
|
205
|
+
@click.option(
|
|
206
|
+
"--locale",
|
|
207
|
+
envvar=LOCALE_ENV_VAR,
|
|
208
|
+
default=None,
|
|
209
|
+
help="Language for help and messages (ru or en; default: ru)",
|
|
210
|
+
)
|
|
185
211
|
@click.option(
|
|
186
212
|
"--op-token-ref",
|
|
187
213
|
envvar="YANDEX_DIRECT_OP_TOKEN_REF",
|
|
@@ -209,6 +235,7 @@ def cli(
|
|
|
209
235
|
login,
|
|
210
236
|
profile,
|
|
211
237
|
sandbox,
|
|
238
|
+
locale,
|
|
212
239
|
op_token_ref,
|
|
213
240
|
op_login_ref,
|
|
214
241
|
bw_token_ref,
|
|
@@ -218,6 +245,7 @@ def cli(
|
|
|
218
245
|
ctx.ensure_object(dict)
|
|
219
246
|
ctx.obj["sandbox"] = sandbox
|
|
220
247
|
ctx.obj["profile"] = profile
|
|
248
|
+
ctx.obj["locale"] = locale
|
|
221
249
|
active_profile = None
|
|
222
250
|
if ctx.invoked_subcommand != "auth":
|
|
223
251
|
active_profile = get_active_profile()
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"""Yandex Direct v4 Live events commands."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from ..api import create_v4_client
|
|
10
|
+
from ..output import format_output, print_error
|
|
11
|
+
from ..utils import parse_csv_strings, parse_ids
|
|
12
|
+
from ..v4 import build_v4_body, call_v4
|
|
13
|
+
from ..v4_contracts import v4_method_contract
|
|
14
|
+
from .v4shells import V4_EPILOG
|
|
15
|
+
|
|
16
|
+
V4_DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S"
|
|
17
|
+
V4_DATETIME_RE = re.compile(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}")
|
|
18
|
+
|
|
19
|
+
# Documented GetEventsLogFilter.EventType enum (dg-v4/live/GetEventsLog).
|
|
20
|
+
EVENT_TYPES = [
|
|
21
|
+
"BannerModerated",
|
|
22
|
+
"CampaignFinished",
|
|
23
|
+
"LowCTR",
|
|
24
|
+
"MoneyOut",
|
|
25
|
+
"MoneyWarning",
|
|
26
|
+
"MoneyIn",
|
|
27
|
+
"PausedByDayBudget",
|
|
28
|
+
"WarnMinPrice",
|
|
29
|
+
"WarnPlace",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _events_log_filter(
|
|
34
|
+
campaign_ids: Optional[str],
|
|
35
|
+
banner_ids: Optional[str],
|
|
36
|
+
phrase_ids: Optional[str],
|
|
37
|
+
account_ids: Optional[str],
|
|
38
|
+
event_type: Optional[str],
|
|
39
|
+
) -> Optional[dict]:
|
|
40
|
+
"""Build the GetEventsLogFilter object; None when no filter is provided."""
|
|
41
|
+
filter_obj: dict = {}
|
|
42
|
+
for option_name, raw, key in (
|
|
43
|
+
("--filter-campaign-ids", campaign_ids, "CampaignIDS"),
|
|
44
|
+
("--filter-banner-ids", banner_ids, "BannerIDS"),
|
|
45
|
+
("--filter-phrase-ids", phrase_ids, "PhraseIDS"),
|
|
46
|
+
("--filter-account-ids", account_ids, "AccountIDS"),
|
|
47
|
+
):
|
|
48
|
+
if not raw:
|
|
49
|
+
continue
|
|
50
|
+
try:
|
|
51
|
+
parsed = parse_ids(raw)
|
|
52
|
+
except ValueError as exc:
|
|
53
|
+
raise click.UsageError(f"{option_name}: {exc}") from exc
|
|
54
|
+
if parsed:
|
|
55
|
+
filter_obj[key] = parsed
|
|
56
|
+
if event_type:
|
|
57
|
+
types = parse_csv_strings(event_type)
|
|
58
|
+
if types:
|
|
59
|
+
unknown = [value for value in types if value not in EVENT_TYPES]
|
|
60
|
+
if unknown:
|
|
61
|
+
raise click.UsageError(
|
|
62
|
+
"--filter-event-type has unknown values: "
|
|
63
|
+
f"{', '.join(unknown)}. Valid values: {', '.join(EVENT_TYPES)}"
|
|
64
|
+
)
|
|
65
|
+
filter_obj["EventType"] = types
|
|
66
|
+
return filter_obj or None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _parse_v4_datetime(value: str, option_name: str) -> datetime:
|
|
70
|
+
"""Parse a strict v4 Live CLI datetime token."""
|
|
71
|
+
if not V4_DATETIME_RE.fullmatch(value):
|
|
72
|
+
raise click.UsageError(
|
|
73
|
+
f"Invalid {option_name}: {value}. Expected YYYY-MM-DDTHH:MM:SS"
|
|
74
|
+
)
|
|
75
|
+
try:
|
|
76
|
+
return datetime.strptime(value, V4_DATETIME_FORMAT)
|
|
77
|
+
except ValueError:
|
|
78
|
+
raise click.UsageError(
|
|
79
|
+
f"Invalid {option_name}: {value}. Expected YYYY-MM-DDTHH:MM:SS"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _events_log_param(
|
|
84
|
+
timestamp_from: str,
|
|
85
|
+
timestamp_to: str,
|
|
86
|
+
currency: str,
|
|
87
|
+
limit: Optional[int],
|
|
88
|
+
offset: Optional[int],
|
|
89
|
+
last_event_only: Optional[str] = None,
|
|
90
|
+
with_text_description: Optional[str] = None,
|
|
91
|
+
logins: Optional[str] = None,
|
|
92
|
+
campaign_ids: Optional[str] = None,
|
|
93
|
+
banner_ids: Optional[str] = None,
|
|
94
|
+
phrase_ids: Optional[str] = None,
|
|
95
|
+
account_ids: Optional[str] = None,
|
|
96
|
+
event_type: Optional[str] = None,
|
|
97
|
+
) -> dict:
|
|
98
|
+
"""Build the v4 Live GetEventsLog parameter."""
|
|
99
|
+
from_dt = _parse_v4_datetime(timestamp_from, "--from")
|
|
100
|
+
to_dt = _parse_v4_datetime(timestamp_to, "--to")
|
|
101
|
+
if from_dt > to_dt:
|
|
102
|
+
raise click.UsageError("--from must be earlier than or equal to --to")
|
|
103
|
+
|
|
104
|
+
param = {
|
|
105
|
+
"TimestampFrom": timestamp_from,
|
|
106
|
+
"TimestampTo": timestamp_to,
|
|
107
|
+
"Currency": currency,
|
|
108
|
+
}
|
|
109
|
+
if last_event_only:
|
|
110
|
+
param["LastEventOnly"] = last_event_only
|
|
111
|
+
if with_text_description:
|
|
112
|
+
param["WithTextDescription"] = with_text_description
|
|
113
|
+
if logins:
|
|
114
|
+
login_list = parse_csv_strings(logins)
|
|
115
|
+
if login_list:
|
|
116
|
+
param["Logins"] = login_list
|
|
117
|
+
filter_obj = _events_log_filter(
|
|
118
|
+
campaign_ids, banner_ids, phrase_ids, account_ids, event_type
|
|
119
|
+
)
|
|
120
|
+
if filter_obj is not None:
|
|
121
|
+
param["Filter"] = filter_obj
|
|
122
|
+
if limit is not None:
|
|
123
|
+
param["Limit"] = limit
|
|
124
|
+
if offset is not None:
|
|
125
|
+
param["Offset"] = offset
|
|
126
|
+
return param
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@click.group(epilog=V4_EPILOG)
|
|
130
|
+
def v4events():
|
|
131
|
+
"""Yandex Direct v4 Live events commands."""
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@v4_method_contract("GetEventsLog")
|
|
135
|
+
@v4events.command(name="get-events-log")
|
|
136
|
+
@click.option(
|
|
137
|
+
"--from",
|
|
138
|
+
"timestamp_from",
|
|
139
|
+
required=True,
|
|
140
|
+
help="Start timestamp in YYYY-MM-DDTHH:MM:SS format",
|
|
141
|
+
)
|
|
142
|
+
@click.option(
|
|
143
|
+
"--to",
|
|
144
|
+
"timestamp_to",
|
|
145
|
+
required=True,
|
|
146
|
+
help="End timestamp in YYYY-MM-DDTHH:MM:SS format",
|
|
147
|
+
)
|
|
148
|
+
@click.option(
|
|
149
|
+
"--last-event-only",
|
|
150
|
+
type=click.Choice(["Yes", "No"]),
|
|
151
|
+
help="Return only the latest record per event type — Yes/No",
|
|
152
|
+
)
|
|
153
|
+
@click.option(
|
|
154
|
+
"--with-text-description",
|
|
155
|
+
type=click.Choice(["Yes", "No"]),
|
|
156
|
+
help="Include event text descriptions in the response — Yes/No",
|
|
157
|
+
)
|
|
158
|
+
@click.option("--currency", default="RUB", show_default=True, help="Currency")
|
|
159
|
+
@click.option("--logins", help="Comma-separated client logins")
|
|
160
|
+
@click.option("--filter-campaign-ids", help="Filter: comma-separated campaign IDs")
|
|
161
|
+
@click.option("--filter-banner-ids", help="Filter: comma-separated banner IDs")
|
|
162
|
+
@click.option("--filter-phrase-ids", help="Filter: comma-separated phrase IDs")
|
|
163
|
+
@click.option("--filter-account-ids", help="Filter: comma-separated account IDs")
|
|
164
|
+
@click.option(
|
|
165
|
+
"--filter-event-type",
|
|
166
|
+
help="Filter: comma-separated event types (" + ", ".join(EVENT_TYPES) + ")",
|
|
167
|
+
)
|
|
168
|
+
@click.option("--limit", type=click.IntRange(min=0), help="Result limit")
|
|
169
|
+
@click.option("--offset", type=click.IntRange(min=0), help="Result offset")
|
|
170
|
+
@click.option(
|
|
171
|
+
"--format",
|
|
172
|
+
"output_format",
|
|
173
|
+
default="json",
|
|
174
|
+
type=click.Choice(["json", "table", "csv", "tsv"]),
|
|
175
|
+
help="Output format",
|
|
176
|
+
)
|
|
177
|
+
@click.option("--output", help="Output file")
|
|
178
|
+
@click.option("--dry-run", is_flag=True, help="Show request without sending")
|
|
179
|
+
@click.pass_context
|
|
180
|
+
def get_events_log(
|
|
181
|
+
ctx,
|
|
182
|
+
timestamp_from,
|
|
183
|
+
timestamp_to,
|
|
184
|
+
last_event_only,
|
|
185
|
+
with_text_description,
|
|
186
|
+
currency,
|
|
187
|
+
logins,
|
|
188
|
+
filter_campaign_ids,
|
|
189
|
+
filter_banner_ids,
|
|
190
|
+
filter_phrase_ids,
|
|
191
|
+
filter_account_ids,
|
|
192
|
+
filter_event_type,
|
|
193
|
+
limit,
|
|
194
|
+
offset,
|
|
195
|
+
output_format,
|
|
196
|
+
output,
|
|
197
|
+
dry_run,
|
|
198
|
+
):
|
|
199
|
+
"""Get v4 Live events log entries."""
|
|
200
|
+
param = _events_log_param(
|
|
201
|
+
timestamp_from,
|
|
202
|
+
timestamp_to,
|
|
203
|
+
currency,
|
|
204
|
+
limit,
|
|
205
|
+
offset,
|
|
206
|
+
last_event_only=last_event_only,
|
|
207
|
+
with_text_description=with_text_description,
|
|
208
|
+
logins=logins,
|
|
209
|
+
campaign_ids=filter_campaign_ids,
|
|
210
|
+
banner_ids=filter_banner_ids,
|
|
211
|
+
phrase_ids=filter_phrase_ids,
|
|
212
|
+
account_ids=filter_account_ids,
|
|
213
|
+
event_type=filter_event_type,
|
|
214
|
+
)
|
|
215
|
+
if dry_run:
|
|
216
|
+
format_output(build_v4_body("GetEventsLog", param), "json", None)
|
|
217
|
+
return
|
|
218
|
+
|
|
219
|
+
try:
|
|
220
|
+
client = create_v4_client(
|
|
221
|
+
token=ctx.obj.get("token"),
|
|
222
|
+
login=ctx.obj.get("login"),
|
|
223
|
+
profile=ctx.obj.get("profile"),
|
|
224
|
+
sandbox=ctx.obj.get("sandbox"),
|
|
225
|
+
)
|
|
226
|
+
data = call_v4(client, "GetEventsLog", param)
|
|
227
|
+
format_output(data, output_format, output)
|
|
228
|
+
except click.ClickException:
|
|
229
|
+
raise
|
|
230
|
+
except Exception as e:
|
|
231
|
+
print_error(str(e))
|
|
232
|
+
raise click.Abort()
|