direct-cli 0.3.0__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 (194) hide show
  1. {direct_cli-0.3.0 → direct_cli-0.3.1}/PKG-INFO +21 -1
  2. {direct_cli-0.3.0 → direct_cli-0.3.1}/README.md +20 -0
  3. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/_smoke_probes.py +8 -3
  4. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/_vendor/tapi_yandex_direct/__init__.py +1 -1
  5. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.py +7 -5
  6. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/api.py +53 -0
  7. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/cli.py +18 -0
  8. direct_cli-0.3.1/direct_cli/commands/balance.py +55 -0
  9. direct_cli-0.3.1/direct_cli/commands/v4goals.py +103 -0
  10. direct_cli-0.3.1/direct_cli/commands/v4shells.py +40 -0
  11. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/smoke_matrix.py +11 -5
  12. direct_cli-0.3.1/direct_cli/v4.py +17 -0
  13. direct_cli-0.3.1/direct_cli/v4_contracts.py +433 -0
  14. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli.egg-info/PKG-INFO +21 -1
  15. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli.egg-info/SOURCES.txt +10 -0
  16. {direct_cli-0.3.0 → direct_cli-0.3.1}/pyproject.toml +2 -1
  17. {direct_cli-0.3.0 → direct_cli-0.3.1}/scripts/test_safe_commands.sh +4 -1
  18. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/api_coverage_payloads.py +2 -0
  19. direct_cli-0.3.1/tests/test_balance.py +137 -0
  20. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/test_comprehensive.py +8 -0
  21. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/test_dry_run.py +2 -2
  22. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/test_integration.py +3 -4
  23. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/test_smoke_matrix.py +21 -8
  24. direct_cli-0.3.1/tests/test_v4_contracts.py +141 -0
  25. direct_cli-0.3.1/tests/test_v4_foundation.py +91 -0
  26. direct_cli-0.3.1/tests/test_v4_live_contracts.py +79 -0
  27. direct_cli-0.3.1/tests/test_v4goals.py +190 -0
  28. {direct_cli-0.3.0 → direct_cli-0.3.1}/.env.example +0 -0
  29. {direct_cli-0.3.0 → direct_cli-0.3.1}/.github/copilot-instructions.md +0 -0
  30. {direct_cli-0.3.0 → direct_cli-0.3.1}/.github/workflows/api-coverage.yml +0 -0
  31. {direct_cli-0.3.0 → direct_cli-0.3.1}/.github/workflows/claude-code-review.yml +0 -0
  32. {direct_cli-0.3.0 → direct_cli-0.3.1}/.github/workflows/claude.yml +0 -0
  33. {direct_cli-0.3.0 → direct_cli-0.3.1}/.gitignore +0 -0
  34. {direct_cli-0.3.0 → direct_cli-0.3.1}/AGENTS.md +0 -0
  35. {direct_cli-0.3.0 → direct_cli-0.3.1}/CLAUDE.md +0 -0
  36. {direct_cli-0.3.0 → direct_cli-0.3.1}/MANIFEST.in +0 -0
  37. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/__init__.py +0 -0
  38. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/_deprecated.py +0 -0
  39. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/_vendor/__init__.py +0 -0
  40. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/_vendor/tapi_yandex_direct/exceptions.py +0 -0
  41. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/_vendor/tapi_yandex_direct/resource_mapping.py +0 -0
  42. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.py +0 -0
  43. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.pyi +0 -0
  44. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/_vendor/tapi_yandex_direct/v4/__init__.py +0 -0
  45. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.pyi +0 -0
  46. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/_vendor/tapi_yandex_direct/v4/resource_mapping.py +0 -0
  47. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/auth.py +0 -0
  48. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/__init__.py +0 -0
  49. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/adextensions.py +0 -0
  50. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/adgroups.py +0 -0
  51. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/adimages.py +0 -0
  52. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/ads.py +0 -0
  53. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/advideos.py +0 -0
  54. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/agencyclients.py +0 -0
  55. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/audiencetargets.py +0 -0
  56. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/auth.py +0 -0
  57. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/bidmodifiers.py +0 -0
  58. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/bids.py +0 -0
  59. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/businesses.py +0 -0
  60. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/campaigns.py +0 -0
  61. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/changes.py +0 -0
  62. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/clients.py +0 -0
  63. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/creatives.py +0 -0
  64. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/dictionaries.py +0 -0
  65. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/dynamicads.py +0 -0
  66. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/dynamicfeedadtargets.py +0 -0
  67. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/feeds.py +0 -0
  68. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/keywordbids.py +0 -0
  69. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/keywords.py +0 -0
  70. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/keywordsresearch.py +0 -0
  71. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/leads.py +0 -0
  72. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/negativekeywordsharedsets.py +0 -0
  73. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/reports.py +0 -0
  74. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/retargeting.py +0 -0
  75. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/sitelinks.py +0 -0
  76. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/smartadtargets.py +0 -0
  77. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/strategies.py +0 -0
  78. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/turbopages.py +0 -0
  79. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/commands/vcards.py +0 -0
  80. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/output.py +0 -0
  81. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/reports_coverage.py +0 -0
  82. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/utils.py +0 -0
  83. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli/wsdl_coverage.py +0 -0
  84. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli.egg-info/dependency_links.txt +0 -0
  85. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli.egg-info/entry_points.txt +0 -0
  86. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli.egg-info/requires.txt +0 -0
  87. {direct_cli-0.3.0 → direct_cli-0.3.1}/direct_cli.egg-info/top_level.txt +0 -0
  88. {direct_cli-0.3.0 → direct_cli-0.3.1}/docs/superpowers/plans/2026-04-12-issue-32-completion.md +0 -0
  89. {direct_cli-0.3.0 → direct_cli-0.3.1}/docs/superpowers/specs/2026-04-23-vendor-tapi-yandex-direct-design.md +0 -0
  90. {direct_cli-0.3.0 → direct_cli-0.3.1}/scripts/anonymize_cassettes.py +0 -0
  91. {direct_cli-0.3.0 → direct_cli-0.3.1}/scripts/build_api_coverage_checklist.py +0 -0
  92. {direct_cli-0.3.0 → direct_cli-0.3.1}/scripts/build_api_coverage_report.py +0 -0
  93. {direct_cli-0.3.0 → direct_cli-0.3.1}/scripts/check_reports_drift.py +0 -0
  94. {direct_cli-0.3.0 → direct_cli-0.3.1}/scripts/check_wsdl_drift.py +0 -0
  95. {direct_cli-0.3.0 → direct_cli-0.3.1}/scripts/patch_vendor_imports.py +0 -0
  96. {direct_cli-0.3.0 → direct_cli-0.3.1}/scripts/refresh_reports_cache.py +0 -0
  97. {direct_cli-0.3.0 → direct_cli-0.3.1}/scripts/refresh_wsdl_cache.py +0 -0
  98. {direct_cli-0.3.0 → direct_cli-0.3.1}/scripts/release_pypi.sh +0 -0
  99. {direct_cli-0.3.0 → direct_cli-0.3.1}/scripts/sandbox_write_live.py +0 -0
  100. {direct_cli-0.3.0 → direct_cli-0.3.1}/scripts/test_dangerous_commands.sh +0 -0
  101. {direct_cli-0.3.0 → direct_cli-0.3.1}/scripts/test_sandbox_write.sh +0 -0
  102. {direct_cli-0.3.0 → direct_cli-0.3.1}/scripts/update_vendor.sh +0 -0
  103. {direct_cli-0.3.0 → direct_cli-0.3.1}/setup.cfg +0 -0
  104. {direct_cli-0.3.0 → direct_cli-0.3.1}/setup.py +0 -0
  105. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/API_COVERAGE.md +0 -0
  106. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/API_ISSUE_AUDIT.md +0 -0
  107. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/MANUAL_COVERAGE.md +0 -0
  108. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/__init__.py +0 -0
  109. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_adgroups_add_update_delete.yaml +0 -0
  110. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_adimages_add_get_delete.yaml +0 -0
  111. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_ads_add_update_delete.yaml +0 -0
  112. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_ads_suspend_resume_archive_unarchive.yaml +0 -0
  113. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_advideos_add_get.yaml +0 -0
  114. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_audiencetargets_add_delete.yaml +0 -0
  115. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_audiencetargets_suspend_resume.yaml +0 -0
  116. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_bids_set.yaml +0 -0
  117. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_campaign_create_get_delete.yaml +0 -0
  118. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_creatives_chain_advideo_to_creative.yaml +0 -0
  119. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_dynamicads_add_delete.yaml +0 -0
  120. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_dynamicads_suspend_resume.yaml +0 -0
  121. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_keywordbids_set.yaml +0 -0
  122. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_keywords_add_update_delete.yaml +0 -0
  123. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_keywords_suspend_resume.yaml +0 -0
  124. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_sitelinks_add_get_delete.yaml +0 -0
  125. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_smartadtargets_add_update_delete.yaml +0 -0
  126. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_live_write/test_live_draft_smartadtargets_suspend_resume.yaml +0 -0
  127. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteAdExtensions.test_add_delete.yaml +0 -0
  128. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteAdGroups.test_add_update_delete.yaml +0 -0
  129. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteAdImages.test_add_delete.yaml +0 -0
  130. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteAds.test_add_text_ad_update_delete.yaml +0 -0
  131. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteAudienceTargets.test_add_delete.yaml +0 -0
  132. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteBidModifiersAdd.test_add_delete_mobile.yaml +0 -0
  133. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteBidModifiersSet.test_set_without_id_is_rejected.yaml +0 -0
  134. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteBids.test_set_bid.yaml +0 -0
  135. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteCampaignDraftLifecycle.test_draft_create_get_delete.yaml +0 -0
  136. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteCampaigns.test_campaign_lifecycle.yaml +0 -0
  137. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteDynamicAds.test_add_update_delete.yaml +0 -0
  138. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteFeeds.test_add_update_delete.yaml +0 -0
  139. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteKeywordBids.test_set_keyword_bid.yaml +0 -0
  140. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteKeywords.test_add_update_delete.yaml +0 -0
  141. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteNegativeKeywordSharedSets.test_add_update_delete.yaml +0 -0
  142. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteRetargeting.test_add_delete.yaml +0 -0
  143. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteSitelinks.test_add_delete.yaml +0 -0
  144. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteSmartAdTargets.test_add_update_delete.yaml +0 -0
  145. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/cassettes/test_integration_write/TestWriteVCards.test_add_delete.yaml +0 -0
  146. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/conftest.py +0 -0
  147. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/fixtures/test-video.mp4 +0 -0
  148. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/reports_cache/raw/fields-list.html +0 -0
  149. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/reports_cache/raw/headers.html +0 -0
  150. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/reports_cache/raw/spec.html +0 -0
  151. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/reports_cache/raw/type.html +0 -0
  152. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/reports_cache/spec.json +0 -0
  153. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/test_api_coverage.py +0 -0
  154. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/test_auth_bw.py +0 -0
  155. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/test_auth_oauth.py +0 -0
  156. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/test_auth_op.py +0 -0
  157. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/test_cli.py +0 -0
  158. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/test_integration_live_write.py +0 -0
  159. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/test_integration_write.py +0 -0
  160. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/test_reports_drift.py +0 -0
  161. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/test_transport_contract.py +0 -0
  162. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/test_vendor_imports.py +0 -0
  163. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/adextensions.xml +0 -0
  164. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/adgroups.xml +0 -0
  165. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/adimages.xml +0 -0
  166. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/ads.xml +0 -0
  167. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/advideos.xml +0 -0
  168. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/agencyclients.xml +0 -0
  169. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/audiencetargets.xml +0 -0
  170. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/bidmodifiers.xml +0 -0
  171. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/bids.xml +0 -0
  172. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/businesses.xml +0 -0
  173. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/campaigns.xml +0 -0
  174. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/changes.xml +0 -0
  175. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/clients.xml +0 -0
  176. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/creatives.xml +0 -0
  177. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/dictionaries.xml +0 -0
  178. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/dynamicfeedadtargets.xml +0 -0
  179. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/dynamictextadtargets.xml +0 -0
  180. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/feeds.xml +0 -0
  181. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/imports/adextensiontypes.xsd +0 -0
  182. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/imports/general.xsd +0 -0
  183. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/imports/generalclients.xsd +0 -0
  184. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/keywordbids.xml +0 -0
  185. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/keywords.xml +0 -0
  186. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/keywordsresearch.xml +0 -0
  187. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/leads.xml +0 -0
  188. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/negativekeywordsharedsets.xml +0 -0
  189. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/retargetinglists.xml +0 -0
  190. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/sitelinks.xml +0 -0
  191. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/smartadtargets.xml +0 -0
  192. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/strategies.xml +0 -0
  193. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/turbopages.xml +0 -0
  194. {direct_cli-0.3.0 → direct_cli-0.3.1}/tests/wsdl_cache/vcards.xml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: direct-cli
