direct-cli 0.3.13__tar.gz → 0.3.15__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.
Files changed (239) hide show
  1. {direct_cli-0.3.13 → direct_cli-0.3.15}/CHANGELOG.md +280 -0
  2. {direct_cli-0.3.13 → direct_cli-0.3.15}/CLAUDE.md +30 -0
  3. {direct_cli-0.3.13 → direct_cli-0.3.15}/PKG-INFO +4 -4
  4. {direct_cli-0.3.13 → direct_cli-0.3.15}/README.md +3 -3
  5. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/_bidding_strategy.py +0 -5
  6. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/_vendor/tapi_yandex_direct/resource_mapping.py +8 -8
  7. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/adgroups.py +117 -0
  8. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/ads.py +193 -7
  9. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/agencyclients.py +85 -1
  10. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/bidmodifiers.py +153 -1
  11. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/campaigns.py +113 -50
  12. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/clients.py +85 -1
  13. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/creatives.py +71 -2
  14. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/feeds.py +47 -2
  15. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/keywordbids.py +61 -3
  16. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/keywords.py +59 -1
  17. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/sitelinks.py +37 -4
  18. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/strategies.py +256 -10
  19. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/v4finance.py +4 -35
  20. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/reports_coverage.py +23 -1
  21. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/utils.py +35 -1
  22. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/v4_contracts.py +28 -18
  23. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/wsdl_coverage.py +57 -1
  24. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli.egg-info/PKG-INFO +4 -4
  25. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli.egg-info/SOURCES.txt +2 -0
  26. {direct_cli-0.3.13 → direct_cli-0.3.15}/pyproject.toml +1 -1
  27. direct_cli-0.3.15/scripts/check_all_docs_urls.py +166 -0
  28. direct_cli-0.3.15/scripts/preflight_check.sh +62 -0
  29. {direct_cli-0.3.13 → direct_cli-0.3.15}/scripts/release_pypi.sh +8 -0
  30. {direct_cli-0.3.13 → direct_cli-0.3.15}/scripts/test_dangerous_commands.sh +1 -1
  31. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/API_COVERAGE.md +6 -5
  32. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/api_coverage_payloads.py +9 -6
  33. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/reports_cache/raw/fields-list.html +6 -6
  34. direct_cli-0.3.15/tests/reports_cache/raw/headers.html +88 -0
  35. direct_cli-0.3.15/tests/reports_cache/raw/period.html +119 -0
  36. direct_cli-0.3.15/tests/reports_cache/raw/spec.html +643 -0
  37. direct_cli-0.3.15/tests/reports_cache/raw/type.html +279 -0
  38. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/reports_cache/spec.json +1 -1
  39. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_api_coverage.py +191 -19
  40. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_comprehensive.py +7 -20
  41. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_dry_run.py +1263 -117
  42. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_integration.py +84 -9
  43. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_low_coverage_payloads.py +1 -1
  44. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_sandbox_write_audit.py +7 -3
  45. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_smoke_matrix.py +49 -7
  46. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_v4_contracts.py +4 -4
  47. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_v4finance_money.py +15 -20
  48. direct_cli-0.3.13/tests/reports_cache/raw/headers.html +0 -88
  49. direct_cli-0.3.13/tests/reports_cache/raw/period.html +0 -119
  50. direct_cli-0.3.13/tests/reports_cache/raw/spec.html +0 -660
  51. direct_cli-0.3.13/tests/reports_cache/raw/type.html +0 -279
  52. {direct_cli-0.3.13 → direct_cli-0.3.15}/.env.example +0 -0
  53. {direct_cli-0.3.13 → direct_cli-0.3.15}/.github/copilot-instructions.md +0 -0
  54. {direct_cli-0.3.13 → direct_cli-0.3.15}/.github/workflows/api-coverage.yml +0 -0
  55. {direct_cli-0.3.13 → direct_cli-0.3.15}/.github/workflows/claude.yml +0 -0
  56. {direct_cli-0.3.13 → direct_cli-0.3.15}/.github/workflows/quality.yml +0 -0
  57. {direct_cli-0.3.13 → direct_cli-0.3.15}/.gitignore +0 -0
  58. {direct_cli-0.3.13 → direct_cli-0.3.15}/AGENTS.md +0 -0
  59. {direct_cli-0.3.13 → direct_cli-0.3.15}/MANIFEST.in +0 -0
  60. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/__init__.py +0 -0
  61. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/_deprecated.py +0 -0
  62. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/_smoke_probes.py +0 -0
  63. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/_vendor/__init__.py +0 -0
  64. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/_vendor/tapi_yandex_direct/__init__.py +0 -0
  65. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/_vendor/tapi_yandex_direct/endpoints.py +0 -0
  66. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/_vendor/tapi_yandex_direct/exceptions.py +0 -0
  67. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.py +0 -0
  68. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.pyi +0 -0
  69. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/_vendor/tapi_yandex_direct/v4/__init__.py +0 -0
  70. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.py +0 -0
  71. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.pyi +0 -0
  72. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/_vendor/tapi_yandex_direct/v4/resource_mapping.py +0 -0
  73. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/api.py +0 -0
  74. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/auth.py +0 -0
  75. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/cli.py +0 -0
  76. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/__init__.py +0 -0
  77. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/adextensions.py +0 -0
  78. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/adimages.py +0 -0
  79. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/advideos.py +0 -0
  80. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/audiencetargets.py +0 -0
  81. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/auth.py +0 -0
  82. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/balance.py +0 -0
  83. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/bids.py +0 -0
  84. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/businesses.py +0 -0
  85. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/changes.py +0 -0
  86. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/dictionaries.py +0 -0
  87. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/dynamicads.py +0 -0
  88. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/dynamicfeedadtargets.py +0 -0
  89. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/keywordsresearch.py +0 -0
  90. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/leads.py +0 -0
  91. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/negativekeywordsharedsets.py +0 -0
  92. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/reports.py +0 -0
  93. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/retargeting.py +0 -0
  94. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/smartadtargets.py +0 -0
  95. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/turbopages.py +0 -0
  96. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/v4account.py +0 -0
  97. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/v4events.py +0 -0
  98. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/v4forecast.py +0 -0
  99. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/v4goals.py +0 -0
  100. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/v4shells.py +0 -0
  101. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/v4tags.py +0 -0
  102. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/v4wordstat.py +0 -0
  103. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/commands/vcards.py +0 -0
  104. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/output.py +0 -0
  105. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/smoke_matrix.py +0 -0
  106. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/v4/__init__.py +0 -0
  107. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli/v4/money.py +0 -0
  108. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli.egg-info/dependency_links.txt +0 -0
  109. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli.egg-info/entry_points.txt +0 -0
  110. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli.egg-info/requires.txt +0 -0
  111. {direct_cli-0.3.13 → direct_cli-0.3.15}/direct_cli.egg-info/top_level.txt +0 -0
  112. {direct_cli-0.3.13 → direct_cli-0.3.15}/docs/audits/issue-198-mutating-wsdl-audit.md +0 -0
  113. {direct_cli-0.3.13 → direct_cli-0.3.15}/docs/superpowers/plans/2026-04-12-issue-32-completion.md +0 -0
  114. {direct_cli-0.3.13 → direct_cli-0.3.15}/docs/superpowers/specs/2026-04-23-vendor-tapi-yandex-direct-design.md +0 -0
  115. {direct_cli-0.3.13 → direct_cli-0.3.15}/scripts/anonymize_cassettes.py +0 -0
  116. {direct_cli-0.3.13 → direct_cli-0.3.15}/scripts/build_api_coverage_checklist.py +0 -0
  117. {direct_cli-0.3.13 → direct_cli-0.3.15}/scripts/build_api_coverage_report.py +0 -0
  118. {direct_cli-0.3.13 → direct_cli-0.3.15}/scripts/build_wsdl_optional_field_audit.py +0 -0
  119. {direct_cli-0.3.13 → direct_cli-0.3.15}/scripts/check_reports_drift.py +0 -0
  120. {direct_cli-0.3.13 → direct_cli-0.3.15}/scripts/check_wsdl_drift.py +0 -0
  121. {direct_cli-0.3.13 → direct_cli-0.3.15}/scripts/patch_vendor_imports.py +0 -0
  122. {direct_cli-0.3.13 → direct_cli-0.3.15}/scripts/refresh_reports_cache.py +0 -0
  123. {direct_cli-0.3.13 → direct_cli-0.3.15}/scripts/refresh_wsdl_cache.py +0 -0
  124. {direct_cli-0.3.13 → direct_cli-0.3.15}/scripts/sandbox_write_audit.py +0 -0
  125. {direct_cli-0.3.13 → direct_cli-0.3.15}/scripts/sandbox_write_live.py +0 -0
  126. {direct_cli-0.3.13 → direct_cli-0.3.15}/scripts/test_safe_commands.sh +0 -0
  127. {direct_cli-0.3.13 → direct_cli-0.3.15}/scripts/test_sandbox_write.sh +0 -0
  128. {direct_cli-0.3.13 → direct_cli-0.3.15}/scripts/update_vendor.sh +0 -0
  129. {direct_cli-0.3.13 → direct_cli-0.3.15}/setup.cfg +0 -0
  130. {direct_cli-0.3.13 → direct_cli-0.3.15}/setup.py +0 -0
  131. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/API_ISSUE_AUDIT.md +0 -0
  132. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/MANUAL_COVERAGE.md +0 -0
  133. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/WSDL_OPTIONAL_FIELD_AUDIT.md +0 -0
  134. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/__init__.py +0 -0
  135. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/_orphan_store.py +0 -0
  136. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteAdExtensions.test_add_delete.yaml +0 -0
  137. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteAdGroups.test_add_update_delete.yaml +0 -0
  138. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteAdImages.test_add_delete.yaml +0 -0
  139. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteAds.test_add_text_ad_update_delete.yaml +0 -0
  140. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteAudienceTargets.test_add_delete.yaml +0 -0
  141. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteBidModifiersAdd.test_add_delete_mobile.yaml +0 -0
  142. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteBidModifiersSet.test_set_without_id_is_rejected.yaml +0 -0
  143. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteBids.test_set_bid.yaml +0 -0
  144. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteBidsRead.test_bids_get.yaml +0 -0
  145. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteBidsRead.test_bids_set_auto.yaml +0 -0
  146. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteCampaignDraftLifecycle.test_draft_create_get_delete.yaml +0 -0
  147. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteCampaigns.test_campaign_lifecycle.yaml +0 -0
  148. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteDynamicAds.test_add_update_delete.yaml +0 -0
  149. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteFeeds.test_add_update_delete.yaml +0 -0
  150. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteKeywordBids.test_set_keyword_bid.yaml +0 -0
  151. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteKeywords.test_add_update_delete.yaml +0 -0
  152. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteNegativeKeywordSharedSets.test_add_update_delete.yaml +0 -0
  153. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteRetargeting.test_add_delete.yaml +0 -0
  154. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteRetargetingUpdate.test_retargeting_update.yaml +0 -0
  155. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteSitelinks.test_add_delete.yaml +0 -0
  156. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteSmartAdTargets.test_add_update_delete.yaml +0 -0
  157. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteStrategies.test_strategies_lifecycle.yaml +0 -0
  158. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_integration_write/TestWriteVCards.test_add_delete.yaml +0 -0
  159. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_v5_live_write/test_v5_live_draft_adgroups_add_update_delete.yaml +0 -0
  160. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_v5_live_write/test_v5_live_draft_adimages_add_get_delete.yaml +0 -0
  161. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_v5_live_write/test_v5_live_draft_ads_add_update_delete.yaml +0 -0
  162. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_v5_live_write/test_v5_live_draft_ads_suspend_resume_archive_unarchive.yaml +0 -0
  163. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_v5_live_write/test_v5_live_draft_advideos_add_get.yaml +0 -0
  164. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_add_delete.yaml +0 -0
  165. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_suspend_resume.yaml +0 -0
  166. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_v5_live_write/test_v5_live_draft_bids_set.yaml +0 -0
  167. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_v5_live_write/test_v5_live_draft_campaign_create_get_delete.yaml +0 -0
  168. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_v5_live_write/test_v5_live_draft_creatives_chain_advideo_to_creative.yaml +0 -0
  169. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_v5_live_write/test_v5_live_draft_dynamicads_add_delete.yaml +0 -0
  170. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_v5_live_write/test_v5_live_draft_dynamicads_suspend_resume.yaml +0 -0
  171. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywordbids_set.yaml +0 -0
  172. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywords_add_update_delete.yaml +0 -0
  173. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywords_suspend_resume.yaml +0 -0
  174. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_v5_live_write/test_v5_live_draft_sitelinks_add_get_delete.yaml +0 -0
  175. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_v5_live_write/test_v5_live_draft_smartadtargets_add_update_delete.yaml +0 -0
  176. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/cassettes/test_v5_live_write/test_v5_live_draft_smartadtargets_suspend_resume.yaml +0 -0
  177. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/conftest.py +0 -0
  178. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/fixtures/test-video.mp4 +0 -0
  179. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_auth_bw.py +0 -0
  180. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_auth_oauth.py +0 -0
  181. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_auth_op.py +0 -0
  182. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_auth_write_json.py +0 -0
  183. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_balance.py +0 -0
  184. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_changes.py +0 -0
  185. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_cli.py +0 -0
  186. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_cli_contract.py +0 -0
  187. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_env_loading.py +0 -0
  188. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_integration_write.py +0 -0
  189. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_reports_drift.py +0 -0
  190. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_reports_parsing.py +0 -0
  191. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_transport_contract.py +0 -0
  192. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_unknown_option_hints.py +0 -0
  193. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_v4_exit_codes.py +0 -0
  194. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_v4_foundation.py +0 -0
  195. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_v4_live_contracts.py +0 -0
  196. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_v4_runtime_shape.py +0 -0
  197. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_v4_safety.py +0 -0
  198. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_v4account.py +0 -0
  199. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_v4events.py +0 -0
  200. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_v4finance_read.py +0 -0
  201. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_v4forecast.py +0 -0
  202. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_v4goals.py +0 -0
  203. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_v4tags.py +0 -0
  204. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_v4wordstat.py +0 -0
  205. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_v5_live_write.py +0 -0
  206. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_vendor_imports.py +0 -0
  207. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/test_wsdl_parity_gate.py +0 -0
  208. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/adextensions.xml +0 -0
  209. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/adgroups.xml +0 -0
  210. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/adimages.xml +0 -0
  211. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/ads.xml +0 -0
  212. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/advideos.xml +0 -0
  213. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/agencyclients.xml +0 -0
  214. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/audiencetargets.xml +0 -0
  215. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/bidmodifiers.xml +0 -0
  216. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/bids.xml +0 -0
  217. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/businesses.xml +0 -0
  218. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/campaigns.xml +0 -0
  219. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/changes.xml +0 -0
  220. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/clients.xml +0 -0
  221. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/creatives.xml +0 -0
  222. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/dictionaries.xml +0 -0
  223. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/dynamicfeedadtargets.xml +0 -0
  224. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/dynamictextadtargets.xml +0 -0
  225. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/feeds.xml +0 -0
  226. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/imports/adextensiontypes.xsd +0 -0
  227. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/imports/general.xsd +0 -0
  228. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/imports/generalclients.xsd +0 -0
  229. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/keywordbids.xml +0 -0
  230. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/keywords.xml +0 -0
  231. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/keywordsresearch.xml +0 -0
  232. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/leads.xml +0 -0
  233. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/negativekeywordsharedsets.xml +0 -0
  234. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/retargetinglists.xml +0 -0
  235. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/sitelinks.xml +0 -0
  236. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/smartadtargets.xml +0 -0
  237. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/strategies.xml +0 -0
  238. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/turbopages.xml +0 -0
  239. {direct_cli-0.3.13 → direct_cli-0.3.15}/tests/wsdl_cache/vcards.xml +0 -0
