direct-cli 0.2.11__tar.gz → 0.3.1__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 (197) hide show
  1. {direct_cli-0.2.11 → direct_cli-0.3.1}/CLAUDE.md +11 -1
  2. {direct_cli-0.2.11 → direct_cli-0.3.1}/PKG-INFO +39 -9
  3. {direct_cli-0.2.11 → direct_cli-0.3.1}/README.md +38 -8
  4. direct_cli-0.3.1/direct_cli/_smoke_probes.py +94 -0
  5. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/_vendor/tapi_yandex_direct/__init__.py +2 -1
  6. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/_vendor/tapi_yandex_direct/exceptions.py +43 -0
  7. direct_cli-0.3.1/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.pyi +163 -0
  8. direct_cli-0.3.1/direct_cli/_vendor/tapi_yandex_direct/v4/__init__.py +9 -0
  9. direct_cli-0.3.1/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.py +221 -0
  10. direct_cli-0.3.1/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.pyi +54 -0
  11. direct_cli-0.3.1/direct_cli/_vendor/tapi_yandex_direct/v4/resource_mapping.py +66 -0
  12. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/api.py +53 -0
  13. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/cli.py +18 -0
  14. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/adextensions.py +4 -2
  15. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/ads.py +3 -5
  16. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/agencyclients.py +70 -13
  17. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/audiencetargets.py +30 -17
  18. direct_cli-0.3.1/direct_cli/commands/balance.py +55 -0
  19. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/bidmodifiers.py +15 -3
  20. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/bids.py +15 -3
  21. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/businesses.py +2 -2
  22. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/changes.py +14 -7
  23. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/clients.py +51 -16
  24. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/creatives.py +2 -4
  25. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/dynamicads.py +18 -9
  26. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/dynamicfeedadtargets.py +2 -4
  27. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/keywordbids.py +7 -11
  28. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/keywordsresearch.py +2 -2
  29. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/leads.py +2 -6
  30. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/negativekeywordsharedsets.py +4 -2
  31. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/sitelinks.py +2 -2
  32. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/smartadtargets.py +21 -10
  33. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/strategies.py +8 -6
  34. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/turbopages.py +2 -2
  35. direct_cli-0.3.1/direct_cli/commands/v4goals.py +103 -0
  36. direct_cli-0.3.1/direct_cli/commands/v4shells.py +40 -0
  37. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/vcards.py +2 -6
  38. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/smoke_matrix.py +14 -5
  39. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/utils.py +229 -10
  40. direct_cli-0.3.1/direct_cli/v4.py +17 -0
  41. direct_cli-0.3.1/direct_cli/v4_contracts.py +433 -0
  42. direct_cli-0.3.1/direct_cli/wsdl_coverage.py +581 -0
  43. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli.egg-info/PKG-INFO +39 -9
  44. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli.egg-info/SOURCES.txt +21 -1
  45. {direct_cli-0.2.11 → direct_cli-0.3.1}/pyproject.toml +2 -1
  46. {direct_cli-0.2.11 → direct_cli-0.3.1}/scripts/build_api_coverage_checklist.py +48 -34
  47. direct_cli-0.3.1/scripts/build_api_coverage_report.py +1040 -0
  48. {direct_cli-0.2.11 → direct_cli-0.3.1}/scripts/check_wsdl_drift.py +49 -2
  49. {direct_cli-0.2.11 → direct_cli-0.3.1}/scripts/patch_vendor_imports.py +40 -9
  50. {direct_cli-0.2.11 → direct_cli-0.3.1}/scripts/test_safe_commands.sh +73 -14
  51. {direct_cli-0.2.11 → direct_cli-0.3.1}/scripts/update_vendor.sh +5 -6
  52. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/API_COVERAGE.md +11 -1
  53. direct_cli-0.3.1/tests/api_coverage_payloads.py +450 -0
  54. direct_cli-0.3.1/tests/test_api_coverage.py +1765 -0
  55. direct_cli-0.3.1/tests/test_balance.py +137 -0
  56. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/test_comprehensive.py +8 -0
  57. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/test_dry_run.py +348 -138
  58. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/test_integration.py +5 -55
  59. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/test_smoke_matrix.py +31 -8
  60. direct_cli-0.3.1/tests/test_v4_contracts.py +141 -0
  61. direct_cli-0.3.1/tests/test_v4_foundation.py +91 -0
  62. direct_cli-0.3.1/tests/test_v4_live_contracts.py +79 -0
  63. direct_cli-0.3.1/tests/test_v4goals.py +190 -0
  64. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/test_vendor_imports.py +34 -12
  65. direct_cli-0.3.1/tests/wsdl_cache/imports/adextensiontypes.xsd +60 -0
  66. direct_cli-0.3.1/tests/wsdl_cache/imports/general.xsd +444 -0
  67. direct_cli-0.3.1/tests/wsdl_cache/imports/generalclients.xsd +396 -0
  68. direct_cli-0.2.11/direct_cli/wsdl_coverage.py +0 -346
  69. direct_cli-0.2.11/scripts/build_api_coverage_report.py +0 -236
  70. direct_cli-0.2.11/tests/test_api_coverage.py +0 -1373
  71. {direct_cli-0.2.11 → direct_cli-0.3.1}/.env.example +0 -0
  72. {direct_cli-0.2.11 → direct_cli-0.3.1}/.github/copilot-instructions.md +0 -0
  73. {direct_cli-0.2.11 → direct_cli-0.3.1}/.github/workflows/api-coverage.yml +0 -0
  74. {direct_cli-0.2.11 → direct_cli-0.3.1}/.github/workflows/claude-code-review.yml +0 -0
  75. {direct_cli-0.2.11 → direct_cli-0.3.1}/.github/workflows/claude.yml +0 -0
  76. {direct_cli-0.2.11 → direct_cli-0.3.1}/.gitignore +0 -0
  77. {direct_cli-0.2.11 → direct_cli-0.3.1}/AGENTS.md +0 -0
  78. {direct_cli-0.2.11 → direct_cli-0.3.1}/MANIFEST.in +0 -0
  79. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/__init__.py +0 -0
  80. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/_deprecated.py +0 -0
  81. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/_vendor/__init__.py +0 -0
  82. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/_vendor/tapi_yandex_direct/resource_mapping.py +0 -0
  83. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.py +0 -0
  84. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/auth.py +0 -0
  85. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/__init__.py +0 -0
  86. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/adgroups.py +0 -0
  87. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/adimages.py +0 -0
  88. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/advideos.py +0 -0
  89. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/auth.py +0 -0
  90. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/campaigns.py +0 -0
  91. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/dictionaries.py +0 -0
  92. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/feeds.py +0 -0
  93. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/keywords.py +0 -0
  94. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/reports.py +0 -0
  95. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/commands/retargeting.py +0 -0
  96. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/output.py +0 -0
  97. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli/reports_coverage.py +0 -0
  98. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli.egg-info/dependency_links.txt +0 -0
  99. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli.egg-info/entry_points.txt +0 -0
  100. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli.egg-info/requires.txt +0 -0
  101. {direct_cli-0.2.11 → direct_cli-0.3.1}/direct_cli.egg-info/top_level.txt +0 -0
  102. {direct_cli-0.2.11 → direct_cli-0.3.1}/docs/superpowers/plans/2026-04-12-issue-32-completion.md +0 -0
  103. {direct_cli-0.2.11 → direct_cli-0.3.1}/docs/superpowers/specs/2026-04-23-vendor-tapi-yandex-direct-design.md +0 -0
  104. {direct_cli-0.2.11 → direct_cli-0.3.1}/scripts/anonymize_cassettes.py +0 -0
  105. {direct_cli-0.2.11 → direct_cli-0.3.1}/scripts/check_reports_drift.py +0 -0
  106. {direct_cli-0.2.11 → direct_cli-0.3.1}/scripts/refresh_reports_cache.py +0 -0
  107. {direct_cli-0.2.11 → direct_cli-0.3.1}/scripts/refresh_wsdl_cache.py +0 -0
  108. {direct_cli-0.2.11 → direct_cli-0.3.1}/scripts/release_pypi.sh +0 -0
  109. {direct_cli-0.2.11 → direct_cli-0.3.1}/scripts/sandbox_write_live.py +0 -0
  110. {direct_cli-0.2.11 → direct_cli-0.3.1}/scripts/test_dangerous_commands.sh +0 -0
  111. {direct_cli-0.2.11 → direct_cli-0.3.1}/scripts/test_sandbox_write.sh +0 -0
  112. {direct_cli-0.2.11 → direct_cli-0.3.1}/setup.cfg +0 -0
  113. {direct_cli-0.2.11 → direct_cli-0.3.1}/setup.py +0 -0
  114. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/API_ISSUE_AUDIT.md +0 -0
  115. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/MANUAL_COVERAGE.md +0 -0
  116. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/__init__.py +0 -0
  117. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_adgroups_add_update_delete.yaml +0 -0
  118. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_adimages_add_get_delete.yaml +0 -0
  119. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_ads_add_update_delete.yaml +0 -0
  120. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_ads_suspend_resume_archive_unarchive.yaml +0 -0
  121. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_advideos_add_get.yaml +0 -0
  122. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_audiencetargets_add_delete.yaml +0 -0
  123. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_audiencetargets_suspend_resume.yaml +0 -0
  124. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_bids_set.yaml +0 -0
  125. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_campaign_create_get_delete.yaml +0 -0
  126. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_creatives_chain_advideo_to_creative.yaml +0 -0
  127. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_dynamicads_add_delete.yaml +0 -0
  128. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_dynamicads_suspend_resume.yaml +0 -0
  129. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_keywordbids_set.yaml +0 -0
  130. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_keywords_add_update_delete.yaml +0 -0
  131. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_keywords_suspend_resume.yaml +0 -0
  132. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_sitelinks_add_get_delete.yaml +0 -0
  133. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_smartadtargets_add_update_delete.yaml +0 -0
  134. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_smartadtargets_suspend_resume.yaml +0 -0
  135. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteAdExtensions.test_add_delete.yaml +0 -0
  136. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteAdGroups.test_add_update_delete.yaml +0 -0
  137. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteAdImages.test_add_delete.yaml +0 -0
  138. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteAds.test_add_text_ad_update_delete.yaml +0 -0
  139. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteAudienceTargets.test_add_delete.yaml +0 -0
  140. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteBidModifiersAdd.test_add_delete_mobile.yaml +0 -0
  141. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteBidModifiersSet.test_set_without_id_is_rejected.yaml +0 -0
  142. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteBids.test_set_bid.yaml +0 -0
  143. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteCampaignDraftLifecycle.test_draft_create_get_delete.yaml +0 -0
  144. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteCampaigns.test_campaign_lifecycle.yaml +0 -0
  145. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteDynamicAds.test_add_update_delete.yaml +0 -0
  146. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteFeeds.test_add_update_delete.yaml +0 -0
  147. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteKeywordBids.test_set_keyword_bid.yaml +0 -0
  148. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteKeywords.test_add_update_delete.yaml +0 -0
  149. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteNegativeKeywordSharedSets.test_add_update_delete.yaml +0 -0
  150. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteRetargeting.test_add_delete.yaml +0 -0
  151. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteSitelinks.test_add_delete.yaml +0 -0
  152. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteSmartAdTargets.test_add_update_delete.yaml +0 -0
  153. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteVCards.test_add_delete.yaml +0 -0
  154. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/conftest.py +0 -0
  155. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/fixtures/test-video.mp4 +0 -0
  156. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/reports_cache/raw/fields-list.html +0 -0
  157. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/reports_cache/raw/headers.html +0 -0
  158. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/reports_cache/raw/spec.html +0 -0
  159. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/reports_cache/raw/type.html +0 -0
  160. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/reports_cache/spec.json +0 -0
  161. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/test_auth_bw.py +0 -0
  162. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/test_auth_oauth.py +0 -0
  163. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/test_auth_op.py +0 -0
  164. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/test_cli.py +0 -0
  165. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/test_integration_live_write.py +0 -0
  166. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/test_integration_write.py +0 -0
  167. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/test_reports_drift.py +0 -0
  168. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/test_transport_contract.py +0 -0
  169. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/adextensions.xml +0 -0
  170. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/adgroups.xml +0 -0
  171. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/adimages.xml +0 -0
  172. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/ads.xml +0 -0
  173. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/advideos.xml +0 -0
  174. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/agencyclients.xml +0 -0
  175. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/audiencetargets.xml +0 -0
  176. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/bidmodifiers.xml +0 -0
  177. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/bids.xml +0 -0
  178. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/businesses.xml +0 -0
  179. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/campaigns.xml +0 -0
  180. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/changes.xml +0 -0
  181. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/clients.xml +0 -0
  182. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/creatives.xml +0 -0
  183. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/dictionaries.xml +0 -0
  184. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/dynamicfeedadtargets.xml +0 -0
  185. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/dynamictextadtargets.xml +0 -0
  186. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/feeds.xml +0 -0
  187. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/keywordbids.xml +0 -0
  188. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/keywords.xml +0 -0
  189. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/keywordsresearch.xml +0 -0
  190. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/leads.xml +0 -0
  191. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/negativekeywordsharedsets.xml +0 -0
  192. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/retargetinglists.xml +0 -0
  193. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/sitelinks.xml +0 -0
  194. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/smartadtargets.xml +0 -0
  195. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/strategies.xml +0 -0
  196. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/turbopages.xml +0 -0
  197. {direct_cli-0.2.11 → direct_cli-0.3.1}/tests/wsdl_cache/vcards.xml +0 -0
