direct-cli 0.3.12__tar.gz → 0.3.14__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 (241) hide show
  1. {direct_cli-0.3.12 → direct_cli-0.3.14}/AGENTS.md +6 -6
  2. {direct_cli-0.3.12 → direct_cli-0.3.14}/CHANGELOG.md +268 -0
  3. {direct_cli-0.3.12 → direct_cli-0.3.14}/CLAUDE.md +30 -0
  4. {direct_cli-0.3.12 → direct_cli-0.3.14}/PKG-INFO +50 -41
  5. {direct_cli-0.3.12 → direct_cli-0.3.14}/README.md +49 -40
  6. direct_cli-0.3.14/direct_cli/_bidding_strategy.py +5553 -0
  7. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/_vendor/tapi_yandex_direct/resource_mapping.py +8 -8
  8. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/adgroups.py +117 -0
  9. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/ads.py +206 -50
  10. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/agencyclients.py +85 -1
  11. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/bidmodifiers.py +153 -1
  12. direct_cli-0.3.14/direct_cli/commands/campaigns.py +7547 -0
  13. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/changes.py +11 -5
  14. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/clients.py +85 -1
  15. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/creatives.py +71 -2
  16. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/feeds.py +47 -2
  17. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/keywordbids.py +61 -3
  18. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/keywords.py +59 -1
  19. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/sitelinks.py +37 -4
  20. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/strategies.py +256 -10
  21. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/reports_coverage.py +23 -1
  22. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/utils.py +53 -7
  23. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/v4/money.py +1 -1
  24. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/wsdl_coverage.py +57 -1
  25. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli.egg-info/PKG-INFO +50 -41
  26. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli.egg-info/SOURCES.txt +2 -0
  27. {direct_cli-0.3.12 → direct_cli-0.3.14}/pyproject.toml +1 -1
  28. {direct_cli-0.3.12 → direct_cli-0.3.14}/scripts/build_api_coverage_report.py +1 -1
  29. direct_cli-0.3.14/scripts/check_all_docs_urls.py +166 -0
  30. {direct_cli-0.3.12 → direct_cli-0.3.14}/scripts/release_pypi.sh +8 -0
  31. {direct_cli-0.3.12 → direct_cli-0.3.14}/scripts/test_safe_commands.sh +1 -1
  32. direct_cli-0.3.14/tests/WSDL_OPTIONAL_FIELD_AUDIT.md +3292 -0
  33. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/api_coverage_payloads.py +332 -1
  34. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/reports_cache/raw/fields-list.html +6 -6
  35. direct_cli-0.3.14/tests/reports_cache/raw/headers.html +88 -0
  36. direct_cli-0.3.14/tests/reports_cache/raw/period.html +119 -0
  37. direct_cli-0.3.14/tests/reports_cache/raw/spec.html +643 -0
  38. direct_cli-0.3.14/tests/reports_cache/raw/type.html +279 -0
  39. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/reports_cache/spec.json +1 -1
  40. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_api_coverage.py +191 -19
  41. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_changes.py +29 -2
  42. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_cli.py +3 -4
  43. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_cli_contract.py +6 -1
  44. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_comprehensive.py +7 -20
  45. direct_cli-0.3.14/tests/test_dry_run.py +22918 -0
  46. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_integration.py +85 -10
  47. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_low_coverage_payloads.py +18 -6
  48. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_sandbox_write_audit.py +7 -3
  49. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_smoke_matrix.py +49 -7
  50. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_wsdl_parity_gate.py +2598 -91
  51. direct_cli-0.3.12/direct_cli/commands/campaigns.py +0 -2678
  52. direct_cli-0.3.12/tests/WSDL_OPTIONAL_FIELD_AUDIT.md +0 -5239
  53. direct_cli-0.3.12/tests/reports_cache/raw/headers.html +0 -88
  54. direct_cli-0.3.12/tests/reports_cache/raw/period.html +0 -119
  55. direct_cli-0.3.12/tests/reports_cache/raw/spec.html +0 -660
  56. direct_cli-0.3.12/tests/reports_cache/raw/type.html +0 -279
  57. direct_cli-0.3.12/tests/test_dry_run.py +0 -11240
  58. {direct_cli-0.3.12 → direct_cli-0.3.14}/.env.example +0 -0
  59. {direct_cli-0.3.12 → direct_cli-0.3.14}/.github/copilot-instructions.md +0 -0
  60. {direct_cli-0.3.12 → direct_cli-0.3.14}/.github/workflows/api-coverage.yml +0 -0
  61. {direct_cli-0.3.12 → direct_cli-0.3.14}/.github/workflows/claude.yml +0 -0
  62. {direct_cli-0.3.12 → direct_cli-0.3.14}/.github/workflows/quality.yml +0 -0
  63. {direct_cli-0.3.12 → direct_cli-0.3.14}/.gitignore +0 -0
  64. {direct_cli-0.3.12 → direct_cli-0.3.14}/MANIFEST.in +0 -0
  65. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/__init__.py +0 -0
  66. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/_deprecated.py +0 -0
  67. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/_smoke_probes.py +0 -0
  68. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/_vendor/__init__.py +0 -0
  69. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/_vendor/tapi_yandex_direct/__init__.py +0 -0
  70. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/_vendor/tapi_yandex_direct/endpoints.py +0 -0
  71. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/_vendor/tapi_yandex_direct/exceptions.py +0 -0
  72. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.py +0 -0
  73. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.pyi +0 -0
  74. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/_vendor/tapi_yandex_direct/v4/__init__.py +0 -0
  75. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.py +0 -0
  76. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.pyi +0 -0
  77. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/_vendor/tapi_yandex_direct/v4/resource_mapping.py +0 -0
  78. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/api.py +0 -0
  79. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/auth.py +0 -0
  80. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/cli.py +0 -0
  81. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/__init__.py +0 -0
  82. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/adextensions.py +0 -0
  83. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/adimages.py +0 -0
  84. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/advideos.py +0 -0
  85. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/audiencetargets.py +0 -0
  86. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/auth.py +0 -0
  87. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/balance.py +0 -0
  88. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/bids.py +0 -0
  89. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/businesses.py +0 -0
  90. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/dictionaries.py +0 -0
  91. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/dynamicads.py +0 -0
  92. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/dynamicfeedadtargets.py +0 -0
  93. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/keywordsresearch.py +0 -0
  94. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/leads.py +0 -0
  95. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/negativekeywordsharedsets.py +0 -0
  96. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/reports.py +0 -0
  97. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/retargeting.py +0 -0
  98. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/smartadtargets.py +0 -0
  99. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/turbopages.py +0 -0
  100. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/v4account.py +0 -0
  101. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/v4events.py +0 -0
  102. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/v4finance.py +0 -0
  103. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/v4forecast.py +0 -0
  104. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/v4goals.py +0 -0
  105. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/v4shells.py +0 -0
  106. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/v4tags.py +0 -0
  107. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/v4wordstat.py +0 -0
  108. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/commands/vcards.py +0 -0
  109. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/output.py +0 -0
  110. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/smoke_matrix.py +0 -0
  111. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/v4/__init__.py +0 -0
  112. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli/v4_contracts.py +0 -0
  113. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli.egg-info/dependency_links.txt +0 -0
  114. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli.egg-info/entry_points.txt +0 -0
  115. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli.egg-info/requires.txt +0 -0
  116. {direct_cli-0.3.12 → direct_cli-0.3.14}/direct_cli.egg-info/top_level.txt +0 -0
  117. {direct_cli-0.3.12 → direct_cli-0.3.14}/docs/audits/issue-198-mutating-wsdl-audit.md +0 -0
  118. {direct_cli-0.3.12 → direct_cli-0.3.14}/docs/superpowers/plans/2026-04-12-issue-32-completion.md +0 -0
  119. {direct_cli-0.3.12 → direct_cli-0.3.14}/docs/superpowers/specs/2026-04-23-vendor-tapi-yandex-direct-design.md +0 -0
  120. {direct_cli-0.3.12 → direct_cli-0.3.14}/scripts/anonymize_cassettes.py +0 -0
  121. {direct_cli-0.3.12 → direct_cli-0.3.14}/scripts/build_api_coverage_checklist.py +0 -0
  122. {direct_cli-0.3.12 → direct_cli-0.3.14}/scripts/build_wsdl_optional_field_audit.py +0 -0
  123. {direct_cli-0.3.12 → direct_cli-0.3.14}/scripts/check_reports_drift.py +0 -0
  124. {direct_cli-0.3.12 → direct_cli-0.3.14}/scripts/check_wsdl_drift.py +0 -0
  125. {direct_cli-0.3.12 → direct_cli-0.3.14}/scripts/patch_vendor_imports.py +0 -0
  126. {direct_cli-0.3.12 → direct_cli-0.3.14}/scripts/refresh_reports_cache.py +0 -0
  127. {direct_cli-0.3.12 → direct_cli-0.3.14}/scripts/refresh_wsdl_cache.py +0 -0
  128. {direct_cli-0.3.12 → direct_cli-0.3.14}/scripts/sandbox_write_audit.py +0 -0
  129. {direct_cli-0.3.12 → direct_cli-0.3.14}/scripts/sandbox_write_live.py +0 -0
  130. {direct_cli-0.3.12 → direct_cli-0.3.14}/scripts/test_dangerous_commands.sh +0 -0
  131. {direct_cli-0.3.12 → direct_cli-0.3.14}/scripts/test_sandbox_write.sh +0 -0
  132. {direct_cli-0.3.12 → direct_cli-0.3.14}/scripts/update_vendor.sh +0 -0
  133. {direct_cli-0.3.12 → direct_cli-0.3.14}/setup.cfg +0 -0
  134. {direct_cli-0.3.12 → direct_cli-0.3.14}/setup.py +0 -0
  135. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/API_COVERAGE.md +0 -0
  136. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/API_ISSUE_AUDIT.md +0 -0
  137. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/MANUAL_COVERAGE.md +0 -0
  138. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/__init__.py +0 -0
  139. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/_orphan_store.py +0 -0
  140. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteAdExtensions.test_add_delete.yaml +0 -0
  141. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteAdGroups.test_add_update_delete.yaml +0 -0
  142. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteAdImages.test_add_delete.yaml +0 -0
  143. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteAds.test_add_text_ad_update_delete.yaml +0 -0
  144. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteAudienceTargets.test_add_delete.yaml +0 -0
  145. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteBidModifiersAdd.test_add_delete_mobile.yaml +0 -0
  146. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteBidModifiersSet.test_set_without_id_is_rejected.yaml +0 -0
  147. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteBids.test_set_bid.yaml +0 -0
  148. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteBidsRead.test_bids_get.yaml +0 -0
  149. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteBidsRead.test_bids_set_auto.yaml +0 -0
  150. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteCampaignDraftLifecycle.test_draft_create_get_delete.yaml +0 -0
  151. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteCampaigns.test_campaign_lifecycle.yaml +0 -0
  152. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteDynamicAds.test_add_update_delete.yaml +0 -0
  153. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteFeeds.test_add_update_delete.yaml +0 -0
  154. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteKeywordBids.test_set_keyword_bid.yaml +0 -0
  155. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteKeywords.test_add_update_delete.yaml +0 -0
  156. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteNegativeKeywordSharedSets.test_add_update_delete.yaml +0 -0
  157. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteRetargeting.test_add_delete.yaml +0 -0
  158. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteRetargetingUpdate.test_retargeting_update.yaml +0 -0
  159. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteSitelinks.test_add_delete.yaml +0 -0
  160. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteSmartAdTargets.test_add_update_delete.yaml +0 -0
  161. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteStrategies.test_strategies_lifecycle.yaml +0 -0
  162. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_integration_write/TestWriteVCards.test_add_delete.yaml +0 -0
  163. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_v5_live_write/test_v5_live_draft_adgroups_add_update_delete.yaml +0 -0
  164. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_v5_live_write/test_v5_live_draft_adimages_add_get_delete.yaml +0 -0
  165. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_v5_live_write/test_v5_live_draft_ads_add_update_delete.yaml +0 -0
  166. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_v5_live_write/test_v5_live_draft_ads_suspend_resume_archive_unarchive.yaml +0 -0
  167. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_v5_live_write/test_v5_live_draft_advideos_add_get.yaml +0 -0
  168. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_add_delete.yaml +0 -0
  169. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_suspend_resume.yaml +0 -0
  170. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_v5_live_write/test_v5_live_draft_bids_set.yaml +0 -0
  171. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_v5_live_write/test_v5_live_draft_campaign_create_get_delete.yaml +0 -0
  172. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_v5_live_write/test_v5_live_draft_creatives_chain_advideo_to_creative.yaml +0 -0
  173. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_v5_live_write/test_v5_live_draft_dynamicads_add_delete.yaml +0 -0
  174. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_v5_live_write/test_v5_live_draft_dynamicads_suspend_resume.yaml +0 -0
  175. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywordbids_set.yaml +0 -0
  176. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywords_add_update_delete.yaml +0 -0
  177. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywords_suspend_resume.yaml +0 -0
  178. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_v5_live_write/test_v5_live_draft_sitelinks_add_get_delete.yaml +0 -0
  179. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_v5_live_write/test_v5_live_draft_smartadtargets_add_update_delete.yaml +0 -0
  180. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/cassettes/test_v5_live_write/test_v5_live_draft_smartadtargets_suspend_resume.yaml +0 -0
  181. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/conftest.py +0 -0
  182. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/fixtures/test-video.mp4 +0 -0
  183. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_auth_bw.py +0 -0
  184. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_auth_oauth.py +0 -0
  185. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_auth_op.py +0 -0
  186. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_auth_write_json.py +0 -0
  187. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_balance.py +0 -0
  188. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_env_loading.py +0 -0
  189. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_integration_write.py +0 -0
  190. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_reports_drift.py +0 -0
  191. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_reports_parsing.py +0 -0
  192. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_transport_contract.py +0 -0
  193. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_unknown_option_hints.py +0 -0
  194. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_v4_contracts.py +0 -0
  195. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_v4_exit_codes.py +0 -0
  196. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_v4_foundation.py +0 -0
  197. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_v4_live_contracts.py +0 -0
  198. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_v4_runtime_shape.py +0 -0
  199. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_v4_safety.py +0 -0
  200. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_v4account.py +0 -0
  201. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_v4events.py +0 -0
  202. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_v4finance_money.py +0 -0
  203. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_v4finance_read.py +0 -0
  204. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_v4forecast.py +0 -0
  205. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_v4goals.py +0 -0
  206. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_v4tags.py +0 -0
  207. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_v4wordstat.py +0 -0
  208. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_v5_live_write.py +0 -0
  209. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/test_vendor_imports.py +0 -0
  210. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/adextensions.xml +0 -0
  211. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/adgroups.xml +0 -0
  212. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/adimages.xml +0 -0
  213. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/ads.xml +0 -0
  214. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/advideos.xml +0 -0
  215. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/agencyclients.xml +0 -0
  216. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/audiencetargets.xml +0 -0
  217. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/bidmodifiers.xml +0 -0
  218. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/bids.xml +0 -0
  219. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/businesses.xml +0 -0
  220. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/campaigns.xml +0 -0
  221. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/changes.xml +0 -0
  222. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/clients.xml +0 -0
  223. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/creatives.xml +0 -0
  224. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/dictionaries.xml +0 -0
  225. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/dynamicfeedadtargets.xml +0 -0
  226. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/dynamictextadtargets.xml +0 -0
  227. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/feeds.xml +0 -0
  228. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/imports/adextensiontypes.xsd +0 -0
  229. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/imports/general.xsd +0 -0
  230. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/imports/generalclients.xsd +0 -0
  231. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/keywordbids.xml +0 -0
  232. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/keywords.xml +0 -0
  233. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/keywordsresearch.xml +0 -0
  234. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/leads.xml +0 -0
  235. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/negativekeywordsharedsets.xml +0 -0
  236. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/retargetinglists.xml +0 -0
  237. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/sitelinks.xml +0 -0
  238. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/smartadtargets.xml +0 -0
  239. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/strategies.xml +0 -0
  240. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/turbopages.xml +0 -0
  241. {direct_cli-0.3.12 → direct_cli-0.3.14}/tests/wsdl_cache/vcards.xml +0 -0