@@ -1,5 +1,285 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.15
4
+
5
+ **BREAKING CHANGES:**
6
+
7
+ - `direct v4finance transfer-money` no longer accepts `--currency`, and
8
+ the wire-body no longer carries `Currency` on `FromCampaigns`/
9
+ `ToCampaigns` items. The official v4 docs
10
+ (`dg-v4/reference/TransferMoney`) define `PayCampElement` with only
11
+ `CampaignID` and `Sum`; `Sum` is in conventional units. The CLI now
12
+ matches the docs 1:1. Closes #432.
13
+ - `direct v4finance pay-campaigns` no longer accepts `--currency`. The
14
+ v4 documentation (`dg-v4/reference/PayCampaigns`) defines
15
+ `PayCampElement` with only `CampaignID` and `Sum` — `Currency` is not
16
+ part of the wire-body and was never forwarded to the API. The option
17
+ is removed entirely to make the CLI surface 1:1 with the docs.
18
+ - `direct v4finance pay-campaigns` no longer accepts `--pay-method
19
+ Overdraft`. The v4 documentation
20
+ (`dg-v4/reference/PayCampaigns#PayMethod`) lists only `"Bank"` as a
21
+ supported value; `Overdraft` was a historical undocumented value
22
+ retained by the CLI for sandbox flow. Strict 1:1 docs alignment
23
+ drops it.
24
+ - `direct v4finance create-invoice` no longer accepts `--currency`. The
25
+ v4 documentation (`dg-v4/reference/CreateInvoice`) defines
26
+ `PayCampElement` with only `CampaignID` and `Sum` — `Currency` is not
27
+ part of the wire-body and was never forwarded to the API. The option
28
+ is removed entirely to make the CLI surface 1:1 with the docs.
29
+
30
+ ## 0.3.14
31
+
32
+ **Fixed:**
33
+
34
+ - Reports drift checker now points at the canonical Yandex docs URLs
35
+ (`/ru/type`, `/ru/period`, `/ru/fields-list`, `/ru/spec`) after Yandex
36
+ retired the `/ru/reports/<page>` path layout and renamed `spec.html`
37
+ to `spec`. The pre-existing `tests/reports_cache/raw/` had silently
38
+ been captcha-poisoned for three of those pages (~14.6 KB Yandex
39
+ SmartCaptcha gateway in place of real docs); cache is now refetched
40
+ from the live canonical URLs and `spec.json` is byte-equivalent to
41
+ the pre-migration snapshot except for one updated description string.
42
+ - Five `RESOURCE_MAPPING_V5[*]["docs"]` URLs that Yandex moved from the
43
+ legacy `…/ru/<group>/<group>` to `…/ru/<group>` single-segment form
44
+ (`dynamictextadtargets`, `dynamicfeedadtargets`, `reports`,
45
+ `smartadtargets`, `vcards`). Closes #426.
46
+
47
+ **Added (drift protection):**
48
+
49
+ - `direct_cli/reports_coverage.py::fetch_reports_spec` and
50
+ `direct_cli/wsdl_coverage.py::fetch_wsdl` / `fetch_live_wsdl` now
51
+ refuse responses that look like a Yandex SmartCaptcha gateway (markers
52
+ `showcaptcha`, `smartcaptcha`, `<title>Captcha`) or are suspiciously
53
+ short. This prevents silently poisoning the docs/WSDL cache with
54
+ rate-limited captcha HTML.
55
+ - `tests/test_api_coverage.py::TestReportsCoverage::test_reports_cache_files_are_real_content`
56
+ and `TestWsdlCacheFreshness::test_wsdl_cache_files_are_real_content`
57
+ guard the committed cache files against the same poisoning.
58
+ - `scripts/check_all_docs_urls.py` — health-checks every URL in
59
+ `RESOURCE_MAPPING_V5` and `REPORTS_SPEC_URLS`. Hard-fails on
60
+ redirect-to-captcha, canonical move (`Location` with a different path
61
+ segment), 4xx, or captcha body; soft-warns on 5xx; paces requests to
62
+ avoid Yandex rate-limit. Wired into `scripts/release_pypi.sh` as a
63
+ mandatory pre-release gate together with `refresh_reports_cache.py`
64
+ and a focused pytest pass.
65
+
66
+ **Contract** (`CLAUDE.md`):
67
+
68
+ - New rule "No URL literals outside the registry" — every Yandex
69
+ docs/API URL is declared once in `RESOURCE_MAPPING_V5` or
70
+ `REPORTS_SPEC_URLS`; importers reference the constant.
71
+ - New rule "Docs/cache freshness guard" — fetchers and cache files
72
+ enforce minimum-size and no-captcha invariants.
73
+ - New section "PyPI Release" — documents the three pre-release health
74
+ checks executed by `release_pypi.sh`.
75
+
76
+ **Breaking changes:**
77
+
78
+ - `direct ads get` flag `--text-ad-fields` is **renamed** to the
79
+ WSDL-canonical `--text-ad-field-names` form matching the
80
+ `TextAdFieldNames` request parameter declared by `AdsGetRequest`.
81
+ The old `--text-ad-fields` form is no longer accepted — update
82
+ scripts and automation accordingly. Closes #406.
83
+ - `direct campaigns add` / `direct campaigns update` and `direct
84
+ strategies add` / `direct strategies update` now reject `--priority-goals`
85
+ / `--priority-goal` values below 100,000 (0.1 unit in micro-currency).
86
+ Per Yandex Direct API (add-text-campaign, strategies-types),
87
+ `PriorityGoalsItem.Value` is `xsd:long` in advertiser currency
88
+ multiplied by 1,000,000 — the same contract as `--budget`,
89
+ `--average-cpa`, and other money flags after #399/#400. The error
90
+ message suggests the micro-currency conversion (e.g. `Did you mean
91
+ 500000000?`). Negative values are also rejected up-front rather than
92
+ reaching the API. Both parsers share a single
93
+ `validate_priority_goal_value` helper. Closes #387.
94
+
95
+ **Added:**
96
+
97
+ - `direct sitelinks get` now exposes `--sitelink-field-names` for the
98
+ separate WSDL `SitelinkFieldNames` request parameter
99
+ (`SitelinkFieldEnum`: `Title`, `Href`, `Description`, `TurboPageId`).
100
+ Previously only the top-level `--fields` (mapping to `FieldNames`)
101
+ was available, so the nested `Sitelinks[]` projection could not be
102
+ controlled from CLI.
103
+ - `direct keywordbids get` now exposes `--fields`,
104
+ `--search-field-names`, and `--network-field-names` for the
105
+ separate `FieldNames`, `SearchFieldNames`, and `NetworkFieldNames`
106
+ request parameters declared by `KeywordBidsGetRequest`. Defaults
107
+ from `COMMON_FIELDS` are preserved when flags are absent.
108
+ - Regression test `test_every_nested_fieldnames_param_has_cli_option`
109
+ (`tests/test_api_coverage.py`) scans every cached WSDL `get`
110
+ request type for `*FieldNames` parameters and verifies that each
111
+ one has a matching kebab-case CLI option. Acknowledged remaining
112
+ gaps are tracked in `NESTED_FIELDNAMES_EXCLUSIONS` and #402 so
113
+ future additions cannot silently slip in.
114
+ - `direct feeds get` now exposes `--file-feed-field-names` and
115
+ `--url-feed-field-names` for the separate WSDL `FileFeedFieldNames`
116
+ (`FileFeedFieldEnum`: `Filename`) and `UrlFeedFieldNames`
117
+ (`UrlFeedFieldEnum`: `Login`, `Url`, `RemoveUtmTags`) request
118
+ parameters declared by `FeedsGetRequest`. Previously only the
119
+ top-level `--fields` (mapping to `FieldNames`) was available, so
120
+ the nested `FileFeed` / `UrlFeed` projections could not be
121
+ controlled from CLI. Closes #412.
122
+ - `direct keywords get` now exposes
123
+ `--autotargeting-settings-brand-options-field-names`
124
+ (`AutotargetingBrandOptionsFieldEnum`: `WithoutBrands`,
125
+ `WithAdvertiserBrand`, `WithCompetitorsBrand`) and
126
+ `--autotargeting-settings-categories-field-names`
127
+ (`AutotargetingCategoriesFieldEnum`: `Exact`, `Narrow`,
128
+ `Alternative`, `Accessory`, `Broader`) for the separate WSDL
129
+ `*FieldNames` request parameters declared by
130
+ `KeywordsGetRequest`. Previously only the top-level `--fields`
131
+ (mapping to `FieldNames`) was available, so the nested
132
+ `AutotargetingSettings.BrandOptions` / `Categories` projections
133
+ could not be controlled from CLI. Closes #413.
134
+ - `direct creatives get` now exposes
135
+ `--cpc-video-creative-field-names`,
136
+ `--cpm-video-creative-field-names`,
137
+ `--smart-creative-field-names`, and
138
+ `--video-extension-creative-field-names` for the four nested
139
+ WSDL `*FieldNames` request parameters declared by
140
+ `CreativesGetRequest` (`CpcVideoCreativeFieldEnum`,
141
+ `CpmVideoCreativeFieldEnum`, `SmartCreativeFieldEnum`,
142
+ `VideoExtensionCreativeFieldEnum`). Previously only the top-level
143
+ `--fields` (mapping to `FieldNames`) was available, so the
144
+ per-subtype projections could not be controlled from CLI.
145
+ Closes #411.
146
+ - `direct clients get` now exposes `--contract-field-names`,
147
+ `--contragent-field-names`, `--contragent-tin-info-field-names`,
148
+ `--organization-field-names`, and `--tin-info-field-names` for
149
+ the five nested WSDL `*FieldNames` request parameters declared
150
+ by `ClientsGetRequest` (`ContractInfoFieldEnum`,
151
+ `ContragentInfoFieldEnum`, `TinInfoFieldEnum`,
152
+ `OrgInfoFieldEnum`, `TinInfoFieldEnum`). The command also gains
153
+ `--dry-run` for parity with other read-path commands.
154
+ Previously only the top-level `--fields` (mapping to `FieldNames`)
155
+ was available, so the per-subtype ERIR projections could not be
156
+ controlled from CLI. Closes #410.
157
+ - `direct agencyclients get` now exposes `--contract-field-names`,
158
+ `--contragent-field-names`, `--contragent-tin-info-field-names`,
159
+ `--organization-field-names`, and `--tin-info-field-names` for
160
+ the five nested WSDL `*FieldNames` request parameters declared
161
+ by `AgencyClientsGetRequest` (`ContractInfoFieldEnum`,
162
+ `ContragentInfoFieldEnum`, `TinInfoFieldEnum`,
163
+ `OrgInfoFieldEnum`, `TinInfoFieldEnum`). The command also gains
164
+ `--dry-run` for parity with other read-path commands.
165
+ Previously only the top-level `--fields` (mapping to `FieldNames`)
166
+ was available, so the per-subtype ERIR projections could not be
167
+ controlled from CLI. Closes #407.
168
+ - `direct adgroups get` now exposes eight additional
169
+ `--*-field-names` flags for the separate WSDL `*FieldNames`
170
+ request parameters declared by `AdGroupsGetRequest`:
171
+ `--autotargeting-settings-brand-options-field-names`,
172
+ `--autotargeting-settings-categories-field-names`,
173
+ `--dynamic-text-ad-group-field-names`,
174
+ `--dynamic-text-feed-ad-group-field-names`,
175
+ `--mobile-app-ad-group-field-names`,
176
+ `--smart-ad-group-field-names`,
177
+ `--text-ad-group-feed-params-field-names`, and
178
+ `--unified-ad-group-field-names`. Previously only the top-level
179
+ `--fields` (mapping to `FieldNames`) was available, so the
180
+ per-subtype ad-group projections could not be controlled from
181
+ CLI. Closes #405.
182
+ - `direct ads get` now exposes sixteen additional `--*-field-names`
183
+ flags for the separate WSDL `*FieldNames` request parameters
184
+ declared by `AdsGetRequest`: `--cpc-video-ad-builder-ad-field-names`,
185
+ `--cpm-banner-ad-builder-ad-field-names`,
186
+ `--cpm-video-ad-builder-ad-field-names`,
187
+ `--dynamic-text-ad-field-names`, `--listing-ad-field-names`,
188
+ `--mobile-app-ad-builder-ad-field-names`,
189
+ `--mobile-app-ad-field-names`,
190
+ `--mobile-app-cpc-video-ad-builder-ad-field-names`,
191
+ `--mobile-app-image-ad-field-names`,
192
+ `--responsive-ad-field-names`, `--shopping-ad-field-names`,
193
+ `--smart-ad-builder-ad-field-names`,
194
+ `--text-ad-builder-ad-field-names`,
195
+ `--text-ad-field-names`,
196
+ `--text-ad-price-extension-field-names`, and
197
+ `--text-image-ad-field-names`. Previously only the top-level
198
+ `--fields` (mapping to `FieldNames`) and non-canonical
199
+ `--text-ad-fields` were available, so the per-ad-subtype projections
200
+ could not be controlled from CLI. Closes #406.
201
+
202
+ **BREAKING CHANGES:**
203
+
204
+ - `direct campaigns get` flags `--text-campaign-fields`,
205
+ `--mobile-app-campaign-fields`, `--dynamic-text-campaign-fields`,
206
+ `--cpm-banner-campaign-fields`, `--smart-campaign-fields`,
207
+ `--unified-campaign-fields`,
208
+ `--text-campaign-search-strategy-placement-types-fields`,
209
+ `--dynamic-text-campaign-search-strategy-placement-types-fields`,
210
+ `--unified-campaign-search-strategy-placement-types-fields`, and
211
+ `--unified-campaign-package-bidding-strategy-platforms-fields`
212
+ are **renamed** to their kebab-case WSDL-canonical `*-field-names`
213
+ form (`--text-campaign-field-names`,
214
+ `--mobile-app-campaign-field-names`, ...), matching the parameter
215
+ names declared by `CampaignsGetRequest`. The old `--*-fields`
216
+ forms are no longer accepted — update scripts and automation
217
+ accordingly. Closes #409.
218
+
219
+ **Additional features:**
220
+
221
+ - `direct bidmodifiers get` now exposes thirteen additional
222
+ `--*-adjustment-field-names` flags for the per-adjustment-subtype
223
+ WSDL `*FieldNames` request parameters declared by
224
+ `BidModifiersGetRequest`: `--ad-group-adjustment-field-names`,
225
+ `--demographics-adjustment-field-names`,
226
+ `--desktop-adjustment-field-names`,
227
+ `--desktop-only-adjustment-field-names`,
228
+ `--income-grade-adjustment-field-names`,
229
+ `--mobile-adjustment-field-names`,
230
+ `--regional-adjustment-field-names`,
231
+ `--retargeting-adjustment-field-names`,
232
+ `--serp-layout-adjustment-field-names`,
233
+ `--smart-ad-adjustment-field-names`,
234
+ `--smart-tv-adjustment-field-names`,
235
+ `--tablet-adjustment-field-names`, and
236
+ `--video-adjustment-field-names`. Previously only the top-level
237
+ `--fields` (mapping to `FieldNames`) was available, so the
238
+ per-adjustment projections could not be controlled from CLI.
239
+ Closes #408.
240
+ - `direct strategies get` now exposes sixteen additional
241
+ `--strategy-*-field-names` flags for the separate WSDL
242
+ `*FieldNames` request parameters declared by `StrategiesGetRequest`,
243
+ including `--strategy-average-cpa-field-names`,
244
+ `--strategy-average-cpa-multiple-goals-field-names`,
245
+ `--strategy-average-cpc-field-names`,
246
+ `--strategy-maximum-clicks-field-names`,
247
+ `--strategy-maximum-conversion-rate-field-names`,
248
+ `--strategy-pay-for-conversion-field-names`, and the remaining
249
+ per-campaign / per-filter strategy projections. The command also
250
+ gains `--dry-run` for read-path payload tests. Previously only the
251
+ top-level `--fields` (mapping to `FieldNames`) was available, so
252
+ per-strategy-subtype projections could not be controlled from CLI.
253
+ Closes #414.
254
+
255
+ Closes #360.
256
+
257
+ **Tests:**
258
+
259
+ - `tests/test_integration.py` now gracefully skips the seven read-only
260
+ classes that rely on live-API probes (`TestReadOnlyAdGroups`,
261
+ `TestReadOnlyAds`, `TestReadOnlyKeywords`,
262
+ `TestReadOnlyDynamicFeedAdTargets`, `TestReadOnlyLeads`,
263
+ `TestReadOnlyBusinesses`, `TestReadOnlyAdVideos`) when the probe
264
+ raises — previously a temporary API outage crashed `setUpClass`
265
+ with an opaque traceback.
266
+ - `invoke_get` in `tests/test_integration.py` now passes the resolved
267
+ test credentials as explicit `--token`/`--login` flags so the
268
+ integration suite cannot silently fall through to a developer's
269
+ active `direct auth` profile (priority 1 in the CLI credential
270
+ chain wins over the profile, matching CLAUDE.md guidance).
271
+ - `tests/test_comprehensive.py` slimmed down: `TestCLIHelp` (full
272
+ duplicate of `tests/test_cli.py`) removed; the unique
273
+ `TestCommandsRegistered`, `TestUtils`, `TestOutputFormatters`,
274
+ `TestAuth`, and `TestErrorHandling` classes are kept.
275
+ - `tests/test_smoke_matrix.py` no longer hard-codes
276
+ `total_cli_subcommands == 144` or `wsdl_operations == 112`. Counts
277
+ are derived from the live Click registry and parsed WSDLs.
278
+ - `tests/test_sandbox_write_audit.py` no longer hard-codes
279
+ `total == 83`. The count derives from `commands_for_category`.
280
+
281
+ Closes #396.
282
+
3
283
  ## 0.3.13