@@ -39,11 +39,17 @@ Click group-of-groups. Each Yandex Direct API resource = one file in `direct_cli
39
39
 
40
40
  **`--dry-run`:** `add`/`update` commands print request JSON without calling the API. Use as test seam.
41
41
 
42
+ **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`.
43
+
42
44
  **SelectionCriteria:** Resources like `adgroups`, `ads`, `keywords` require at least one of `Ids`, `CampaignIds`, or `AdGroupIds` — otherwise API error 4001.
43
45
 
44
46
  **Error handling:** All commands wrap API calls in `try/except Exception` → `print_error(str(e))` + `raise click.Abort()`.
45
47
 
46
- **Default fields:** `COMMON_FIELDS` in `utils.py` maps resource names to `FieldNames`. Not all fields are valid for all resources (e.g., `adimages` uses `AdImageHash`, not `Id`).
48
+ **Default fields:** `COMMON_FIELDS` in `utils.py` maps resource names to default `*FieldNames`. Most entries are `list[str]`; multi-`*FieldNames` resources use `dict[str, list[str]]` keyed by WSDL request param (for example `FieldNames`, `TextAdFieldNames`, `SearchFieldNames`). Not all fields are valid for all resources (e.g., `adimages` uses `AdImageHash`, not `Id`).
49
+
50
+ **WSDL imports:** Imported schemas used by request validation are cached in `tests/wsdl_cache/imports/` and registered in `IMPORTED_XSD_REGISTRY`. Keep imported nested types resolved; empty `item_fields` for registered imports is coverage drift.
51
+
52
+ **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`.
47
53
 
48
54
  ## Tests
49
55
 
@@ -66,3 +72,7 @@ subcommand belongs to exactly one category:
66
72
  Never auto-test production mutations: agency client changes, live bid changes,
67
73
  moderation, lifecycle operations, `clients update`, or any `delete` without
68
74
  `--sandbox`.
75
+
76
+ Client update payloads (`clients update` and `agencyclients update`) must use
77
+ the shared typed helpers in `direct_cli/utils.py`; do not expose raw JSON for
78
+ general client update fields or nested client update payloads.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: direct-cli
3
- Version: 0.2.11
3
+ Version: 0.3.1
4
4
  Summary: Command-line interface for Yandex Direct API
5
5
  Author: axisrow
6
6
  License: MIT
@@ -118,6 +118,18 @@ Install with `pip install direct-cli`, then run commands with `direct`.
118
118
  Invoking the deprecated `direct-cli` entrypoint exits with
119
119
  `use direct instead of direct-cli`.
120
120
 
121
+ ### Quick Start: Check Balance
122
+
123
+ Yandex removed the legacy v4 `GetBalance` method. Direct CLI uses the v4 Live
124
+ `AccountManagement` method with `Action=Get` for `direct balance`, returning
125
+ money fields such as `Amount`, `AmountAvailableForTransfer`, and `Currency`.
126
+
127
+ ```bash
128
+ direct balance
129
+ direct balance --logins client-login,other-client --format table
130
+ direct balance --logins client-login --dry-run
131
+ ```
132
+
121
133
  ### Global Options
122
134
 
123
135
  | Option | Description |
@@ -127,6 +139,14 @@ Invoking the deprecated `direct-cli` entrypoint exits with
127
139
  | `--profile` | Credential profile name |
128
140
  | `--sandbox` | Use sandbox API |
129
141
 
142
+ ### V4 Live Goals
143
+
144
+ ```bash
145
+ direct v4goals get-stat-goals --campaign-ids 123,456
146
+ direct v4goals get-retargeting-goals --campaign-ids 123,456 --format table
147
+ direct v4goals get-stat-goals --campaign-ids 123 --dry-run
148
+ ```
149
+
130
150
  ### CLI Convention
131
151
 
132
152
  The current CLI convention is defined as follows.
@@ -342,7 +362,7 @@ direct dictionaries get-geo-regions --name Moscow --region-ids 225,187 --exact-n
342
362
  direct clients get --fields ClientId,Login,Currency
343
363
 
344
364
  # Changes
345
- direct changes check --campaign-ids 1,2,3 --timestamp 2026-04-14T00:00:00
365
+ direct changes check --campaign-ids 1,2,3 --timestamp 2026-04-14T00:00:00 --fields CampaignIds,AdGroupIds,AdIds,CampaignsStat
346
366
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
347
367
  direct changes check-dictionaries
348
368
 
@@ -352,15 +372,18 @@ direct retargeting add --name "List A" --type AUDIENCE --rule "ALL:12345:30|6789
352
372
  direct retargeting update --id 55 --name "Renamed" --rule "ANY:12345:30" --dry-run
353
373
 
354
374
  # Bids and modifiers
375
+ direct bids get --campaign-ids 123 --fields CampaignId,AdGroupId,KeywordId,Bid
355
376
  direct bids set --keyword-id 123 --bid 15000000
356
377
  direct bids set-auto --keyword-id 123 --max-bid 20000000 --position PREMIUMBLOCK --scope SEARCH --dry-run
357
378
  direct keywordbids set --keyword-id 321 --search-bid 8000000 --network-bid 3000000
358
379
  direct keywordbids set-auto --keyword-id 321 --target-traffic-volume 100 --increase-percent 10 --bid-ceiling 12500000 --dry-run
380
+ direct bidmodifiers get --campaign-ids 123 --fields Id,CampaignId,AdGroupId,Level,Type
359
381
  direct bidmodifiers add --campaign-id 123 --type DEMOGRAPHICS_ADJUSTMENT --value 150 --gender GENDER_MALE --age AGE_25_34 --dry-run
360
382
  direct bidmodifiers set --id 99 --value 130 --dry-run
361
383
 
362
384
  # Canonical multiword groups
363
385
  direct negativekeywordsharedsets update --id 123 --keywords "foo,bar"
386
+ direct audiencetargets get --campaign-ids 123 --fields Id,AdGroupId,RetargetingListId,State,ContextBid
364
387
  direct audiencetargets add --adgroup-id 100 --retargeting-list-id 200 --bid 12000000 --priority HIGH --dry-run
365
388
  direct audiencetargets set-bids --id 101 --context-bid 7000000 --priority LOW --dry-run
366
389
  direct dynamicads add --adgroup-id 33 --name "Webpage A" --condition "URL:CONTAINS_ANY:test|shop" --condition "PAGE_CONTENT:CONTAINS:baz" --bid 3000000 --context-bid 2000000 --priority HIGH --dry-run
@@ -388,13 +411,15 @@ direct adimages add --name banner.png --image-data BASE64DATA --type ICON --dry-
388
411
  direct creatives add --video-id video-id --dry-run
389
412
  direct feeds add --name "Feed A" --url "https://example.com/feed.xml" --dry-run
390
413
  direct feeds update --id 18 --name "Feed A v2" --url "https://example.com/feed-v2.xml" --dry-run
391
- direct clients update --client-id 999 --phone +70000000000 --fax +70000000001 --email user@example.com --city Moscow --dry-run
392
- direct agencyclients add --login client-login --first-name Alice --last-name Smith --currency RUB --notification-email ops@example.com --notification-lang RU --send-account-news --no-send-warnings --dry-run
414
+ direct clients update --client-info "Priority client" --phone +70000000000 --notification-email user@example.com --notification-lang EN --email-subscription RECEIVE_RECOMMENDATIONS=YES --setting DISPLAY_STORE_RATING=NO --dry-run
415
+ direct --login CLIENT_LOGIN clients update --phone +70000000000 --notification-email user@example.com --dry-run
393
416
  direct agencyclients add-passport-organization --name "Org" --currency RUB --notification-email ops@example.com --notification-lang EN --no-send-account-news --send-warnings --dry-run
394
417
  direct agencyclients add-passport-organization-member --passport-organization-login org-login --role CHIEF --invite-email user@example.com --dry-run
395
- direct agencyclients update --client-id 42 --phone +70000000000 --email user@example.com --grant EDIT_CAMPAIGNS --grant IMPORT_XLS --dry-run
418
+ direct agencyclients update --client-id 42 --phone +70000000000 --notification-email user@example.com --grant EDIT_CAMPAIGNS=YES --grant IMPORT_XLS=NO --dry-run
396
419
  ```