3
- Version: 0.3.0
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.
@@ -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.
@@ -8,7 +8,11 @@ from typing import Any, Optional
8
8
 
9
9
  from .api import create_client
10
10
 
11
- VIDEO_CREATIVE_TYPES = {"VIDEO_EXTENSION_CREATIVE", "CPM_VIDEO_CREATIVE"}
11
+ VIDEO_CREATIVE_TYPES = {
12
+ "VIDEO_EXTENSION_CREATIVE",
13
+ "CPM_VIDEO_CREATIVE",
14
+ "CPC_VIDEO_CREATIVE",
15
+ }
12
16
 
13
17
 
14
18
  def _extract_items(response: Any) -> list[dict[str, Any]]:
@@ -18,7 +22,7 @@ def _extract_items(response: Any) -> list[dict[str, Any]]:
18
22
 
19
23
 
20
24
  def _advideo_get_accepts(client: Any, candidate: str) -> bool:
21
- """Return whether advideos.get accepts and returns the candidate ID."""
25
+ """Return whether advideos.get accepts the candidate ID."""
22
26
  body = {
23
27
  "method": "get",
24
28
  "params": {
@@ -28,7 +32,8 @@ def _advideo_get_accepts(client: Any, candidate: str) -> bool:
28
32
  },
29
33
  }