4
284
 
5
285
  **Breaking changes:**
@@ -41,6 +41,8 @@ Click group-of-groups. Each Yandex Direct API resource = one file in `direct_cli
41
41
 
42
42
  **`--dry-run`:** `add`/`update` commands print request JSON without calling the API. Use as test seam.
43
43
 
44
+ **No legacy CLI flag aliases.** A CLI option must be exactly the kebab-case form of the WSDL request parameter (`SitelinkFieldNames` → `--sitelink-field-names`). If an existing flag uses a different name for the same WSDL parameter, rename it as a **breaking change** — do not keep the old form as a second `@click.option` name on the same variable. Document the rename in `CHANGELOG.md` under a `BREAKING CHANGES` heading and bump the version accordingly.
45
+
44
46
  **Runtime-deprecated methods:** WSDL-visible methods that Yandex rejects at runtime belong in `RUNTIME_DEPRECATED_METHODS` (`direct_cli/wsdl_coverage.py`) and must fail with `click.UsageError` before request construction. `agencyclients add` is blocked this way; use `agencyclients add-passport-organization`.
45
47
 
46
48
  **Strict WSDL parity:** `DRY_RUN_PAYLOAD_EXCLUSIONS` in `tests/api_coverage_payloads.py` must NOT contain any entry whose rationale claims the CLI surface is a «helper», «legacy», or «not part of strict WSDL parity». If the WSDL declares the operation, the CLI mirrors it 1:1 with a `PAYLOAD_CASES` fixture. Legitimate permanent exclusions are limited to:
@@ -72,6 +74,34 @@ Adding a new mutating command requires extending `COMMAND_WSDL_MAP` in `tests/te
72
74
 