397
420
 
421
+ `direct agencyclients add` is runtime-deprecated by Yandex Direct and is blocked by the CLI. Use `direct agencyclients add-passport-organization` instead.
422
+
398
423
  ### Known Unsupported API Operation
399
424
 
400
425
  `dynamicads update` is unsupported by API. The Yandex Direct
@@ -959,7 +984,7 @@ direct dictionaries get-geo-regions --name Москва --region-ids 225,187 --e
959
984
  direct clients get --fields ClientId,Login,Currency
960
985
 
961
986
  # Изменения
962
- direct changes check --campaign-ids 1,2,3 --timestamp 2026-04-14T00:00:00
987
+ direct changes check --campaign-ids 1,2,3 --timestamp 2026-04-14T00:00:00 --fields CampaignIds,AdGroupIds,AdIds,CampaignsStat
963
988
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
964
989
  direct changes check-dictionaries
965
990
 
@@ -969,15 +994,18 @@ direct retargeting add --name "Список A" --type AUDIENCE --rule "ALL:12345
969
994
  direct retargeting update --id 55 --name "Переименованный список" --rule "ANY:12345:30" --dry-run
970
995
 
971
996
  # Ставки и модификаторы
997
+ direct bids get --campaign-ids 123 --fields CampaignId,AdGroupId,KeywordId,Bid
972
998
  direct bids set --keyword-id 123 --bid 15000000