@@ -73,21 +73,22 @@ direct dictionaries get-geo-regions \
73
73
 
74
74
  ### Datetime Rules
75
75
 
76
- - Datetime parameters must be passed in the format `YYYY-MM-DDTHH:MM:SS`.
76
+ - Changes timestamps must match Yandex Direct API format `YYYY-MM-DDTHH:MM:SSZ`.
77
+ - Other datetime parameters use their method-specific documented format.
77
78
  - Datetime values must be passed as a single shell token.
78
- - Canonical examples must not use timezone suffixes like `Z`.
79
+ - Canonical `changes` examples must use the `Z` suffix.
79
80
  - Canonical examples must not use quoted space-separated datetime values.
80
81
 
81
82
  Use:
82
83
 
83
84
  ```bash
84
- direct changes check-campaigns --timestamp 2026-04-14T00:00:00
85
+ direct changes check-campaigns --timestamp 2026-04-14T00:00:00Z
85
86
  ```
86
87
 
87
88
  Do not use:
88
89
 
89
90
  ```bash
90
- direct changes check-campaigns --timestamp 2026-04-14T00:00:00Z
91
+ direct changes check-campaigns --timestamp 2026-04-14T00:00:00
91
92
  direct changes check-campaigns --timestamp "2026-04-14 00:00:00"
92
93
  ```