30
34
  try:
31
- return bool(_extract_items(client.advideos().post(data=body)))
35
+ _extract_items(client.advideos().post(data=body))
36
+ return True
32
37
  except Exception:
33
38
  return False
34
39
 
@@ -1,7 +1,7 @@
1
1
 
2
2
  __author__ = 'Pavel Maksimov'
3
3
  __email__ = 'vur21@ya.ru'
4
- __version__ = '2026.4.27'
4
+ __version__ = '2026.4.28'
5
5
 
6
6
 
7
7
  from .resource_mapping import *
@@ -44,9 +44,10 @@ class V4LiveClientAdapter(JSONAdapterMixin, TapiAdapter):
44
44
  login = api_params.get("login")
45
45
  language = api_params.get("language", "en")
46
46
 
47
- # Enrich the JSON body with token / locale / login (param.login for
48
- # agent calls). format_data_to_request does not see api_params, so we
49
- # do this here, after super() has already serialised the user data.
47
+ # Enrich the JSON body with token / locale. format_data_to_request does
48
+ # not see api_params, so we do this here, after super() has already
49
+ # serialised the user data. Agency/client selection is transport-level
50
+ # (Client-Login header); method params must stay schema-shaped.
50
51
  raw = params.get("data")
51
52
  if raw:
52
53
  if isinstance(raw, (bytes, bytearray)):
@@ -66,14 +67,15 @@ class V4LiveClientAdapter(JSONAdapterMixin, TapiAdapter):
66
67
  body.setdefault("token", token)
67
68
  if language:
68
69
  body.setdefault("locale", language)
69
- if login and isinstance(body.get("param"), dict):
70
- body["param"].setdefault("login", login)
71
70
 
72
71
  params["data"] = orjson.dumps(body)
73
72
 
74
73
  if token:
75
74
  params["headers"]["Authorization"] = "Bearer {}".format(token)
76
75
 
76
+ if login:
77
+ params["headers"]["Client-Login"] = login
78
+
77
79
  return params
78
80
 
79
81
  def format_data_to_request(self, data) -> Optional[bytes]:
@@ -5,6 +5,7 @@ API client wrapper for Direct CLI
5
5
  from typing import Optional, Dict, Any, List
6
6
 
7
7
  from direct_cli._vendor.tapi_yandex_direct import YandexDirect
8
+ from direct_cli._vendor.tapi_yandex_direct.v4 import YandexDirectV4Live
8
9
  from .auth import get_credentials
9
10
 
10
11
 
@@ -67,6 +68,58 @@ def create_client(
67
68
  )
68
69
 
69
70
 