973
999
  direct bids set-auto --keyword-id 123 --max-bid 20000000 --position PREMIUMBLOCK --scope SEARCH --dry-run
974
1000
  direct keywordbids set --keyword-id 321 --search-bid 8000000 --network-bid 3000000
975
1001
  direct keywordbids set-auto --keyword-id 321 --target-traffic-volume 100 --increase-percent 10 --bid-ceiling 12500000 --dry-run
1002
+ direct bidmodifiers get --campaign-ids 123 --fields Id,CampaignId,AdGroupId,Level,Type
976
1003
  direct bidmodifiers add --campaign-id 123 --type DEMOGRAPHICS_ADJUSTMENT --value 150 --gender GENDER_MALE --age AGE_25_34 --dry-run
977
1004
  direct bidmodifiers set --id 99 --value 130 --dry-run
978
1005
 
979
1006
  # Канонические многословные группы
980
1007
  direct negativekeywordsharedsets update --id 123 --keywords "foo,bar"
1008
+ direct audiencetargets get --campaign-ids 123 --fields Id,AdGroupId,RetargetingListId,State,ContextBid
981
1009
  direct audiencetargets add --adgroup-id 100 --retargeting-list-id 200 --bid 12000000 --priority HIGH --dry-run
982
1010
  direct audiencetargets set-bids --id 101 --context-bid 7000000 --priority LOW --dry-run
983
1011
  direct dynamicads add --adgroup-id 33 --name "Webpage A" --condition "URL:CONTAINS_ANY:test|shop" --condition "PAGE_CONTENT:CONTAINS:baz" --bid 3000000 --context-bid 2000000 --priority HIGH --dry-run
@@ -1005,13 +1033,15 @@ direct adimages add --name banner.png --image-data BASE64DATA --type ICON --dry-
1005
1033
  direct creatives add --video-id video-id --dry-run
1006
1034
  direct feeds add --name "Фид A" --url "https://example.com/feed.xml" --dry-run
1007
1035
  direct feeds update --id 18 --name "Фид A v2" --url "https://example.com/feed-v2.xml" --dry-run
1008
- direct clients update --client-id 999 --phone +70000000000 --fax +70000000001 --email user@example.com --city Moscow --dry-run
1009
- direct agencyclients add --login client-login --first-name Alice --last-name Smith --currency RUB --notification-email ops@example.com --notification-lang RU --send-account-news --no-send-warnings --dry-run
1036
+ direct clients update --client-info "Приоритетный клиент" --phone +70000000000 --notification-email user@example.com --notification-lang EN --email-subscription RECEIVE_RECOMMENDATIONS=YES --setting DISPLAY_STORE_RATING=NO --dry-run
1037
+ direct --login CLIENT_LOGIN clients update --phone +70000000000 --notification-email user@example.com --dry-run
1010
1038
  direct agencyclients add-passport-organization --name "Org" --currency RUB --notification-email ops@example.com --notification-lang EN --no-send-account-news --send-warnings --dry-run
1011
1039
  direct agencyclients add-passport-organization-member --passport-organization-login org-login --role CHIEF --invite-email user@example.com --dry-run