93
94
 
@@ -104,7 +105,7 @@ Valid canonical examples:
104
105
 
105
106
  ```bash
106
107
  direct campaigns get --ids 1,2,3
107
- direct changes check-campaigns --timestamp 2026-04-14T00:00:00
108
+ direct changes check-campaigns --timestamp 2026-04-14T00:00:00Z
108
109
  direct keywordsresearch has-search-volume --keywords "buy laptop,buy desktop"
109
110
  direct smartadtargets update --id 456 --priority HIGH
110
111
  direct dynamicads set-bids --id 789 --bid 12.5
@@ -119,7 +120,6 @@ direct dynamicads set-bids --id 789 --bid 12.5 --json '{"StrategyPriority":"HIGH
119
120
  direct dictionaries get-geo-regions \
120
121
  --region-ids 225 \
121
122
  --fields GeoRegionId,GeoRegionName
122
- direct changes check-campaigns --timestamp 2026-04-14T00:00:00Z
123
123
  direct changes check-campaigns --timestamp "2026-04-14 00:00:00"
124
124
  ```
125
125
 
@@ -1,5 +1,273 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.14
4
+
5
+ **Fixed:**
6
+
7
+ - Reports drift checker now points at the canonical Yandex docs URLs
8
+ (`/ru/type`, `/ru/period`, `/ru/fields-list`, `/ru/spec`) after Yandex
9
+ retired the `/ru/reports/<page>` path layout and renamed `spec.html`
10
+ to `spec`. The pre-existing `tests/reports_cache/raw/` had silently
11
+ been captcha-poisoned for three of those pages (~14.6 KB Yandex
12
+ SmartCaptcha gateway in place of real docs); cache is now refetched
13
+ from the live canonical URLs and `spec.json` is byte-equivalent to
14
+ the pre-migration snapshot except for one updated description string.
15
+ - Five `RESOURCE_MAPPING_V5[*]["docs"]` URLs that Yandex moved from the
16
+ legacy `…/ru/<group>/<group>` to `…/ru/<group>` single-segment form
17
+ (`dynamictextadtargets`, `dynamicfeedadtargets`, `reports`,
18
+ `smartadtargets`, `vcards`). Closes #426.
19
+
20
+ **Added (drift protection):**
21
+
22
+ - `direct_cli/reports_coverage.py::fetch_reports_spec` and
23
+ `direct_cli/wsdl_coverage.py::fetch_wsdl` / `fetch_live_wsdl` now
24
+ refuse responses that look like a Yandex SmartCaptcha gateway (markers
25
+ `showcaptcha`, `smartcaptcha`, `<title>Captcha`) or are suspiciously
26
+ short. This prevents silently poisoning the docs/WSDL cache with
27
+ rate-limited captcha HTML.
28
+ - `tests/test_api_coverage.py::TestReportsCoverage::test_reports_cache_files_are_real_content`
29
+ and `TestWsdlCacheFreshness::test_wsdl_cache_files_are_real_content`
30
+ guard the committed cache files against the same poisoning.
31
+ - `scripts/check_all_docs_urls.py` — health-checks every URL in
32
+ `RESOURCE_MAPPING_V5` and `REPORTS_SPEC_URLS`. Hard-fails on
33
+ redirect-to-captcha, canonical move (`Location` with a different path
34
+ segment), 4xx, or captcha body; soft-warns on 5xx; paces requests to
35
+ avoid Yandex rate-limit. Wired into `scripts/release_pypi.sh` as a
36
+ mandatory pre-release gate together with `refresh_reports_cache.py`
37
+ and a focused pytest pass.
38
+
39
+ **Contract** (`CLAUDE.md`):
40
+
41
+ - New rule "No URL literals outside the registry" — every Yandex
42
+ docs/API URL is declared once in `RESOURCE_MAPPING_V5` or
43
+ `REPORTS_SPEC_URLS`; importers reference the constant.
44
+ - New rule "Docs/cache freshness guard" — fetchers and cache files
45
+ enforce minimum-size and no-captcha invariants.
46
+ - New section "PyPI Release" — documents the three pre-release health
47
+ checks executed by `release_pypi.sh`.
48
+
49
+ **Breaking changes:**
50
+
51
+ - `direct ads get` flag `--text-ad-fields` is **renamed** to the
52
+ WSDL-canonical `--text-ad-field-names` form matching the
53
+ `TextAdFieldNames` request parameter declared by `AdsGetRequest`.
54
+ The old `--text-ad-fields` form is no longer accepted — update
55
+ scripts and automation accordingly. Closes #406.
56
+ - `direct campaigns add` / `direct campaigns update` and `direct
57
+ strategies add` / `direct strategies update` now reject `--priority-goals`
58
+ / `--priority-goal` values below 100,000 (0.1 unit in micro-currency).
59
+ Per Yandex Direct API (add-text-campaign, strategies-types),
60
+ `PriorityGoalsItem.Value` is `xsd:long` in advertiser currency
61
+ multiplied by 1,000,000 — the same contract as `--budget`,
62
+ `--average-cpa`, and other money flags after #399/#400. The error
63
+ message suggests the micro-currency conversion (e.g. `Did you mean
64
+ 500000000?`). Negative values are also rejected up-front rather than
65
+ reaching the API. Both parsers share a single
66
+ `validate_priority_goal_value` helper. Closes #387.
67
+
68
+ **Added:**
69
+
70
+ - `direct sitelinks get` now exposes `--sitelink-field-names` for the
71
+ separate WSDL `SitelinkFieldNames` request parameter
72
+ (`SitelinkFieldEnum`: `Title`, `Href`, `Description`, `TurboPageId`).
73
+ Previously only the top-level `--fields` (mapping to `FieldNames`)
74
+ was available, so the nested `Sitelinks[]` projection could not be
75
+ controlled from CLI.
76
+ - `direct keywordbids get` now exposes `--fields`,
77
+ `--search-field-names`, and `--network-field-names` for the
78
+ separate `FieldNames`, `SearchFieldNames`, and `NetworkFieldNames`
79
+ request parameters declared by `KeywordBidsGetRequest`. Defaults
80
+ from `COMMON_FIELDS` are preserved when flags are absent.
81
+ - Regression test `test_every_nested_fieldnames_param_has_cli_option`
82
+ (`tests/test_api_coverage.py`) scans every cached WSDL `get`
83
+ request type for `*FieldNames` parameters and verifies that each
84
+ one has a matching kebab-case CLI option. Acknowledged remaining
85
+ gaps are tracked in `NESTED_FIELDNAMES_EXCLUSIONS` and #402 so
86
+ future additions cannot silently slip in.
87
+ - `direct feeds get` now exposes `--file-feed-field-names` and
88
+ `--url-feed-field-names` for the separate WSDL `FileFeedFieldNames`
89
+ (`FileFeedFieldEnum`: `Filename`) and `UrlFeedFieldNames`
90
+ (`UrlFeedFieldEnum`: `Login`, `Url`, `RemoveUtmTags`) request
91
+ parameters declared by `FeedsGetRequest`. Previously only the
92
+ top-level `--fields` (mapping to `FieldNames`) was available, so
93
+ the nested `FileFeed` / `UrlFeed` projections could not be
94
+ controlled from CLI. Closes #412.
95
+ - `direct keywords get` now exposes
96
+ `--autotargeting-settings-brand-options-field-names`
97
+ (`AutotargetingBrandOptionsFieldEnum`: `WithoutBrands`,
98
+ `WithAdvertiserBrand`, `WithCompetitorsBrand`) and
99
+ `--autotargeting-settings-categories-field-names`
100
+ (`AutotargetingCategoriesFieldEnum`: `Exact`, `Narrow`,
101
+ `Alternative`, `Accessory`, `Broader`) for the separate WSDL
102
+ `*FieldNames` request parameters declared by
103
+ `KeywordsGetRequest`. Previously only the top-level `--fields`
104
+ (mapping to `FieldNames`) was available, so the nested
105
+ `AutotargetingSettings.BrandOptions` / `Categories` projections
106
+ could not be controlled from CLI. Closes #413.
107
+ - `direct creatives get` now exposes
108
+ `--cpc-video-creative-field-names`,
109
+ `--cpm-video-creative-field-names`,
110
+ `--smart-creative-field-names`, and
111
+ `--video-extension-creative-field-names` for the four nested
112
+ WSDL `*FieldNames` request parameters declared by
113
+ `CreativesGetRequest` (`CpcVideoCreativeFieldEnum`,
114
+ `CpmVideoCreativeFieldEnum`, `SmartCreativeFieldEnum`,
115
+ `VideoExtensionCreativeFieldEnum`). Previously only the top-level
116
+ `--fields` (mapping to `FieldNames`) was available, so the
117
+ per-subtype projections could not be controlled from CLI.
118
+ Closes #411.
119
+ - `direct clients get` now exposes `--contract-field-names`,
120
+ `--contragent-field-names`, `--contragent-tin-info-field-names`,
121
+ `--organization-field-names`, and `--tin-info-field-names` for
122
+ the five nested WSDL `*FieldNames` request parameters declared
123
+ by `ClientsGetRequest` (`ContractInfoFieldEnum`,
124
+ `ContragentInfoFieldEnum`, `TinInfoFieldEnum`,
125
+ `OrgInfoFieldEnum`, `TinInfoFieldEnum`). The command also gains
126
+ `--dry-run` for parity with other read-path commands.
127
+ Previously only the top-level `--fields` (mapping to `FieldNames`)
128
+ was available, so the per-subtype ERIR projections could not be
129
+ controlled from CLI. Closes #410.
130
+ - `direct agencyclients get` now exposes `--contract-field-names`,
131
+ `--contragent-field-names`, `--contragent-tin-info-field-names`,
132
+ `--organization-field-names`, and `--tin-info-field-names` for
133
+ the five nested WSDL `*FieldNames` request parameters declared
134
+ by `AgencyClientsGetRequest` (`ContractInfoFieldEnum`,
135
+ `ContragentInfoFieldEnum`, `TinInfoFieldEnum`,
136
+ `OrgInfoFieldEnum`, `TinInfoFieldEnum`). The command also gains
137
+ `--dry-run` for parity with other read-path commands.
138
+ Previously only the top-level `--fields` (mapping to `FieldNames`)
139
+ was available, so the per-subtype ERIR projections could not be
140
+ controlled from CLI. Closes #407.
141
+ - `direct adgroups get` now exposes eight additional
142
+ `--*-field-names` flags for the separate WSDL `*FieldNames`
143
+ request parameters declared by `AdGroupsGetRequest`:
144
+ `--autotargeting-settings-brand-options-field-names`,
145
+ `--autotargeting-settings-categories-field-names`,
146
+ `--dynamic-text-ad-group-field-names`,
147
+ `--dynamic-text-feed-ad-group-field-names`,
148
+ `--mobile-app-ad-group-field-names`,
149
+ `--smart-ad-group-field-names`,
150
+ `--text-ad-group-feed-params-field-names`, and
151
+ `--unified-ad-group-field-names`. Previously only the top-level
152
+ `--fields` (mapping to `FieldNames`) was available, so the
153
+ per-subtype ad-group projections could not be controlled from
154
+ CLI. Closes #405.
155
+ - `direct ads get` now exposes sixteen additional `--*-field-names`
156
+ flags for the separate WSDL `*FieldNames` request parameters
157
+ declared by `AdsGetRequest`: `--cpc-video-ad-builder-ad-field-names`,
158
+ `--cpm-banner-ad-builder-ad-field-names`,
159
+ `--cpm-video-ad-builder-ad-field-names`,
160
+ `--dynamic-text-ad-field-names`, `--listing-ad-field-names`,
161
+ `--mobile-app-ad-builder-ad-field-names`,
162
+ `--mobile-app-ad-field-names`,
163
+ `--mobile-app-cpc-video-ad-builder-ad-field-names`,
164
+ `--mobile-app-image-ad-field-names`,
165
+ `--responsive-ad-field-names`, `--shopping-ad-field-names`,
166
+ `--smart-ad-builder-ad-field-names`,
167
+ `--text-ad-builder-ad-field-names`,
168
+ `--text-ad-field-names`,
169
+ `--text-ad-price-extension-field-names`, and
170
+ `--text-image-ad-field-names`. Previously only the top-level
171
+ `--fields` (mapping to `FieldNames`) and non-canonical
172
+ `--text-ad-fields` were available, so the per-ad-subtype projections
173
+ could not be controlled from CLI. Closes #406.
174
+
175
+ **BREAKING CHANGES:**
176
+
177
+ - `direct campaigns get` flags `--text-campaign-fields`,
178
+ `--mobile-app-campaign-fields`, `--dynamic-text-campaign-fields`,
179
+ `--cpm-banner-campaign-fields`, `--smart-campaign-fields`,
180
+ `--unified-campaign-fields`,
181
+ `--text-campaign-search-strategy-placement-types-fields`,
182
+ `--dynamic-text-campaign-search-strategy-placement-types-fields`,
183
+ `--unified-campaign-search-strategy-placement-types-fields`, and
184
+ `--unified-campaign-package-bidding-strategy-platforms-fields`
185
+ are **renamed** to their kebab-case WSDL-canonical `*-field-names`
186
+ form (`--text-campaign-field-names`,
187
+ `--mobile-app-campaign-field-names`, ...), matching the parameter
188
+ names declared by `CampaignsGetRequest`. The old `--*-fields`
189
+ forms are no longer accepted — update scripts and automation
190
+ accordingly. Closes #409.
191
+
192
+ **Additional features:**
193
+
194
+ - `direct bidmodifiers get` now exposes thirteen additional
195
+ `--*-adjustment-field-names` flags for the per-adjustment-subtype
196
+ WSDL `*FieldNames` request parameters declared by
197
+ `BidModifiersGetRequest`: `--ad-group-adjustment-field-names`,
198
+ `--demographics-adjustment-field-names`,
199
+ `--desktop-adjustment-field-names`,
200
+ `--desktop-only-adjustment-field-names`,
201
+ `--income-grade-adjustment-field-names`,
202
+ `--mobile-adjustment-field-names`,
203
+ `--regional-adjustment-field-names`,
204
+ `--retargeting-adjustment-field-names`,
205
+ `--serp-layout-adjustment-field-names`,
206
+ `--smart-ad-adjustment-field-names`,
207
+ `--smart-tv-adjustment-field-names`,
208
+ `--tablet-adjustment-field-names`, and
209
+ `--video-adjustment-field-names`. Previously only the top-level
210
+ `--fields` (mapping to `FieldNames`) was available, so the
211
+ per-adjustment projections could not be controlled from CLI.
212
+ Closes #408.
213
+ - `direct strategies get` now exposes sixteen additional
214
+ `--strategy-*-field-names` flags for the separate WSDL
215
+ `*FieldNames` request parameters declared by `StrategiesGetRequest`,
216
+ including `--strategy-average-cpa-field-names`,
217
+ `--strategy-average-cpa-multiple-goals-field-names`,
218
+ `--strategy-average-cpc-field-names`,
219
+ `--strategy-maximum-clicks-field-names`,
220
+ `--strategy-maximum-conversion-rate-field-names`,
221
+ `--strategy-pay-for-conversion-field-names`, and the remaining
222
+ per-campaign / per-filter strategy projections. The command also
223
+ gains `--dry-run` for read-path payload tests. Previously only the
224
+ top-level `--fields` (mapping to `FieldNames`) was available, so
225
+ per-strategy-subtype projections could not be controlled from CLI.
226
+ Closes #414.
227
+
228
+ Closes #360.
229
+
230
+ **Tests:**
231
+
232
+ - `tests/test_integration.py` now gracefully skips the seven read-only
233
+ classes that rely on live-API probes (`TestReadOnlyAdGroups`,
234
+ `TestReadOnlyAds`, `TestReadOnlyKeywords`,
235
+ `TestReadOnlyDynamicFeedAdTargets`, `TestReadOnlyLeads`,
236
+ `TestReadOnlyBusinesses`, `TestReadOnlyAdVideos`) when the probe
237
+ raises — previously a temporary API outage crashed `setUpClass`
238
+ with an opaque traceback.
239
+ - `invoke_get` in `tests/test_integration.py` now passes the resolved
240
+ test credentials as explicit `--token`/`--login` flags so the
241
+ integration suite cannot silently fall through to a developer's
242
+ active `direct auth` profile (priority 1 in the CLI credential
243
+ chain wins over the profile, matching CLAUDE.md guidance).
244
+ - `tests/test_comprehensive.py` slimmed down: `TestCLIHelp` (full
245
+ duplicate of `tests/test_cli.py`) removed; the unique
246
+ `TestCommandsRegistered`, `TestUtils`, `TestOutputFormatters`,
247
+ `TestAuth`, and `TestErrorHandling` classes are kept.
248
+ - `tests/test_smoke_matrix.py` no longer hard-codes
249
+ `total_cli_subcommands == 144` or `wsdl_operations == 112`. Counts
250
+ are derived from the live Click registry and parsed WSDLs.
251
+ - `tests/test_sandbox_write_audit.py` no longer hard-codes
252
+ `total == 83`. The count derives from `commands_for_category`.
253
+
254
+ Closes #396.
255
+
256
+ ## 0.3.13
257
+
258
+ **Breaking changes:**
259
+
260
+ - `direct campaigns add` and `direct campaigns update` now require all
261
+ bidding-strategy money flags to be passed directly in micro-rubles,
262
+ matching the existing `--budget`, `--average-cpa`, `--bid-ceiling`, and
263
+ `--filter-average-cpc` contract. The CLI no longer accepts decimal currency
264
+ values or performs unit conversion for campaign money
265
+ inputs. Closes #399.
266
+ - `direct ads add` and `direct ads update` now apply the same API-native
267
+ micro-ruble contract to `--price-extension-price` and
268
+ `--price-extension-old-price`; price-extension values are no longer parsed
269
+ as decimal currency amounts.
270
+
3
271
  ## 0.3.12
4
272
 
5
273
  **Added:**
@@ -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.