71
+ def create_v4_client(
72
+ token: Optional[str] = None,
73
+ login: Optional[str] = None,
74
+ profile: Optional[str] = None,
75
+ sandbox: bool = False,
76
+ op_token_ref: Optional[str] = None,
77
+ op_login_ref: Optional[str] = None,
78
+ bw_token_ref: Optional[str] = None,
79
+ bw_login_ref: Optional[str] = None,
80
+ language: Optional[str] = None,
81
+ retry_if_exceeded_limit: bool = True,
82
+ retries_if_server_error: int = 5,
83
+ ) -> YandexDirectV4Live:
84
+ """
85
+ Create YandexDirect v4 Live client.
86
+
87
+ Args:
88
+ token: API access token
89
+ login: Client login (for agency accounts)
90
+ profile: Credential profile name
91
+ sandbox: Use sandbox API
92
+ op_token_ref: 1Password secret reference for token
93
+ op_login_ref: 1Password secret reference for login
94
+ bw_token_ref: Bitwarden item name/ID for token
95
+ bw_login_ref: Bitwarden item name/ID for login
96
+ language: API locale
97
+ retry_if_exceeded_limit: Retry when the API limit is exceeded
98
+ retries_if_server_error: Number of retries for server errors
99
+
100
+ Returns:
101
+ YandexDirect v4 Live client instance
102
+ """
103
+ final_token, final_login = get_credentials(
104
+ token,
105
+ login,
106
+ profile=profile,
107
+ op_token_ref=op_token_ref,
108
+ op_login_ref=op_login_ref,
109
+ bw_token_ref=bw_token_ref,
110
+ bw_login_ref=bw_login_ref,
111
+ )
112
+
113
+ return YandexDirectV4Live(
114
+ access_token=final_token,
115
+ login=final_login,
116
+ is_sandbox=sandbox,
117
+ language=language or "en",
118
+ retry_if_exceeded_limit=retry_if_exceeded_limit,
119
+ retries_if_server_error=retries_if_server_error,
120
+ )
121
+
122
+
70
123
  def fetch_all_pages(
71
124
  client: YandexDirect,
72
125
  resource_name: str,
@@ -41,6 +41,16 @@ from .commands.advideos import advideos
41
41
  from .commands.dynamicfeedadtargets import dynamicfeedadtargets
42
42
  from .commands.strategies import strategies
43
43
  from .commands.auth import auth
44
+ from .commands.balance import balance
45
+ from .commands.v4shells import (
46
+ v4account,
47
+ v4events,
48
+ v4finance,
49
+ v4forecast,
50
+ v4meta,
51
+ v4wordstat,
52
+ )
53
+ from .commands.v4goals import v4goals
44
54
 
45
55
  # Load .env file
46
56
  load_dotenv()
@@ -173,6 +183,14 @@ for command in (
173
183
  advideos,
174
184
  dynamicfeedadtargets,
175
185
  strategies,
186
+ balance,
187
+ v4finance,
188
+ v4account,
189
+ v4goals,
190
+ v4events,
191
+ v4wordstat,
192
+ v4forecast,
193
+ v4meta,
176
194
  auth,
177
195
  ):
178
196
  _register_command(command)
@@ -0,0 +1,55 @@
1
+ """Money balance command backed by Yandex Direct v4 Live."""
2
+
3
+ import click
4
+
5
+ from ..api import create_v4_client
6
+ from ..output import format_output, print_error
7
+ from ..utils import parse_csv_strings
8
+ from ..v4 import build_v4_body, call_v4
9
+ from ..v4_contracts import v4_method_contract
10
+
11
+
12
+ @v4_method_contract("AccountManagement")
13
+ @click.command()
14
+ @click.option("--logins", help="Comma-separated client logins")
15
+ @click.option(
16
+ "--format",
17
+ "output_format",
18
+ default="json",
19
+ type=click.Choice(["json", "table", "csv", "tsv"]),
20
+ help="Output format",
21
+ )
22
+ @click.option("--output", help="Output file")
23
+ @click.option("--dry-run", is_flag=True, help="Show request without sending")
24
+ @click.pass_context
25
+ def balance(ctx, logins, output_format, output, dry_run):
26
+ """Check account money balance."""
27
+ login_list = parse_csv_strings(logins)
28
+ configured_login = ctx.obj.get("login")
29
+ if not login_list and configured_login:
30
+ login_list = [configured_login]
31
+
32
+ param = {
33
+ "Action": "Get",
34
+ }
35
+ if login_list:
36
+ param["SelectionCriteria"] = {"Logins": login_list}
37
+ elif ctx.obj.get("sandbox"):
38
+ raise click.UsageError("Provide --logins or configure YANDEX_DIRECT_LOGIN")
39
+
40
+ if dry_run:
41
+ format_output(build_v4_body("AccountManagement", param), "json", None)
42
+ return
43
+
44
+ try:
45
+ client = create_v4_client(
46
+ token=ctx.obj.get("token"),
47
+ login=ctx.obj.get("login"),
48
+ profile=ctx.obj.get("profile"),
49
+ sandbox=ctx.obj.get("sandbox"),
50
+ )
51
+ data = call_v4(client, "AccountManagement", param)
52
+ format_output(data, output_format, output)
53
+ except Exception as e:
54
+ print_error(str(e))
55
+ raise click.Abort()
@@ -0,0 +1,103 @@
1
+ """Yandex Direct v4 Live goals commands."""
2
+
3
+ import click
4
+
5
+ from ..api import create_v4_client
6
+ from ..output import format_output, print_error
7
+ from ..utils import parse_ids
8
+ from ..v4 import build_v4_body, call_v4
9
+ from ..v4_contracts import v4_method_contract
10
+ from .v4shells import V4_EPILOG
11
+
12
+
13
+ def _campaign_ids_param(campaign_ids: str) -> dict:
14
+ """Build the v4 Live CampaignIDInfo parameter."""
15
+ try:
16
+ ids = parse_ids(campaign_ids)
17
+ except ValueError as exc:
18
+ raise click.UsageError(str(exc))
19
+ if not ids:
20
+ raise click.UsageError("--campaign-ids must not be empty")
21
+ return {"CampaignIDS": ids}
22
+
23
+
24
+ def _run_goals_command(
25
+ ctx,
26
+ method: str,
27
+ campaign_ids: str,
28
+ output_format: str,
29
+ output: str,
30
+ dry_run: bool,
31
+ ) -> None:
32
+ param = _campaign_ids_param(campaign_ids)
33
+ if dry_run:
34
+ format_output(build_v4_body(method, param), "json", None)
35
+ return
36
+
37
+ try:
38
+ client = create_v4_client(
39
+ token=ctx.obj.get("token"),
40
+ login=ctx.obj.get("login"),
41
+ profile=ctx.obj.get("profile"),
42
+ sandbox=ctx.obj.get("sandbox"),
43
+ )
44
+ data = call_v4(client, method, param)
45
+ format_output(data, output_format, output)
46
+ except Exception as e:
47
+ print_error(str(e))
48
+ raise click.Abort()
49
+
50
+
51
+ @click.group(epilog=V4_EPILOG)
52
+ def v4goals():
53
+ """Yandex Direct v4 Live goals commands."""
54
+
55
+
56
+ @v4_method_contract("GetStatGoals")
57
+ @v4goals.command(name="get-stat-goals")
58
+ @click.option("--campaign-ids", required=True, help="Comma-separated campaign IDs")
59
+ @click.option(
60
+ "--format",
61
+ "output_format",
62
+ default="json",
63
+ type=click.Choice(["json", "table", "csv", "tsv"]),
64
+ help="Output format",
65
+ )
66
+ @click.option("--output", help="Output file")
67
+ @click.option("--dry-run", is_flag=True, help="Show request without sending")
68
+ @click.pass_context
69
+ def get_stat_goals(ctx, campaign_ids, output_format, output, dry_run):
70
+ """Get Yandex Metrica goals available for campaigns."""
71
+ _run_goals_command(
72
+ ctx,
73
+ "GetStatGoals",
74
+ campaign_ids,
75
+ output_format,
76
+ output,
77
+ dry_run,
78
+ )
79
+
80
+
81
+ @v4_method_contract("GetRetargetingGoals")
82
+ @v4goals.command(name="get-retargeting-goals")
83
+ @click.option("--campaign-ids", required=True, help="Comma-separated campaign IDs")
84
+ @click.option(
85
+ "--format",
86
+ "output_format",
87
+ default="json",
88
+ type=click.Choice(["json", "table", "csv", "tsv"]),
89
+ help="Output format",
90
+ )
91
+ @click.option("--output", help="Output file")
92
+ @click.option("--dry-run", is_flag=True, help="Show request without sending")
93
+ @click.pass_context
94
+ def get_retargeting_goals(ctx, campaign_ids, output_format, output, dry_run):
95
+ """Get retargeting goals for campaigns."""
96
+ _run_goals_command(
97
+ ctx,
98
+ "GetRetargetingGoals",
99
+ campaign_ids,
100
+ output_format,
101
+ output,
102
+ dry_run,
103
+ )
@@ -0,0 +1,40 @@
1
+ """Shell command groups for Yandex Direct v4 Live command families."""
2
+
3
+ import click
4
+
5
+ V4_EPILOG = (
6
+ "\b\n"
7
+ "V4 Live commands use typed flags only. Supported methods are listed in "
8
+ "the Yandex Direct v4 Live documentation: "
9
+ "https://yandex.com/dev/direct/doc/dg-v4/en/live/concepts"
10
+ )
11
+
12
+
13
+ @click.group(epilog=V4_EPILOG)
14
+ def v4finance():
15
+ """Yandex Direct v4 Live finance commands."""
16
+
17
+
18
+ @click.group(epilog=V4_EPILOG)
19
+ def v4account():
20
+ """Yandex Direct v4 Live account commands."""
21
+
22
+
23
+ @click.group(epilog=V4_EPILOG)
24
+ def v4events():
25
+ """Yandex Direct v4 Live events commands."""
26
+
27
+
28
+ @click.group(epilog=V4_EPILOG)
29
+ def v4wordstat():
30
+ """Yandex Direct v4 Live wordstat commands."""
31
+
32
+
33
+ @click.group(epilog=V4_EPILOG)
34
+ def v4forecast():
35
+ """Yandex Direct v4 Live forecast commands."""
36
+
37
+
38
+ @click.group(epilog=V4_EPILOG)
39
+ def v4meta():
40
+ """Yandex Direct v4 Live metadata commands."""
@@ -19,6 +19,7 @@ DANGEROUS = "DANGEROUS"
19
19
 
20
20
  SMOKE_MATRIX = {
21
21
  SAFE: [
22
+ "balance",
22
23
  "adextensions.get",
23
24
  "adgroups.get",
24
25
  "adimages.get",
@@ -56,6 +57,8 @@ SMOKE_MATRIX = {
56
57
  "smartadtargets.get",
57
58
  "strategies.get",
58
59
  "turbopages.get",
60
+ "v4goals.get-retargeting-goals",
61
+ "v4goals.get-stat-goals",
59
62
  "vcards.get",
60
63
  ],
61
64
  WRITE_SANDBOX: [
@@ -190,11 +193,14 @@ def commands_for_category(category: str) -> list[str]:
190
193
  def _registered_cli_commands() -> set[str]:
191
194
  from direct_cli.cli import cli
192
195
 
193
- return {
194
- command_key(group_name, command_name)
195
- for group_name, group in cli.commands.items()
196
- for command_name in getattr(group, "commands", {})
197
- }
196
+ registered = set()
197
+ for group_name, group in cli.commands.items():
198
+ if hasattr(group, "commands"):
199
+ for command_name in group.commands:
200
+ registered.add(command_key(group_name, command_name))
201
+ else:
202
+ registered.add(group_name)
203
+ return registered
198
204
 
199
205
 
200
206
  def _wsdl_operations_count() -> int:
@@ -0,0 +1,17 @@
1
+ """Shared helpers for Yandex Direct v4 Live commands."""
2
+
3
+ from typing import Any, Optional
4
+
5
+
6
+ def build_v4_body(method: str, param: Optional[Any] = None) -> dict:
7
+ """Build a v4 Live request body."""
8
+ body = {"method": method}
9
+ if param is not None:
10
+ body["param"] = param
11
+ return body
12
+
13
+
14
+ def call_v4(client: Any, method: str, param: Optional[Any] = None) -> Any:
15
+ """Call one v4 Live method and return the extracted response payload."""
16
+ result = client.v4live().post(data=build_v4_body(method, param))
17
+ return result().extract()