1012
- direct agencyclients update --client-id 42 --phone +70000000000 --email user@example.com --grant EDIT_CAMPAIGNS --grant IMPORT_XLS --dry-run
1040
+ direct agencyclients update --client-id 42 --phone +70000000000 --notification-email user@example.com --grant EDIT_CAMPAIGNS=YES --grant IMPORT_XLS=NO --dry-run
1013
1041
  ```
1014
1042
 
1043
+ `direct agencyclients add` runtime-deprecated в Yandex Direct и блокируется CLI. Используйте `direct agencyclients add-passport-organization`.
1044
+
1015
1045
  ### Известная неподдерживаемая API-операция
1016
1046
 
1017
1047
  `dynamicads update` unsupported by API. Сервис Яндекс Директа
@@ -79,6 +79,18 @@ Install with `pip install direct-cli`, then run commands with `direct`.
79
79
  Invoking the deprecated `direct-cli` entrypoint exits with
80
80
  `use direct instead of direct-cli`.
81
81
 
82
+ ### Quick Start: Check Balance
83
+
84
+ Yandex removed the legacy v4 `GetBalance` method. Direct CLI uses the v4 Live
85
+ `AccountManagement` method with `Action=Get` for `direct balance`, returning
86
+ money fields such as `Amount`, `AmountAvailableForTransfer`, and `Currency`.
87
+
88
+ ```bash
89
+ direct balance
90
+ direct balance --logins client-login,other-client --format table
91
+ direct balance --logins client-login --dry-run
92
+ ```
93
+
82
94
  ### Global Options
83
95
 
84
96
  | Option | Description |
@@ -88,6 +100,14 @@ Invoking the deprecated `direct-cli` entrypoint exits with
88
100
  | `--profile` | Credential profile name |
89
101
  | `--sandbox` | Use sandbox API |
90
102
 
103
+ ### V4 Live Goals
104
+
105
+ ```bash
106
+ direct v4goals get-stat-goals --campaign-ids 123,456
107
+ direct v4goals get-retargeting-goals --campaign-ids 123,456 --format table
108
+ direct v4goals get-stat-goals --campaign-ids 123 --dry-run
109
+ ```
110
+
91
111
  ### CLI Convention
92
112
 
93
113
  The current CLI convention is defined as follows.
@@ -303,7 +323,7 @@ direct dictionaries get-geo-regions --name Moscow --region-ids 225,187 --exact-n
303
323
  direct clients get --fields ClientId,Login,Currency
304
324
 
305
325
  # Changes
306
- direct changes check --campaign-ids 1,2,3 --timestamp 2026-04-14T00:00:00
326
+ direct changes check --campaign-ids 1,2,3 --timestamp 2026-04-14T00:00:00 --fields CampaignIds,AdGroupIds,AdIds,CampaignsStat
307
327
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
308
328
  direct changes check-dictionaries
309
329
 
@@ -313,15 +333,18 @@ direct retargeting add --name "List A" --type AUDIENCE --rule "ALL:12345:30|6789
313
333
  direct retargeting update --id 55 --name "Renamed" --rule "ANY:12345:30" --dry-run
314
334
 
315
335
  # Bids and modifiers
336
+ direct bids get --campaign-ids 123 --fields CampaignId,AdGroupId,KeywordId,Bid
316
337
  direct bids set --keyword-id 123 --bid 15000000
317
338
  direct bids set-auto --keyword-id 123 --max-bid 20000000 --position PREMIUMBLOCK --scope SEARCH --dry-run
318
339
  direct keywordbids set --keyword-id 321 --search-bid 8000000 --network-bid 3000000
319
340
  direct keywordbids set-auto --keyword-id 321 --target-traffic-volume 100 --increase-percent 10 --bid-ceiling 12500000 --dry-run
341
+ direct bidmodifiers get --campaign-ids 123 --fields Id,CampaignId,AdGroupId,Level,Type
320
342
  direct bidmodifiers add --campaign-id 123 --type DEMOGRAPHICS_ADJUSTMENT --value 150 --gender GENDER_MALE --age AGE_25_34 --dry-run
321
343
  direct bidmodifiers set --id 99 --value 130 --dry-run
322
344
 
323
345
  # Canonical multiword groups
324
346
  direct negativekeywordsharedsets update --id 123 --keywords "foo,bar"
347
+ direct audiencetargets get --campaign-ids 123 --fields Id,AdGroupId,RetargetingListId,State,ContextBid
325
348
  direct audiencetargets add --adgroup-id 100 --retargeting-list-id 200 --bid 12000000 --priority HIGH --dry-run
326
349
  direct audiencetargets set-bids --id 101 --context-bid 7000000 --priority LOW --dry-run
327
350
  direct dynamicads add --adgroup-id 33 --name "Webpage A" --condition "URL:CONTAINS_ANY:test|shop" --condition "PAGE_CONTENT:CONTAINS:baz" --bid 3000000 --context-bid 2000000 --priority HIGH --dry-run
@@ -349,13 +372,15 @@ direct adimages add --name banner.png --image-data BASE64DATA --type ICON --dry-
349
372
  direct creatives add --video-id video-id --dry-run
350
373
  direct feeds add --name "Feed A" --url "https://example.com/feed.xml" --dry-run
351
374
  direct feeds update --id 18 --name "Feed A v2" --url "https://example.com/feed-v2.xml" --dry-run
352
- direct clients update --client-id 999 --phone +70000000000 --fax +70000000001 --email user@example.com --city Moscow --dry-run
353
- direct agencyclients add --login client-login --first-name Alice --last-name Smith --currency RUB --notification-email ops@example.com --notification-lang RU --send-account-news --no-send-warnings --dry-run
375
+ direct clients update --client-info "Priority client" --phone +70000000000 --notification-email user@example.com --notification-lang EN --email-subscription RECEIVE_RECOMMENDATIONS=YES --setting DISPLAY_STORE_RATING=NO --dry-run
376
+ direct --login CLIENT_LOGIN clients update --phone +70000000000 --notification-email user@example.com --dry-run
354
377
  direct agencyclients add-passport-organization --name "Org" --currency RUB --notification-email ops@example.com --notification-lang EN --no-send-account-news --send-warnings --dry-run
355
378
  direct agencyclients add-passport-organization-member --passport-organization-login org-login --role CHIEF --invite-email user@example.com --dry-run
356
- direct agencyclients update --client-id 42 --phone +70000000000 --email user@example.com --grant EDIT_CAMPAIGNS --grant IMPORT_XLS --dry-run
379
+ direct agencyclients update --client-id 42 --phone +70000000000 --notification-email user@example.com --grant EDIT_CAMPAIGNS=YES --grant IMPORT_XLS=NO --dry-run
357
380
  ```
358
381
 