73
75
  **Smoke probes:** Live ID discovery for safe-smoke and integration tests lives in `direct_cli/_smoke_probes.py`. Functions like `advideo_probe_id()` query the live API to find a real resource ID (env override `YANDEX_DIRECT_TEST_ADVIDEO_ID`, fallback through `creatives.get`) and return `None` on any failure — smoke scripts treat `None` as a benign skip, not a fatal error. CLI entry: `python3 -m direct_cli._smoke_probes advideo`.
74
76
 
77
+ **No URL literals outside the registry.** Every Yandex docs/API URL is declared once — either in `direct_cli/_vendor/tapi_yandex_direct/resource_mapping.py` (`docs`, `docs_pages.*`) or in `direct_cli/reports_coverage.py::REPORTS_SPEC_URLS`. Tests and scripts import these constants; never write the URL as a string literal anywhere else. Captcha-poisoning of the docs cache (#426) was possible only because the same URL was duplicated in a hard-coded test assertion. Don't repeat that.
78
+
79
+ **Docs/cache freshness guard.** `direct_cli.reports_coverage.fetch_reports_spec` and `direct_cli.wsdl_coverage.fetch_wsdl` both refuse responses that look like a Yandex SmartCaptcha gate (markers `showcaptcha`, `smartcaptcha`, `<title>Captcha`) or are suspiciously short (<30 KB for HTML, <3 KB for WSDL). The matching tests `test_reports_cache_files_are_real_content` and `test_wsdl_cache_files_are_real_content` enforce the same invariant on committed cache files.
80
+
81
+ ## PyPI Release
82
+
83
+ Release is two-phase on purpose. Yandex frequently rate-limits the docs host with a SmartCaptcha gateway, which is an external rate-limit on our IP — not evidence that an URL is gone. Mixing docs-health checks into the publish path made releases non-deterministic, so they live in a separate preflight script.
84
+
85
+ **Phase 1 — preflight (manual, network-dependent):**
86
+
87
+ ```
88
+ bash scripts/preflight_check.sh
89
+ ```
90
+
91
+ Runs three checks:
92
+
93
+ 1. `python scripts/check_all_docs_urls.py` — every Yandex docs URL in `RESOURCE_MAPPING_V5` and `REPORTS_SPEC_URLS` must be reachable. The probe falls back from `HEAD` to `GET` on 4xx (some CDNs reject `HEAD`), then verifies real content. **Hard fail:** confirmed 3xx-to-different-path or 4xx (canonical move / gone). **Soft warning (does not block):** 5xx or persistent captcha — treated as a transient Yandex rate-limit; re-run later to confirm.
94
+ 2. `pytest TestReportsCoverage TestWsdlCacheFreshness -v` — read-only check that the committed Reports/WSDL/XSD caches are real content (not captcha).
95
+ 3. `git diff --quiet -- tests/reports_cache tests/wsdl_cache` — refuses to proceed with an uncommitted cache refresh. If Yandex changed docs since the last snapshot, run `scripts/refresh_reports_cache.py` separately, review the diff, and commit it before releasing.
96
+
97
+ **Phase 2 — release (deterministic, no Yandex network calls):**
98
+
99
+ ```
100
+ bash scripts/release_pypi.sh all
101
+ ```
102
+
103
+ Builds dist artifacts, runs twine checks, uploads to TestPyPI + PyPI. Does **not** re-run docs/cache checks — if the captcha rate-limit was active during preflight, that should not block a release of an already-verified artifact.
104
+
75
105
  ## Tests
76
106
 
77
107
  - **Unit** (`test_cli.py`, `test_comprehensive.py`) — no API calls, no token needed.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: direct-cli
3
- Version: 0.3.13
3
+ Version: 0.3.15
4
4
  Summary: Command-line interface for Yandex Direct API
5
5
  Author: axisrow
6
6
  License: MIT
@@ -232,10 +232,10 @@ is omitted. Dry-run output masks the financial token.
232
232
  ```bash
233
233
  direct v4finance get-clients-units --logins client-login,other-client --format table
234
234
  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 --currency RUB --master-token MASTER_TOKEN --operation-num 124 --finance-login agency-login --dry-run
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
236
236
  direct v4finance check-payment --custom-transaction-id A123456789012345678901234567890B
237
- 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
238
- direct v4finance pay-campaigns --campaign-ids 123,456 --amount 100.50 --currency RUB --contract-id CONTRACT_ID --pay-method Bank --master-token MASTER_TOKEN --operation-num 123 --finance-login agency-login --dry-run
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
239
239
  ```
240
240
 
241
241
  ### V4 Live Shared Account
@@ -189,10 +189,10 @@ is omitted. Dry-run output masks the financial token.
189
189
  ```bash
190
190
  direct v4finance get-clients-units --logins client-login,other-client --format table
191
191
  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 --currency RUB --master-token MASTER_TOKEN --operation-num 124 --finance-login agency-login --dry-run
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
193
193
  direct v4finance check-payment --custom-transaction-id A123456789012345678901234567890B
194
- 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
195
- direct v4finance pay-campaigns --campaign-ids 123,456 --amount 100.50 --currency RUB --contract-id CONTRACT_ID --pay-method Bank --master-token MASTER_TOKEN --operation-num 123 --finance-login agency-login --dry-run
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
196
196
  ```
197
197
 
198
198
  ### V4 Live Shared Account
@@ -1718,11 +1718,6 @@ def build_text_campaign_search_strategy(
1718
1718
  # the caller has already placed PriorityGoals via the dedicated
1719
1719
  # ``PriorityGoalsUpdateSetting`` shape (with ``Operation: SET``) so
1720
1720
  # we only validate the strategy/subtype combination here.
1721
- #
1722
- # KNOWN ISSUE (#387): ``parse_priority_goals_spec`` forwards
1723
- # ``Value`` as a raw integer. Per Yandex docs ``Value`` is
1724
- # advertiser currency × 1,000,000; the cross-cutting fix is
1725
- # tracked in issue #387 and intentionally NOT bundled into #361.
1726
1721
  if priority_goals_items is not None:
1727
1722
  if subtype not in _TEXT_SEARCH_REQUIRES_PRIORITY_GOALS:
1728
1723
  raise click.UsageError(
@@ -78,12 +78,12 @@ RESOURCE_MAPPING_V5 = {
78
78
  },
79
79
  "dynamicads": {
80
80
  "resource": "json/v5/dynamictextadtargets",
81
- "docs": "https://yandex.ru/dev/direct/doc/ru/dynamictextadtargets/dynamictextadtargets",
81
+ "docs": "https://yandex.ru/dev/direct/doc/ru/dynamictextadtargets",
82
82
  "methods": ["get", "add", "delete", "suspend", "resume", "setBids"],
83
83
  },
84
84
  "dynamicfeedadtargets": {
85
85
  "resource": "json/v5/dynamicfeedadtargets",
86
- "docs": "https://yandex.ru/dev/direct/doc/ru/dynamicfeedadtargets/dynamicfeedadtargets",
86
+ "docs": "https://yandex.ru/dev/direct/doc/ru/dynamicfeedadtargets",
87
87
  "methods": ["get", "add", "delete", "suspend", "resume", "setBids"],
88
88
  },
89
89
  "keywordbids": {
@@ -118,7 +118,7 @@ RESOURCE_MAPPING_V5 = {
118
118
  },
119
119
  "vcards": {
120
120
  "resource": "json/v5/vcards",
121
- "docs": "https://yandex.ru/dev/direct/doc/ru/vcards/vcards",
121
+ "docs": "https://yandex.ru/dev/direct/doc/ru/vcards",
122
122
  "methods": ["get", "add", "delete"],
123
123
  },
124
124
  "turbopages": {
@@ -133,12 +133,12 @@ RESOURCE_MAPPING_V5 = {
133
133
  },
134
134
  "reports": {
135
135
  "resource": "json/v5/reports",
136
- "docs": "https://yandex.ru/dev/direct/doc/ru/reports/reports",
136
+ "docs": "https://yandex.ru/dev/direct/doc/ru/reports",
137
137
  "methods": ["get"],
138
138
  "docs_pages": {
139
- "type": "https://yandex.ru/dev/direct/doc/ru/reports/type",
140
- "period": "https://yandex.ru/dev/direct/doc/ru/reports/period",
141
- "fields-list": "https://yandex.ru/dev/direct/doc/ru/reports/fields-list",
139
+ "type": "https://yandex.ru/dev/direct/doc/ru/type",
140
+ "period": "https://yandex.ru/dev/direct/doc/ru/period",
141
+ "fields-list": "https://yandex.ru/dev/direct/doc/ru/fields-list",
142
142
  "headers": "https://yandex.ru/dev/direct/doc/ru/headers",
143
143
  },
144
144
  },
@@ -154,7 +154,7 @@ RESOURCE_MAPPING_V5 = {
154
154
  },
155
155
  "smartadtargets": {
156
156
  "resource": "json/v5/smartadtargets",
157
- "docs": "https://yandex.ru/dev/direct/doc/ru/smartadtargets/smartadtargets",
157
+ "docs": "https://yandex.ru/dev/direct/doc/ru/smartadtargets",
158
158
  "methods": ["get", "add", "update", "delete", "suspend", "resume", "setBids"],
159
159
  },
160
160
  "strategies": {
@@ -473,6 +473,77 @@ def _post_adgroups(client: Any, body: dict[str, Any]) -> Any:
473
473
  @click.option("--format", "output_format", default="json", help="Output format")
474
474
  @click.option("--output", help="Output file")
475
475
  @click.option("--fields", help="Comma-separated field names")
476
+ @click.option(
477
+ "--autotargeting-settings-brand-options-field-names",
478
+ help=(
479
+ "Comma-separated AutotargetingSettingsBrandOptionsFieldNames "
480
+ "(e.g. WithoutBrands,WithAdvertiserBrand,WithCompetitorsBrand). "
481
+ "Sent as separate top-level request parameter per the "
482
+ "AdGroupsGetRequest WSDL."
483
+ ),
484
+ )
485
+ @click.option(
486
+ "--autotargeting-settings-categories-field-names",
487
+ help=(
488
+ "Comma-separated AutotargetingSettingsCategoriesFieldNames "
489
+ "(e.g. Exact,Narrow,Alternative,Accessory,Broader). "
490
+ "Sent as separate top-level request parameter per the "
491
+ "AdGroupsGetRequest WSDL."
492
+ ),
493
+ )
494
+ @click.option(
495
+ "--dynamic-text-ad-group-field-names",
496
+ help=(
497
+ "Comma-separated DynamicTextAdGroupFieldNames "
498
+ "(e.g. AutotargetingSettings,DomainUrl). "
499
+ "Sent as separate top-level request parameter per the "
500
+ "AdGroupsGetRequest WSDL."
501
+ ),
502
+ )
503
+ @click.option(
504
+ "--dynamic-text-feed-ad-group-field-names",
505
+ help=(
506
+ "Comma-separated DynamicTextFeedAdGroupFieldNames "
507
+ "(e.g. Source,FeedId,SourceType). "
508
+ "Sent as separate top-level request parameter per the "
509
+ "AdGroupsGetRequest WSDL."
510
+ ),
511
+ )
512
+ @click.option(
513
+ "--mobile-app-ad-group-field-names",
514
+ help=(
515
+ "Comma-separated MobileAppAdGroupFieldNames "
516
+ "(e.g. StoreUrl,TargetDeviceType,AppOperatingSystemType). "
517
+ "Sent as separate top-level request parameter per the "
518
+ "AdGroupsGetRequest WSDL."
519
+ ),
520
+ )
521
+ @click.option(
522
+ "--smart-ad-group-field-names",
523
+ help=(
524
+ "Comma-separated SmartAdGroupFieldNames "
525
+ "(e.g. FeedId,AdTitleSource,AdBodySource). "
526
+ "Sent as separate top-level request parameter per the "
527
+ "AdGroupsGetRequest WSDL."
528
+ ),
529
+ )
530
+ @click.option(
531
+ "--text-ad-group-feed-params-field-names",
532
+ help=(
533
+ "Comma-separated TextAdGroupFeedParamsFieldNames "
534
+ "(e.g. FeedId,FeedCategoryIds). "
535
+ "Sent as separate top-level request parameter per the "
536
+ "AdGroupsGetRequest WSDL."
537
+ ),
538
+ )
539
+ @click.option(
540
+ "--unified-ad-group-field-names",
541
+ help=(
542
+ "Comma-separated UnifiedAdGroupFieldNames (e.g. OfferRetargeting). "
543
+ "Sent as separate top-level request parameter per the "
544
+ "AdGroupsGetRequest WSDL."
545
+ ),
546
+ )
476
547
  @click.option("--dry-run", is_flag=True, help="Show request without sending")
477
548
  @click.pass_context
478
549
  def get(
@@ -492,6 +563,14 @@ def get(
492
563
  output_format,
493
564
  output,
494
565
  fields,
566
+ autotargeting_settings_brand_options_field_names,
567
+ autotargeting_settings_categories_field_names,
568
+ dynamic_text_ad_group_field_names,
569
+ dynamic_text_feed_ad_group_field_names,
570
+ mobile_app_ad_group_field_names,
571
+ smart_ad_group_field_names,
572
+ text_ad_group_feed_params_field_names,
573
+ unified_ad_group_field_names,
495
574
  dry_run,
496
575
  ):
497
576
  """Get ad groups"""
@@ -507,6 +586,41 @@ def get(
507
586
 
508
587
  field_names = fields.split(",") if fields else get_default_fields("adgroups")
509
588
 
589
+ raw_nested = (
590
+ (
591
+ "AutotargetingSettingsBrandOptionsFieldNames",
592
+ autotargeting_settings_brand_options_field_names,
593
+ ),
594
+ (
595
+ "AutotargetingSettingsCategoriesFieldNames",
596
+ autotargeting_settings_categories_field_names,
597
+ ),
598
+ (
599
+ "DynamicTextAdGroupFieldNames",
600
+ dynamic_text_ad_group_field_names,
601
+ ),
602
+ (
603
+ "DynamicTextFeedAdGroupFieldNames",
604
+ dynamic_text_feed_ad_group_field_names,
605
+ ),
606
+ ("MobileAppAdGroupFieldNames", mobile_app_ad_group_field_names),
607
+ ("SmartAdGroupFieldNames", smart_ad_group_field_names),
608
+ (
609
+ "TextAdGroupFeedParamsFieldNames",
610
+ text_ad_group_feed_params_field_names,
611
+ ),
612
+ ("UnifiedAdGroupFieldNames", unified_ad_group_field_names),
613
+ )
614
+ parsed_nested = {}
615
+ for wsdl_key, raw_value in raw_nested:
616
+ parsed = parse_csv_strings(raw_value)
617
+ if raw_value is not None and not parsed:
618
+ raise click.UsageError(
619
+ f"Provide a non-empty comma-separated {wsdl_key} list."
620
+ )
621
+ if parsed:
622
+ parsed_nested[wsdl_key] = parsed
623
+
510
624
  criteria = {}
511
625
  if ids:
512
626
  criteria["Ids"] = parse_ids(ids)
@@ -529,6 +643,7 @@ def get(
529
643
  )
530
644
 
531
645
  params = {"SelectionCriteria": criteria, "FieldNames": field_names}
646
+ params.update(parsed_nested)
532
647
 
533
648
  if limit:
534
649
  params["Page"] = {"Limit": limit}
@@ -550,6 +665,8 @@ def get(
550
665
  data = result().extract()
551
666
  format_output(data, output_format, output)
552
667
 
668
+ except click.UsageError:
669
+ raise
553
670
  except Exception as e:
554
671
  print_error(str(e))
555
672
  raise click.Abort()