382
+ `direct agencyclients add` is runtime-deprecated by Yandex Direct and is blocked by the CLI. Use `direct agencyclients add-passport-organization` instead.
383
+
359
384
  ### Known Unsupported API Operation
360
385
 
361
386
  `dynamicads update` is unsupported by API. The Yandex Direct
@@ -920,7 +945,7 @@ direct dictionaries get-geo-regions --name Москва --region-ids 225,187 --e
920
945
  direct clients get --fields ClientId,Login,Currency
921
946
 
922
947
  # Изменения
923
- direct changes check --campaign-ids 1,2,3 --timestamp 2026-04-14T00:00:00
948
+ direct changes check --campaign-ids 1,2,3 --timestamp 2026-04-14T00:00:00 --fields CampaignIds,AdGroupIds,AdIds,CampaignsStat
924
949
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
925
950
  direct changes check-dictionaries
926
951
 
@@ -930,15 +955,18 @@ direct retargeting add --name "Список A" --type AUDIENCE --rule "ALL:12345
930
955
  direct retargeting update --id 55 --name "Переименованный список" --rule "ANY:12345:30" --dry-run
931
956
 
932
957
  # Ставки и модификаторы
958
+ direct bids get --campaign-ids 123 --fields CampaignId,AdGroupId,KeywordId,Bid
933
959
  direct bids set --keyword-id 123 --bid 15000000
934
960
  direct bids set-auto --keyword-id 123 --max-bid 20000000 --position PREMIUMBLOCK --scope SEARCH --dry-run
935
961
  direct keywordbids set --keyword-id 321 --search-bid 8000000 --network-bid 3000000
936
962
  direct keywordbids set-auto --keyword-id 321 --target-traffic-volume 100 --increase-percent 10 --bid-ceiling 12500000 --dry-run
963
+ direct bidmodifiers get --campaign-ids 123 --fields Id,CampaignId,AdGroupId,Level,Type
937
964
  direct bidmodifiers add --campaign-id 123 --type DEMOGRAPHICS_ADJUSTMENT --value 150 --gender GENDER_MALE --age AGE_25_34 --dry-run
938
965
  direct bidmodifiers set --id 99 --value 130 --dry-run
939
966
 
940
967
  # Канонические многословные группы
941
968
  direct negativekeywordsharedsets update --id 123 --keywords "foo,bar"
969
+ direct audiencetargets get --campaign-ids 123 --fields Id,AdGroupId,RetargetingListId,State,ContextBid
942
970
  direct audiencetargets add --adgroup-id 100 --retargeting-list-id 200 --bid 12000000 --priority HIGH --dry-run
943
971
  direct audiencetargets set-bids --id 101 --context-bid 7000000 --priority LOW --dry-run
944
972
  direct dynamicads add --adgroup-id 33 --name "Webpage A" --condition "URL:CONTAINS_ANY:test|shop" --condition "PAGE_CONTENT:CONTAINS:baz" --bid 3000000 --context-bid 2000000 --priority HIGH --dry-run
@@ -966,13 +994,15 @@ direct adimages add --name banner.png --image-data BASE64DATA --type ICON --dry-
966
994
  direct creatives add --video-id video-id --dry-run
967
995
  direct feeds add --name "Фид A" --url "https://example.com/feed.xml" --dry-run
968
996
  direct feeds update --id 18 --name "Фид A v2" --url "https://example.com/feed-v2.xml" --dry-run
969
- direct clients update --client-id 999 --phone +70000000000 --fax +70000000001 --email user@example.com --city Moscow --dry-run
970
- direct agencyclients add --login client-login --first-name Alice --last-name Smith --currency RUB --notification-email ops@example.com --notification-lang RU --send-account-news --no-send-warnings --dry-run
997
+ direct clients update --client-info "Приоритетный клиент" --phone +70000000000 --notification-email user@example.com --notification-lang EN --email-subscription RECEIVE_RECOMMENDATIONS=YES --setting DISPLAY_STORE_RATING=NO --dry-run
998
+ direct --login CLIENT_LOGIN clients update --phone +70000000000 --notification-email user@example.com --dry-run
971
999
  direct agencyclients add-passport-organization --name "Org" --currency RUB --notification-email ops@example.com --notification-lang EN --no-send-account-news --send-warnings --dry-run
972
1000
  direct agencyclients add-passport-organization-member --passport-organization-login org-login --role CHIEF --invite-email user@example.com --dry-run
973
- direct agencyclients update --client-id 42 --phone +70000000000 --email user@example.com --grant EDIT_CAMPAIGNS --grant IMPORT_XLS --dry-run
1001
+ direct agencyclients update --client-id 42 --phone +70000000000 --notification-email user@example.com --grant EDIT_CAMPAIGNS=YES --grant IMPORT_XLS=NO --dry-run
974
1002
  ```
975
1003
 
1004
+ `direct agencyclients add` runtime-deprecated в Yandex Direct и блокируется CLI. Используйте `direct agencyclients add-passport-organization`.
1005
+
976
1006
  ### Известная неподдерживаемая API-операция
977
1007
 
978
1008
  `dynamicads update` unsupported by API. Сервис Яндекс Директа
@@ -0,0 +1,94 @@
1
+ """Live smoke-test probes for IDs that cannot be hardcoded safely."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import sys
7
+ from typing import Any, Optional
8
+
9
+ from .api import create_client
10
+
11
+ VIDEO_CREATIVE_TYPES = {
12
+ "VIDEO_EXTENSION_CREATIVE",
13
+ "CPM_VIDEO_CREATIVE",
14
+ "CPC_VIDEO_CREATIVE",
15
+ }
16
+
17
+
18
+ def _extract_items(response: Any) -> list[dict[str, Any]]:
19
+ """Return a normalized list from a tapi response object."""
20
+ data = response().extract()
21
+ return data if isinstance(data, list) else []
22
+
23
+
24
+ def _advideo_get_accepts(client: Any, candidate: str) -> bool:
25
+ """Return whether advideos.get accepts the candidate ID."""
26
+ body = {
27
+ "method": "get",
28
+ "params": {
29
+ "SelectionCriteria": {"Ids": [candidate]},
30
+ "FieldNames": ["Id", "Status"],
31
+ "Page": {"Limit": 1},
32
+ },
33
+ }
34
+ try:
35
+ _extract_items(client.advideos().post(data=body))
36
+ return True
37
+ except Exception:
38
+ return False
39
+
40
+
41
+ def advideo_probe_id() -> Optional[str]:
42
+ """Return an AdVideos ID accepted by ``advideos.get``, or ``None``.
43
+
44
+ Returns ``None`` (never raises) on auth failure, missing credentials,
45
+ or any network/API error — smoke scripts use this in a context where a
46
+ failed probe should be a benign skip, not a fatal error.
47
+ """
48
+ try:
49
+ client = create_client()
50
+ except Exception:
51
+ return None
52
+
53
+ env_id = os.getenv("YANDEX_DIRECT_TEST_ADVIDEO_ID")
54
+ if env_id and _advideo_get_accepts(client, env_id):
55
+ return env_id
56
+
57
+ body = {
58
+ "method": "get",
59
+ "params": {
60
+ "SelectionCriteria": {},
61
+ "FieldNames": ["Id", "Name", "Type"],
62
+ "Page": {"Limit": 20},
63
+ },
64
+ }
65
+ try:
66
+ creatives = _extract_items(client.creatives().post(data=body))
67
+ except Exception:
68
+ return None
69
+
70
+ for creative in creatives:
71
+ if creative.get("Type") not in VIDEO_CREATIVE_TYPES:
72
+ continue
73
+ candidate = creative.get("Id")
74
+ if candidate and _advideo_get_accepts(client, str(candidate)):
75
+ return str(candidate)
76
+ return None
77
+
78
+
79
+ def main(argv: list[str] | None = None) -> int:
80
+ """CLI entry point for smoke probes."""
81
+ argv = list(sys.argv[1:] if argv is None else argv)
82
+ if argv != ["advideo"]:
83
+ print("Usage: python3 -m direct_cli._smoke_probes advideo", file=sys.stderr)
84
+ return 2
85
+
86
+ probe_id = advideo_probe_id()
87
+ if not probe_id:
88
+ return 1
89
+ print(probe_id)
90
+ return 0
91
+
92
+
93
+ if __name__ == "__main__":
94
+ raise SystemExit(main())
@@ -1,8 +1,9 @@
1
1
 
2
2
  __author__ = 'Pavel Maksimov'
3
3
  __email__ = 'vur21@ya.ru'
4
- __version__ = '2026.4.25'
4
+ __version__ = '2026.4.28'
5
5
 
6
6
 
7
7
  from .resource_mapping import *
8
8
  from .tapi_yandex_direct import YandexDirect
9
+ from .v4 import YandexDirectV4Live
@@ -65,6 +65,49 @@ class YandexDirectRequestsLimitError(YandexDirectClientError):
65
65
  super().__init__(*args, **kwargs)
66
66
 
67
67
 
68
+ class V4LiveError(YandexDirectApiError):
69
+ """Base exception for v4 Live API errors (error_code != 0).
70
+
71
+ v4 Live error payload differs from v5: integer ``error_code``, ``error_str``
72
+ instead of ``error_string``, no ``request_id``. Errors are returned with
73
+ HTTP 200 — they are detected from the response body, not the status code.
74
+ """
75
+
76
+ def __init__(
77
+ self,
78
+ response: Response,
79
+ message: Union[str, dict],
80
+ client: TapiClient,
81
+ *args,
82
+ **kwargs,
83
+ ):
84
+ if isinstance(message, dict):
85
+ try:
86
+ self.error_code = int(message.get("error_code", 0))
87
+ except (TypeError, ValueError):
88
+ self.error_code = 0
89
+ self.error_str = message.get("error_str", "")
90
+ self.error_detail = message.get("error_detail", "")
91
+ else:
92
+ self.error_code = 0
93
+ self.error_str = str(message)
94
+ self.error_detail = ""
95
+ super().__init__(response, message, client, *args, **kwargs)
96
+
97
+ def __str__(self):
98
+ return "v4 Live error_code={}, error_str={}, error_detail={}".format(
99
+ self.error_code, self.error_str, self.error_detail
100
+ )
101
+
102
+
103
+ class V4LiveTokenError(V4LiveError):
104
+ """v4 Live error_code=53 — invalid or missing OAuth token."""
105
+
106
+
107
+ class V4LiveRequestsLimitError(V4LiveError):
108
+ """v4 Live error_code in (54, 55, 56) — rate / method limit exceeded."""
109
+
110
+
68
111
  class BackwardCompatibilityError(Exception):
69
112
  def __init__(self, name):
70
113
  self.name = name
@@ -0,0 +1,163 @@
1
+ from typing import List, Iterator, Union
2
+
3
+ from requests import Response
4
+
5
+ class YandexDirectBaseMethodsClientResponse:
6
+ @property
7
+ def data(self) -> dict: ...
8
+ @property
9
+ def request_kwargs(self) -> dict: ...
10
+ @property
11
+ def response(self) -> Response: ...
12
+ @property
13
+ def status_code(self) -> int: ...
14
+ def __getitem__(self, item) -> Union[dict, list]: ...
15
+ def __iter__(self) -> Iterator: ...
16
+
17
+ # Yandex Direct management.
18
+ class YandexDirectClientResponse(YandexDirectBaseMethodsClientResponse):
19
+ def pages(
20
+ self, *, max_pages: int = None
21
+ ) -> Iterator["YandexDirectPageIteratorExecutor"]: ...
22
+ def items(self, *, max_items: int = None) -> Iterator[dict]: ...
23
+ def iter_items(
24
+ self, *, max_pages: int = None, max_items: int = None
25
+ ) -> Iterator[dict]: ...
26
+ def extract(self) -> List[dict]: ...
27
+
28
+ class YandexDirectClientExecutorResponse(YandexDirectBaseMethodsClientResponse):
29
+ def __call__(self) -> YandexDirectClientResponse: ...
30
+
31
+ class YandexDirectClientExecutor:
32
+ def open_docs(self) -> YandexDirectClientExecutor:
33
+ """Open API official docs of resource in browser."""
34
+ def open_in_browser(self) -> YandexDirectClientExecutor:
35
+ """Send a request in the browser."""
36
+ def help(self) -> YandexDirectClientExecutor:
37
+ """Print docs of resource."""
38
+ def get(
39
+ self, *, params: dict = None, data: dict = None, headers: dict = None
40
+ ) -> YandexDirectClientExecutorResponse:
41
+ """
42
+ Send HTTP 'GET' request.
43
+
44
+ :param params: querystring arguments in the URL
45
+ :param data: send data in the body of the request
46
+ """
47
+ def post(
48
+ self, *, params: dict = None, data: dict = None, headers: dict = None
49
+ ) -> YandexDirectClientExecutorResponse:
50
+ """
51
+ Send HTTP 'POST' request.
52
+
53
+ :param params: querystring arguments in the URL
54
+ :param data: send data in the body of the request
55
+ """
56
+
57
+ class YandexDirectPageIteratorResponse(YandexDirectBaseMethodsClientResponse):
58
+ def items(self, *, max_items: int = None) -> Iterator[dict]: ...
59
+
60
+ class YandexDirectPageIteratorExecutor(YandexDirectBaseMethodsClientResponse):
61
+ def __call__(self) -> YandexDirectPageIteratorResponse: ...
62
+
63
+ # Yandex Direct reports.
64
+ class YandexDirectClientReportResponse(YandexDirectBaseMethodsClientResponse):
65
+ def iter_lines(self) -> Iterator[str]: ...
66
+ def iter_values(self) -> Iterator[list]: ...
67
+ def iter_dicts(self) -> Iterator[dict]: ...
68
+ def to_lines(self) -> List[str]: ...
69
+ def to_values(self) -> List[list]: ...
70
+ def to_columns(self) -> List[list]: ...
71
+ def to_dicts(self) -> List[dict]: ...
72
+
73
+ class YandexDirectClientReportExecutorResponse(YandexDirectBaseMethodsClientResponse):
74
+ def __call__(self) -> YandexDirectClientReportResponse: ...
75
+ @property
76
+ def columns(self) -> List[str]: ...
77
+
78
+ class YandexDirectClientReportExecutor:
79
+ def open_docs(self) -> YandexDirectClientReportExecutor:
80
+ """Open API official docs of resource in browser."""
81
+ def open_in_browser(self) -> YandexDirectClientReportExecutor:
82
+ """Send a request in the browser."""
83
+ def help(self) -> YandexDirectClientReportExecutor:
84
+ """Print docs of resource."""
85
+ def post(
86
+ self, *, params: dict = None, data: dict = None, headers: dict = None
87
+ ) -> YandexDirectClientReportExecutorResponse:
88
+ """
89
+ Send HTTP 'POST' request.
90
+
91
+ :param params: querystring arguments in the URL
92
+ :param data: send data in the body of the request
93
+ """
94
+
95
+ # Main.
96
+ class YandexDirect:
97
+ def __init__(
98
+ self,
99
+ *,
100
+ access_token: str,
101
+ login: str = None,
102
+ is_sandbox: bool = False,
103
+ retry_if_not_enough_units: bool = False,
104
+ retry_if_exceeded_limit: bool = True,
105
+ retries_if_server_error: int = 5,
106
+ language: str = None,
107
+ processing_mode: str = "offline",
108
+ wait_report: bool = True,
109
+ return_money_in_micros: bool = False,
110
+ skip_report_header: bool = True,
111
+ skip_column_header: bool = False,
112
+ skip_report_summary: bool = True,
113
+ ):
114
+ """
115
+ Official documentation of the reports resource: https://yandex.ru/dev/direct/doc/ref-v5/concepts/about.html
116
+ Official documentation of other resources: https://yandex.ru/dev/direct/doc/reports/how-to.html
117
+
118
+ :param access_token: Access token.
119
+ :param login: If you are making inquiries from an agent account, you must be sure to specify the account login.
120
+ :param is_sandbox: Enable sandbox.
121
+ :param retry_if_not_enough_units: Repeat request when units run out
122
+ :param retry_if_exceeded_limit: Repeat the request if the limits on the number of reports or requests are exceeded.
123
+ :param retries_if_server_error: Number of retries when server errors occur.
124
+ :param language: The language in which the data for directories and errors will be returned.
125
+
126
+ :param processing_mode: (report resource) Report generation mode: online, offline or auto.
127
+ :param wait_report: (report resource) When requesting a report, it will wait until the report is prepared and download it.
128
+ :param return_money_in_micros: (report resource) Monetary values in the report are returned in currency with an accuracy of two decimal places.
129
+ :param skip_report_header: (report resource) Do not display a line with the report name and date range in the report.
130
+ :param skip_column_header: (report resource) Do not display a line with field names in the report.
131
+ :param skip_report_summary: (report resource) Do not display a line with the number of statistics lines in the report.
132
+ """
133
+ def reports(self) -> YandexDirectClientReportExecutor: ...
134
+ def adextensions(self) -> YandexDirectClientExecutor: ...
135
+ def adgroups(self) -> YandexDirectClientExecutor: ...
136
+ def adimages(self) -> YandexDirectClientExecutor: ...
137
+ def advideos(self) -> YandexDirectClientExecutor: ...
138
+ def ads(self) -> YandexDirectClientExecutor: ...
139
+ def agencyclients(self) -> YandexDirectClientExecutor: ...
140
+ def audiencetargets(self) -> YandexDirectClientExecutor: ...
141
+ def bidmodifiers(self) -> YandexDirectClientExecutor: ...
142
+ def bids(self) -> YandexDirectClientExecutor: ...
143
+ def businesses(self) -> YandexDirectClientExecutor: ...
144
+ def campaigns(self) -> YandexDirectClientExecutor: ...
145
+ def changes(self) -> YandexDirectClientExecutor: ...
146
+ def clients(self) -> YandexDirectClientExecutor: ...
147
+ def creatives(self) -> YandexDirectClientExecutor: ...
148
+ def debugtoken(self, *, client_id: str) -> YandexDirectClientExecutor: ...
149
+ def dictionaries(self) -> YandexDirectClientExecutor: ...
150
+ def dynamicads(self) -> YandexDirectClientExecutor: ...
151
+ def dynamicfeedadtargets(self) -> YandexDirectClientExecutor: ...
152
+ def feeds(self) -> YandexDirectClientExecutor: ...
153
+ def keywordbids(self) -> YandexDirectClientExecutor: ...
154
+ def keywords(self) -> YandexDirectClientExecutor: ...
155
+ def keywordsresearch(self) -> YandexDirectClientExecutor: ...
156
+ def leads(self) -> YandexDirectClientExecutor: ...
157
+ def negativekeywordsharedsets(self) -> YandexDirectClientExecutor: ...
158
+ def retargeting(self) -> YandexDirectClientExecutor: ...
159
+ def sitelinks(self) -> YandexDirectClientExecutor: ...
160
+ def smartadtargets(self) -> YandexDirectClientExecutor: ...
161
+ def strategies(self) -> YandexDirectClientExecutor: ...
162
+ def turbopages(self) -> YandexDirectClientExecutor: ...
163
+ def vcards(self) -> YandexDirectClientExecutor: ...
@@ -0,0 +1,9 @@
1
+ from .adapter import V4LiveClientAdapter, YandexDirectV4Live
2
+ from .resource_mapping import RESOURCE_MAPPING_V4_LIVE, SUPPORTED_V4_METHODS
3
+
4
+ __all__ = [
5
+ "V4LiveClientAdapter",
6
+ "YandexDirectV4Live",
7
+ "RESOURCE_MAPPING_V4_LIVE",
8
+ "SUPPORTED_V4_METHODS",
9
+ ]