direct-cli 0.2.9.2__tar.gz → 0.2.11__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 (174) hide show
  1. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/.github/copilot-instructions.md +1 -1
  2. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/.gitignore +1 -0
  3. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/CLAUDE.md +14 -5
  4. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/PKG-INFO +220 -61
  5. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/README.md +219 -60
  6. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/_vendor/tapi_yandex_direct/__init__.py +1 -1
  7. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/_vendor/tapi_yandex_direct/resource_mapping.py +38 -1
  8. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.py +2 -2
  9. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/auth.py +19 -0
  10. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/cli.py +46 -31
  11. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/agencyclients.py +32 -14
  12. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/audiencetargets.py +5 -5
  13. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/auth.py +16 -5
  14. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/bidmodifiers.py +22 -20
  15. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/bids.py +5 -5
  16. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/businesses.py +1 -1
  17. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/campaigns.py +8 -8
  18. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/changes.py +5 -8
  19. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/dynamicads.py +9 -9
  20. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/dynamicfeedadtargets.py +9 -9
  21. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/feeds.py +3 -6
  22. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/keywordbids.py +24 -20
  23. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/keywords.py +5 -65
  24. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/keywordsresearch.py +24 -3
  25. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/leads.py +8 -6
  26. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/smartadtargets.py +13 -13
  27. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/strategies.py +1 -1
  28. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/turbopages.py +1 -1
  29. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/reports_coverage.py +15 -5
  30. direct_cli-0.2.11/direct_cli/smoke_matrix.py +252 -0
  31. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/utils.py +50 -11
  32. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/wsdl_coverage.py +0 -6
  33. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli.egg-info/PKG-INFO +220 -61
  34. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli.egg-info/SOURCES.txt +9 -0
  35. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/pyproject.toml +1 -1
  36. direct_cli-0.2.11/scripts/build_api_coverage_checklist.py +444 -0
  37. direct_cli-0.2.11/scripts/patch_vendor_imports.py +142 -0
  38. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/scripts/release_pypi.sh +1 -1
  39. direct_cli-0.2.11/scripts/sandbox_write_live.py +1324 -0
  40. direct_cli-0.2.11/scripts/test_dangerous_commands.sh +50 -0
  41. direct_cli-0.2.11/scripts/test_safe_commands.sh +231 -0
  42. direct_cli-0.2.11/scripts/test_sandbox_write.sh +23 -0
  43. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/scripts/update_vendor.sh +11 -0
  44. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/API_COVERAGE.md +34 -0
  45. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/MANUAL_COVERAGE.md +8 -5
  46. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/conftest.py +19 -5
  47. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/test_api_coverage.py +29 -15
  48. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/test_auth_bw.py +14 -7
  49. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/test_auth_op.py +12 -6
  50. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/test_cli.py +36 -3
  51. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/test_comprehensive.py +7 -4
  52. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/test_dry_run.py +119 -79
  53. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/test_integration.py +211 -25
  54. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/test_integration_live_write.py +16 -15
  55. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/test_integration_write.py +8 -4
  56. direct_cli-0.2.11/tests/test_smoke_matrix.py +224 -0
  57. direct_cli-0.2.11/tests/test_vendor_imports.py +150 -0
  58. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/.env.example +0 -0
  59. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/.github/workflows/api-coverage.yml +0 -0
  60. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/.github/workflows/claude-code-review.yml +0 -0
  61. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/.github/workflows/claude.yml +0 -0
  62. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/AGENTS.md +0 -0
  63. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/MANIFEST.in +0 -0
  64. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/__init__.py +0 -0
  65. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/_deprecated.py +0 -0
  66. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/_vendor/__init__.py +0 -0
  67. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/_vendor/tapi_yandex_direct/exceptions.py +0 -0
  68. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/api.py +0 -0
  69. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/__init__.py +0 -0
  70. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/adextensions.py +0 -0
  71. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/adgroups.py +0 -0
  72. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/adimages.py +0 -0
  73. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/ads.py +0 -0
  74. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/advideos.py +0 -0
  75. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/clients.py +0 -0
  76. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/creatives.py +0 -0
  77. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/dictionaries.py +0 -0
  78. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/negativekeywordsharedsets.py +0 -0
  79. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/reports.py +0 -0
  80. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/retargeting.py +0 -0
  81. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/sitelinks.py +0 -0
  82. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/commands/vcards.py +0 -0
  83. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli/output.py +0 -0
  84. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli.egg-info/dependency_links.txt +0 -0
  85. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli.egg-info/entry_points.txt +0 -0
  86. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli.egg-info/requires.txt +0 -0
  87. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/direct_cli.egg-info/top_level.txt +0 -0
  88. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/docs/superpowers/plans/2026-04-12-issue-32-completion.md +0 -0
  89. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/docs/superpowers/specs/2026-04-23-vendor-tapi-yandex-direct-design.md +0 -0
  90. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/scripts/anonymize_cassettes.py +0 -0
  91. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/scripts/build_api_coverage_report.py +0 -0
  92. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/scripts/check_reports_drift.py +0 -0
  93. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/scripts/check_wsdl_drift.py +0 -0
  94. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/scripts/refresh_reports_cache.py +0 -0
  95. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/scripts/refresh_wsdl_cache.py +0 -0
  96. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/setup.cfg +0 -0
  97. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/setup.py +0 -0
  98. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/API_ISSUE_AUDIT.md +0 -0
  99. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/__init__.py +0 -0
  100. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_adgroups_add_update_delete.yaml +0 -0
  101. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_adimages_add_get_delete.yaml +0 -0
  102. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_ads_add_update_delete.yaml +0 -0
  103. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_ads_suspend_resume_archive_unarchive.yaml +0 -0
  104. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_advideos_add_get.yaml +0 -0
  105. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_audiencetargets_add_delete.yaml +0 -0
  106. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_audiencetargets_suspend_resume.yaml +0 -0
  107. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_bids_set.yaml +0 -0
  108. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_campaign_create_get_delete.yaml +0 -0
  109. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_creatives_chain_advideo_to_creative.yaml +0 -0
  110. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_dynamicads_add_delete.yaml +0 -0
  111. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_dynamicads_suspend_resume.yaml +0 -0
  112. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_keywordbids_set.yaml +0 -0
  113. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_keywords_add_update_delete.yaml +0 -0
  114. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_keywords_suspend_resume.yaml +0 -0
  115. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_sitelinks_add_get_delete.yaml +0 -0
  116. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_smartadtargets_add_update_delete.yaml +0 -0
  117. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_live_write/test_live_draft_smartadtargets_suspend_resume.yaml +0 -0
  118. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteAdExtensions.test_add_delete.yaml +0 -0
  119. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteAdGroups.test_add_update_delete.yaml +0 -0
  120. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteAdImages.test_add_delete.yaml +0 -0
  121. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteAds.test_add_text_ad_update_delete.yaml +0 -0
  122. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteAudienceTargets.test_add_delete.yaml +0 -0
  123. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteBidModifiersAdd.test_add_delete_mobile.yaml +0 -0
  124. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteBidModifiersSet.test_set_without_id_is_rejected.yaml +0 -0
  125. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteBids.test_set_bid.yaml +0 -0
  126. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteCampaignDraftLifecycle.test_draft_create_get_delete.yaml +0 -0
  127. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteCampaigns.test_campaign_lifecycle.yaml +0 -0
  128. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteDynamicAds.test_add_update_delete.yaml +0 -0
  129. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteFeeds.test_add_update_delete.yaml +0 -0
  130. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteKeywordBids.test_set_keyword_bid.yaml +0 -0
  131. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteKeywords.test_add_update_delete.yaml +0 -0
  132. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteNegativeKeywordSharedSets.test_add_update_delete.yaml +0 -0
  133. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteRetargeting.test_add_delete.yaml +0 -0
  134. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteSitelinks.test_add_delete.yaml +0 -0
  135. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteSmartAdTargets.test_add_update_delete.yaml +0 -0
  136. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/cassettes/test_integration_write/TestWriteVCards.test_add_delete.yaml +0 -0
  137. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/fixtures/test-video.mp4 +0 -0
  138. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/reports_cache/raw/fields-list.html +0 -0
  139. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/reports_cache/raw/headers.html +0 -0
  140. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/reports_cache/raw/spec.html +0 -0
  141. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/reports_cache/raw/type.html +0 -0
  142. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/reports_cache/spec.json +0 -0
  143. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/test_auth_oauth.py +0 -0
  144. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/test_reports_drift.py +0 -0
  145. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/test_transport_contract.py +0 -0
  146. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/adextensions.xml +0 -0
  147. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/adgroups.xml +0 -0
  148. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/adimages.xml +0 -0
  149. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/ads.xml +0 -0
  150. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/advideos.xml +0 -0
  151. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/agencyclients.xml +0 -0
  152. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/audiencetargets.xml +0 -0
  153. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/bidmodifiers.xml +0 -0
  154. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/bids.xml +0 -0
  155. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/businesses.xml +0 -0
  156. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/campaigns.xml +0 -0
  157. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/changes.xml +0 -0
  158. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/clients.xml +0 -0
  159. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/creatives.xml +0 -0
  160. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/dictionaries.xml +0 -0
  161. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/dynamicfeedadtargets.xml +0 -0
  162. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/dynamictextadtargets.xml +0 -0
  163. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/feeds.xml +0 -0
  164. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/keywordbids.xml +0 -0
  165. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/keywords.xml +0 -0
  166. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/keywordsresearch.xml +0 -0
  167. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/leads.xml +0 -0
  168. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/negativekeywordsharedsets.xml +0 -0
  169. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/retargetinglists.xml +0 -0
  170. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/sitelinks.xml +0 -0
  171. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/smartadtargets.xml +0 -0
  172. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/strategies.xml +0 -0
  173. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/turbopages.xml +0 -0
  174. {direct_cli-0.2.9.2 → direct_cli-0.2.11}/tests/wsdl_cache/vcards.xml +0 -0
@@ -77,7 +77,7 @@ Never invoke these commands in automated tests against a real account.
77
77
  | `bidmodifiers set` | Changes bid multipliers (device, region, time, etc.) |
78
78
 
79
79
  ### 🟡 Reversible but affect live traffic
80
- `campaigns suspend/resume/archive/unarchive`, `ads suspend/resume/archive/unarchive`, `keywords suspend/resume/archive/unarchive`, `audiencetargets suspend/resume`
80
+ `campaigns suspend/resume/archive/unarchive`, `ads suspend/resume/archive/unarchive`, `keywords suspend/resume`, `audiencetargets suspend/resume`
81
81
 
82
82
  ### 🟡 Account-wide mutations
83
83
  `clients update` — modifies account-level settings.
@@ -39,6 +39,7 @@ coverage.xml
39
39
  .venv
40
40
 
41
41
  # IDE
42
+ .claude/
42
43
  .vscode/
43
44
  .idea/
44
45
  *.swp
@@ -52,8 +52,17 @@ Click group-of-groups. Each Yandex Direct API resource = one file in `direct_cli
52
52
 
53
53
  ## Dangerous Commands — Never Auto-Test
54
54
 
55
- - **Irreversible (delete):** `campaigns/adgroups/ads/keywords/audiencetargets delete`
56
- - **Financial:** `bids set`, `keywordbids set`, `bidmodifiers set`
57
- - **Live traffic:** `suspend/resume/archive/unarchive` on campaigns, ads, keywords
58
- - **Mutations:** all `add`/`update` subcommands (test with `--dry-run`)
59
- - **Safe (read-only):** all `get` subcommands, `changes check*`, `dictionaries list-names`, `keywordsresearch has-search-volume`, `reports list-types`
55
+ Smoke command safety is tracked in `direct_cli/smoke_matrix.py`. Every CLI
56
+ subcommand belongs to exactly one category:
57
+
58
+ - **SAFE:** production read-only smoke tests in `scripts/test_safe_commands.sh`.
59
+ - **WRITE_SANDBOX:** live mutating smoke tests through
60
+ `scripts/test_sandbox_write.sh`; these must call only `direct --sandbox ...`
61
+ and must not touch production data.
62
+ - **DANGEROUS:** manual-only checklist in `scripts/test_dangerous_commands.sh`;
63
+ the script exits with status 1 by design and must not be used as an
64
+ automated runner.
65
+
66
+ Never auto-test production mutations: agency client changes, live bid changes,
67
+ moderation, lifecycle operations, `clients update`, or any `delete` without
68
+ `--sandbox`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: direct-cli
3
- Version: 0.2.9.2
3
+ Version: 0.2.11
4
4
  Summary: Command-line interface for Yandex Direct API
5
5
  Author: axisrow
6
6
  License: MIT
@@ -97,6 +97,23 @@ Notes:
97
97
  - Authorization is performed via `direct auth login`.
98
98
  - Alias `auth_login` is not supported.
99
99
 
100
+ Credential resolution priority:
101
+
102
+ | Priority | Source | Example |
103
+ |----------|--------|---------|
104
+ | 1 | Explicit CLI options | `direct --token TOKEN --login LOGIN campaigns get` |
105
+ | 2 | OAuth profile storage | `direct --profile agency1 campaigns get` |
106
+ | 3 | Profile-specific env vars | `YANDEX_DIRECT_TOKEN_AGENCY1`, `YANDEX_DIRECT_LOGIN_AGENCY1` |
107
+ | 4 | Base env vars or project `.env` | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
108
+ | 5 | 1Password references | `--op-token-ref`, `YANDEX_DIRECT_OP_TOKEN_REF` |
109
+ | 6 | Bitwarden references | `--bw-token-ref`, `YANDEX_DIRECT_BW_TOKEN_REF` |
110
+
111
+ The project `.env` file is loaded automatically. If a profile is selected
112
+ with `--profile` or `direct auth use --profile NAME`, Direct CLI does not
113
+ fall back to base `YANDEX_DIRECT_LOGIN`; this prevents mixing a profile token
114
+ with a login from the project `.env`. For multi-account setups, prefer OAuth
115
+ profiles or profile-specific env vars instead of base credentials.
116
+
100
117
  Install with `pip install direct-cli`, then run commands with `direct`.
101
118
  Invoking the deprecated `direct-cli` entrypoint exits with
102
119
  `use direct instead of direct-cli`.
@@ -145,6 +162,17 @@ the runtime CLI by default. If compatibility is ever needed, an alias must be
145
162
  added as an explicit exception with the concrete legacy syntax that still has to
146
163
  be supported.
147
164
 
165
+ #### Removed Legacy Names
166
+
167
+ | Legacy name | Canonical name |
168
+ |----------------------------|------------------------------|
169
+ | `dynamictargets` | `dynamicads` |
170
+ | `smarttargets` | `smartadtargets` |
171
+ | `negativekeywords` | `negativekeywordsharedsets` |
172
+ | `list` | `get` |
173
+ | `checkcamp` | `check-campaigns` |
174
+ | `checkdict` | `check-dictionaries` |
175
+
148
176
  #### Input Rules
149
177
 
150
178
  - All user-facing input must be passed only through typed CLI flags.
@@ -177,7 +205,7 @@ direct dictionaries get-geo-regions \
177
205
  #### Flag Design Rules
178
206
 
179
207
  - List inputs use comma-separated CLI syntax where appropriate.
180
- - Money and bid values stay human-readable in CLI input and are converted internally to the API wire format.
208
+ - Money and bid values are passed in micro-rubles (API-native format). Values below 100,000 trigger a validation hint suggesting the correct scale.
181
209
  - Selector fields remain explicit flags, for example:
182
210
  - `--id`
183
211
  - `--campaign-id`
@@ -221,7 +249,7 @@ direct campaigns get --ids 1,2,3
221
249
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
222
250
  direct keywordsresearch has-search-volume --keywords "buy laptop,buy desktop"
223
251
  direct smartadtargets update --id 456 --priority HIGH
224
- direct dynamicads set-bids --id 789 --bid 12.5 --context-bid 9 --priority HIGH
252
+ direct dynamicads set-bids --id 789 --bid 12500000 --context-bid 9000000 --priority HIGH
225
253
  direct dictionaries get-geo-regions --name Moscow --region-ids 225,187 --exact-names Москва,Санкт-Петербург --fields GeoRegionId,GeoRegionName
226
254
  ```
227
255
 
@@ -229,7 +257,7 @@ Invalid examples:
229
257
 
230
258
  ```bash
231
259
  direct dictionaries get-geo-regions --json '{"GeoRegionIds":[225]}' --fields GeoRegionId,GeoRegionName
232
- direct dynamicads set-bids --id 789 --bid 12.5 --json '{"StrategyPriority":"HIGH"}'
260
+ direct dynamicads set-bids --id 789 --bid 12500000 --json '{"StrategyPriority":"HIGH"}'
233
261
  direct dictionaries get-geo-regions \
234
262
  --region-ids 225 \
235
263
  --fields GeoRegionId,GeoRegionName
@@ -247,12 +275,12 @@ direct campaigns get --ids 1,2,3 --format table
247
275
  direct campaigns get --fetch-all --format csv --output campaigns.csv
248
276
 
249
277
  # Create (use --dry-run to preview the request)
250
- direct campaigns add --name "My Campaign" --start-date 2024-02-01 --type TEXT_CAMPAIGN --budget 1000 --setting ADD_METRICA_TAG=YES --search-strategy HIGHEST_POSITION --network-strategy SERVING_OFF --dry-run
278
+ direct campaigns add --name "My Campaign" --start-date 2024-02-01 --type TEXT_CAMPAIGN --budget 1000000000 --setting ADD_METRICA_TAG=YES --search-strategy HIGHEST_POSITION --network-strategy SERVING_OFF --dry-run
251
279
  direct campaigns add --name "Dynamic Campaign" --start-date 2024-02-01 --type DYNAMIC_TEXT_CAMPAIGN --setting ADD_METRICA_TAG=NO --search-strategy HIGHEST_POSITION --network-strategy SERVING_OFF --dry-run
252
- direct campaigns add --name "Smart Campaign" --start-date 2024-02-01 --type SMART_CAMPAIGN --network-strategy AVERAGE_CPC_PER_FILTER --filter-average-cpc 1 --counter-id 123 --dry-run
280
+ direct campaigns add --name "Smart Campaign" --start-date 2024-02-01 --type SMART_CAMPAIGN --network-strategy AVERAGE_CPC_PER_FILTER --filter-average-cpc 1000000 --counter-id 123 --dry-run
253
281
 
254
282
  # Update / lifecycle
255
- direct campaigns update --id 12345 --name "New Name" --status SUSPENDED --budget 100 --start-date 2024-02-10 --end-date 2024-03-01
283
+ direct campaigns update --id 12345 --name "New Name" --status SUSPENDED --budget 100000000 --start-date 2024-02-10 --end-date 2024-03-01
256
284
  direct campaigns suspend --id 12345
257
285
  direct campaigns resume --id 12345
258
286
  direct campaigns archive --id 12345
@@ -286,7 +314,7 @@ direct ads delete --id 99999
286
314
 
287
315
  ```bash
288
316
  direct keywords get --campaign-ids 1,2,3
289
- direct keywords add --adgroup-id 12345 --keyword "buy laptop" --bid 10.50 --context-bid 5.25 --user-param-1 segment-a --user-param-2 segment-b --dry-run
317
+ direct keywords add --adgroup-id 12345 --keyword "buy laptop" --bid 10500000 --context-bid 5250000 --user-param-1 segment-a --user-param-2 segment-b --dry-run
290
318
  direct keywords update --id 88888 --keyword "updated keyword text"
291
319
  direct keywords delete --id 88888
292
320
  ```
@@ -324,22 +352,22 @@ direct retargeting add --name "List A" --type AUDIENCE --rule "ALL:12345:30|6789
324
352
  direct retargeting update --id 55 --name "Renamed" --rule "ANY:12345:30" --dry-run
325
353
 
326
354
  # Bids and modifiers
327
- direct bids set --keyword-id 123 --bid 15
328
- direct bids set-auto --keyword-id 123 --max-bid 20 --position PREMIUMBLOCK --scope SEARCH --dry-run
329
- direct keywordbids set --keyword-id 321 --search-bid 8 --network-bid 3
330
- direct keywordbids set-auto --keyword-id 321 --target-traffic-volume 100 --increase-percent 10 --bid-ceiling 12.5 --dry-run
355
+ direct bids set --keyword-id 123 --bid 15000000
356
+ direct bids set-auto --keyword-id 123 --max-bid 20000000 --position PREMIUMBLOCK --scope SEARCH --dry-run
357
+ direct keywordbids set --keyword-id 321 --search-bid 8000000 --network-bid 3000000
358
+ direct keywordbids set-auto --keyword-id 321 --target-traffic-volume 100 --increase-percent 10 --bid-ceiling 12500000 --dry-run
331
359
  direct bidmodifiers add --campaign-id 123 --type DEMOGRAPHICS_ADJUSTMENT --value 150 --gender GENDER_MALE --age AGE_25_34 --dry-run
332
360
  direct bidmodifiers set --id 99 --value 130 --dry-run
333
361
 
334
362
  # Canonical multiword groups
335
363
  direct negativekeywordsharedsets update --id 123 --keywords "foo,bar"
336
- direct audiencetargets add --adgroup-id 100 --retargeting-list-id 200 --bid 12 --priority HIGH --dry-run
337
- direct audiencetargets set-bids --id 101 --context-bid 7 --priority LOW --dry-run
338
- direct dynamicads add --adgroup-id 33 --name "Webpage A" --condition "URL:CONTAINS_ANY:test|shop" --condition "PAGE_CONTENT:CONTAINS:baz" --bid 3 --context-bid 2 --priority HIGH --dry-run
339
- direct smartadtargets add --adgroup-id 55 --name "Audience A" --audience ALL_SEGMENTS --condition "CATEGORY_ID:EQUALS:42" --average-cpc 3 --average-cpa 4 --priority HIGH --available-items-only YES --dry-run
364
+ direct audiencetargets add --adgroup-id 100 --retargeting-list-id 200 --bid 12000000 --priority HIGH --dry-run
365
+ direct audiencetargets set-bids --id 101 --context-bid 7000000 --priority LOW --dry-run
366
+ 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
367
+ direct smartadtargets add --adgroup-id 55 --name "Audience A" --audience ALL_SEGMENTS --condition "CATEGORY_ID:EQUALS:42" --average-cpc 3000000 --average-cpa 4000000 --priority HIGH --available-items-only YES --dry-run
340
368
  direct smartadtargets update --id 456 --priority HIGH
341
- direct smartadtargets set-bids --id 456 --average-cpc 10.5 --average-cpa 15 --priority HIGH
342
- direct dynamicads set-bids --id 789 --bid 12.5 --context-bid 9 --priority HIGH
369
+ direct smartadtargets set-bids --id 456 --average-cpc 10500000 --average-cpa 15000000 --priority HIGH
370
+ direct dynamicads set-bids --id 789 --bid 12500000 --context-bid 9000000 --priority HIGH
343
371
 
344
372
  # Shared bidding strategies
345
373
  direct strategies get --limit 5
@@ -349,8 +377,8 @@ direct strategies archive --id 42 --dry-run
349
377
 
350
378
  # Dynamic feed ad targets
351
379
  direct dynamicfeedadtargets get --adgroup-ids 123 --limit 5
352
- direct dynamicfeedadtargets add --adgroup-id 33 --name "Feed slice A" --condition "CATEGORY:EQUALS:shoes" --bid 5 --dry-run
353
- direct dynamicfeedadtargets set-bids --id 789 --bid 6.5 --context-bid 4 --dry-run
380
+ direct dynamicfeedadtargets add --adgroup-id 33 --name "Feed slice A" --condition "CATEGORY:EQUALS:shoes" --bid 5000000 --dry-run
381
+ direct dynamicfeedadtargets set-bids --id 789 --bid 6500000 --context-bid 4000000 --dry-run
354
382
 
355
383
  # Extensions, assets, feeds, and clients
356
384
  direct sitelinks add --sitelink "Docs|https://example.com/docs" --sitelink "Help|https://example.com/help|Desk" --dry-run
@@ -409,7 +437,7 @@ The following commands make **irreversible changes** — use with caution:
409
437
  | `keywords delete --id` | Permanently deletes a keyword |
410
438
  | `audiencetargets delete --id` | Permanently deletes an audience target |
411
439
 
412
- Commands that affect live ad delivery: `suspend`, `resume`, `archive`, `unarchive` (available on `campaigns`, `ads`, `keywords`).
440
+ Commands that affect live ad delivery: `suspend`, `resume`, `archive`, `unarchive` (available on `campaigns`, `ads`), `suspend`, `resume` (also on `keywords`).
413
441
 
414
442
  Commands that affect bids and spending: `bids set`, `keywordbids set`, `bidmodifiers set`.
415
443
 
@@ -439,6 +467,27 @@ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v # live draft cas
439
467
  YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v --record-mode=rewrite # re-record live draft cassette
440
468
  ```
441
469
 
470
+ #### Smoke command scripts
471
+
472
+ Every CLI subcommand is classified in `direct_cli/smoke_matrix.py`.
473
+
474
+ | Category | Script | When to run |
475
+ |---|---|---|
476
+ | SAFE | `scripts/test_safe_commands.sh` | Production read-only smoke checks; requires `YANDEX_DIRECT_TOKEN` and `YANDEX_DIRECT_LOGIN` |
477
+ | WRITE_SANDBOX | `scripts/test_sandbox_write.sh` | Live sandbox write smoke checks; requires `YANDEX_DIRECT_TOKEN` and `YANDEX_DIRECT_LOGIN`; reports `PASS`, `FAIL`, `SANDBOX_LIMITATION`, or `NOT_COVERED` for each command |
478
+ | DANGEROUS | `scripts/test_dangerous_commands.sh` | Manual checklist only; exits with status 1 by design |
479
+
480
+ Current command surface:
481
+
482
+ | Metric | Count |
483
+ |---|---:|
484
+ | WSDL-backed API services | 29 |
485
+ | Supported API services including Reports | 30 |
486
+ | WSDL operations | 112 |
487
+ | CLI groups including `auth` | 31 |
488
+ | CLI subcommands including `auth` | 122 |
489
+ | API CLI subcommands excluding `auth` | 118 |
490
+
442
491
  ### API Coverage And Drift Monitoring
443
492
 
444
493
  The project now distinguishes four surfaces:
@@ -471,24 +520,43 @@ CI runs a scheduled API coverage workflow that:
471
520
  parity and live-discovered model gap counts;
472
521
  - checks the cached WSDL files against the live Yandex Direct API on schedule.
473
522
 
474
- #### Re-recording write cassettes
523
+ #### Live sandbox write smoke
524
+
525
+ `WRITE_SANDBOX` smoke is a live check against the Yandex Direct **sandbox**.
526
+ It does not replay stored HTTP traffic and it does not create new recordings.
527
+ Run it only when you intentionally want to call `api-sandbox.direct.yandex.com`:
528
+
529
+ ```bash
530
+ set -a && source .env && set +a
531
+ scripts/test_sandbox_write.sh
532
+ ```
475
533
 
476
- The write tests replay HTTP traffic captured from the Yandex Direct **sandbox**
477
- (`--sandbox` is injected automatically). Cassettes live under
478
- `tests/cassettes/test_integration_write/` and are checked into git.
534
+ The runner executes matrix commands through `direct --sandbox ...`, creates
535
+ temporary sandbox prerequisites where possible, and cleans them up best-effort.
536
+ The report contains one row per `WRITE_SANDBOX` command:
479
537
 
480
- If you change the request payload of any write command (e.g. adding a field),
481
- the matching cassette stops replaying and the test fails with a body-mismatch
482
- error. To regenerate:
538
+ - `PASS` means the command completed against the live sandbox API.
539
+ - `SANDBOX_LIMITATION` means the request reached the API and hit a known
540
+ sandbox-only limitation such as codes `8800`, `1000`, `3500`, or `5004`.
541
+ - `FAIL` means an unexpected CLI or API error.
542
+ - `NOT_COVERED` means the runner does not yet know how to safely build the
543
+ prerequisites for that command.
544
+
545
+ The same OAuth token works for both production and the sandbox; no separate
546
+ sandbox token is needed.
547
+
548
+ #### Re-recording write cassettes
549
+
550
+ The `integration_write` pytest tier still replays stored write-test traffic
551
+ for regression coverage. If you change those tests or their payloads and
552
+ intentionally need to refresh the fixtures, regenerate them separately:
483
553
 
484
554
  ```bash
485
555
  set -a && source .env && set +a # load YANDEX_DIRECT_TOKEN / LOGIN
486
556
  pytest -m integration_write -v --record-mode=rewrite
487
557
  ```
488
558
 
489
- **The same OAuth token works for both production and the sandbox** — no
490
- separate sandbox token is needed. After recording, **always audit the
491
- generated YAMLs for leaked secrets**:
559
+ After recording, **always audit the generated YAMLs for leaked secrets**:
492
560
 
493
561
  ```bash
494
562
  grep -r "$YANDEX_DIRECT_TOKEN" tests/cassettes/ # must return nothing
@@ -583,6 +651,45 @@ YANDEX_DIRECT_LOGIN=ваш_логин_на_яндексе
583
651
  direct --token ВАШ_ТОКЕН --login ВАШ_ЛОГИН campaigns get
584
652
  ```
585
653
 
654
+ Используйте профильные credentials из `.env`:
655
+
656
+ ```env
657
+ YANDEX_DIRECT_TOKEN_AGENCY1=token-1
658
+ YANDEX_DIRECT_LOGIN_AGENCY1=client-login-1
659
+ YANDEX_DIRECT_TOKEN_AGENCY2=token-2
660
+ YANDEX_DIRECT_LOGIN_AGENCY2=client-login-2
661
+ ```
662
+
663
+ OAuth и profile-команды:
664
+
665
+ ```bash
666
+ direct auth login
667
+ direct auth login --profile agency1
668
+ direct auth login --code abc123 --profile agency1
669
+ direct auth login --oauth-token y0_example --profile agency1
670
+ direct auth list
671
+ direct auth use --profile agency1
672
+ direct auth status --profile agency1
673
+ direct --profile agency1 campaigns get
674
+ ```
675
+
676
+ Порядок выбора credentials:
677
+
678
+ | Приоритет | Источник | Пример |
679
+ |-----------|----------|--------|
680
+ | 1 | Явные CLI-опции | `direct --token TOKEN --login LOGIN campaigns get` |
681
+ | 2 | OAuth profile storage | `direct --profile agency1 campaigns get` |
682
+ | 3 | Профильные env vars | `YANDEX_DIRECT_TOKEN_AGENCY1`, `YANDEX_DIRECT_LOGIN_AGENCY1` |
683
+ | 4 | Базовые env vars или project `.env` | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
684
+ | 5 | 1Password references | `--op-token-ref`, `YANDEX_DIRECT_OP_TOKEN_REF` |
685
+ | 6 | Bitwarden references | `--bw-token-ref`, `YANDEX_DIRECT_BW_TOKEN_REF` |
686
+
687
+ Файл `.env` в проекте загружается автоматически. Если профиль выбран через
688
+ `--profile` или `direct auth use --profile NAME`, Direct CLI не подставляет
689
+ base `YANDEX_DIRECT_LOGIN`; это защищает от смешивания токена из профиля с
690
+ логином из project `.env`. Для нескольких аккаунтов используйте OAuth profiles
691
+ или профильные env vars, а не базовые credentials.
692
+
586
693
  Установка остаётся через `pip install direct-cli`, а запуск команд теперь идет
587
694
  через `direct`. Вызов deprecated entrypoint `direct-cli` завершается ошибкой с
588
695
  подсказкой `use direct instead of direct-cli`.
@@ -593,6 +700,7 @@ direct --token ВАШ_ТОКЕН --login ВАШ_ЛОГИН campaigns get
593
700
  |-------|----------|
594
701
  | `--token` | OAuth-токен доступа к API |
595
702
  | `--login` | Direct client login |
703
+ | `--profile` | Имя credential profile |
596
704
  | `--sandbox` | Использовать тестовое API (песочница) |
597
705
 
598
706
  ### Использование
@@ -624,6 +732,17 @@ Command naming rules:
624
732
  должен быть добавлен как явное exception-правило с конкретным legacy syntax из
625
733
  `tapi-yandex-direct`, который действительно нужно поддержать.
626
734
 
735
+ Удалённые исторические имена:
736
+
737
+ | Историческое имя | Каноническое имя |
738
+ |----------------------------|------------------------------|
739
+ | `dynamictargets` | `dynamicads` |
740
+ | `smarttargets` | `smartadtargets` |
741
+ | `negativekeywords` | `negativekeywordsharedsets` |
742
+ | `list` | `get` |
743
+ | `checkcamp` | `check-campaigns` |
744
+ | `checkdict` | `check-dictionaries` |
745
+
627
746
  `direct` — это канонический transport entrypoint над API Яндекс Директа,
628
747
  устанавливаемый пакетом `direct-cli`. Канонические имена CLI-групп следуют
629
748
  нормализованным Python-именам из `tapi-yandex-direct`, а имена подкоманд —
@@ -704,7 +823,7 @@ direct dictionaries get-geo-regions \
704
823
  #### Flag Design Rules
705
824
 
706
825
  - List inputs use comma-separated CLI syntax where appropriate.
707
- - Money and bid values stay human-readable in CLI input and are converted internally to the API wire format.
826
+ - Money and bid values are passed in micro-rubles (API-native format). Values below 100,000 trigger a validation hint suggesting the correct scale.
708
827
  - Selector fields remain explicit flags, for example:
709
828
  - `--id`
710
829
  - `--campaign-id`
@@ -747,7 +866,7 @@ Valid canonical examples:
747
866
  direct campaigns get --ids 1,2,3
748
867
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
749
868
  direct keywordsresearch has-search-volume --keywords "buy laptop,buy desktop"
750
- direct dynamicads set-bids --id 789 --bid 12.5
869
+ direct dynamicads set-bids --id 789 --bid 12500000
751
870
  direct dictionaries get-geo-regions --region-ids 225 --fields GeoRegionId,GeoRegionName
752
871
  ```
753
872
 
@@ -755,7 +874,7 @@ Invalid examples:
755
874
 
756
875
  ```bash
757
876
  direct dictionaries get-geo-regions --json '{"GeoRegionIds":[225]}' --fields GeoRegionId,GeoRegionName
758
- direct dynamicads set-bids --id 789 --bid 12.5 --json '{"StrategyPriority":"HIGH"}'
877
+ direct dynamicads set-bids --id 789 --bid 12500000 --json '{"StrategyPriority":"HIGH"}'
759
878
  direct dictionaries get-geo-regions \
760
879
  --region-ids 225 \
761
880
  --fields GeoRegionId,GeoRegionName
@@ -773,12 +892,12 @@ direct campaigns get --ids 1,2,3 --format table
773
892
  direct campaigns get --fetch-all --format csv --output campaigns.csv
774
893
 
775
894
  # Создать (--dry-run покажет запрос без отправки)
776
- direct campaigns add --name "Моя кампания" --start-date 2024-02-01 --type TEXT_CAMPAIGN --budget 1000 --setting ADD_METRICA_TAG=YES --search-strategy HIGHEST_POSITION --network-strategy SERVING_OFF --dry-run
895
+ direct campaigns add --name "Моя кампания" --start-date 2024-02-01 --type TEXT_CAMPAIGN --budget 1000000000 --setting ADD_METRICA_TAG=YES --search-strategy HIGHEST_POSITION --network-strategy SERVING_OFF --dry-run
777
896
  direct campaigns add --name "Динамическая кампания" --start-date 2024-02-01 --type DYNAMIC_TEXT_CAMPAIGN --setting ADD_METRICA_TAG=NO --search-strategy HIGHEST_POSITION --network-strategy SERVING_OFF --dry-run
778
- direct campaigns add --name "Смарт-кампания" --start-date 2024-02-01 --type SMART_CAMPAIGN --network-strategy AVERAGE_CPC_PER_FILTER --filter-average-cpc 1 --counter-id 123 --dry-run
897
+ direct campaigns add --name "Смарт-кампания" --start-date 2024-02-01 --type SMART_CAMPAIGN --network-strategy AVERAGE_CPC_PER_FILTER --filter-average-cpc 1000000 --counter-id 123 --dry-run
779
898
 
780
899
  # Обновление и управление статусом
781
- direct campaigns update --id 12345 --name "Новое название" --status SUSPENDED --budget 100 --start-date 2024-02-10 --end-date 2024-03-01
900
+ direct campaigns update --id 12345 --name "Новое название" --status SUSPENDED --budget 100000000 --start-date 2024-02-10 --end-date 2024-03-01
782
901
  direct campaigns suspend --id 12345
783
902
  direct campaigns resume --id 12345
784
903
  direct campaigns archive --id 12345
@@ -812,7 +931,7 @@ direct ads delete --id 99999
812
931
 
813
932
  ```bash
814
933
  direct keywords get --campaign-ids 1,2,3
815
- direct keywords add --adgroup-id 12345 --keyword "купить ноутбук" --bid 10.50 --context-bid 5.25 --user-param-1 segment-a --user-param-2 segment-b --dry-run
934
+ direct keywords add --adgroup-id 12345 --keyword "купить ноутбук" --bid 10500000 --context-bid 5250000 --user-param-1 segment-a --user-param-2 segment-b --dry-run
816
935
  direct keywords update --id 88888 --keyword "updated keyword text"
817
936
  direct keywords delete --id 88888
818
937
  ```
@@ -850,22 +969,22 @@ direct retargeting add --name "Список A" --type AUDIENCE --rule "ALL:12345
850
969
  direct retargeting update --id 55 --name "Переименованный список" --rule "ANY:12345:30" --dry-run
851
970
 
852
971
  # Ставки и модификаторы
853
- direct bids set --keyword-id 123 --bid 15
854
- direct bids set-auto --keyword-id 123 --max-bid 20 --position PREMIUMBLOCK --scope SEARCH --dry-run
855
- direct keywordbids set --keyword-id 321 --search-bid 8 --network-bid 3
856
- direct keywordbids set-auto --keyword-id 321 --target-traffic-volume 100 --increase-percent 10 --bid-ceiling 12.5 --dry-run
972
+ direct bids set --keyword-id 123 --bid 15000000
973
+ direct bids set-auto --keyword-id 123 --max-bid 20000000 --position PREMIUMBLOCK --scope SEARCH --dry-run
974
+ direct keywordbids set --keyword-id 321 --search-bid 8000000 --network-bid 3000000
975
+ direct keywordbids set-auto --keyword-id 321 --target-traffic-volume 100 --increase-percent 10 --bid-ceiling 12500000 --dry-run
857
976
  direct bidmodifiers add --campaign-id 123 --type DEMOGRAPHICS_ADJUSTMENT --value 150 --gender GENDER_MALE --age AGE_25_34 --dry-run
858
977
  direct bidmodifiers set --id 99 --value 130 --dry-run
859
978
 
860
979
  # Канонические многословные группы
861
980
  direct negativekeywordsharedsets update --id 123 --keywords "foo,bar"
862
- direct audiencetargets add --adgroup-id 100 --retargeting-list-id 200 --bid 12 --priority HIGH --dry-run
863
- direct audiencetargets set-bids --id 101 --context-bid 7 --priority LOW --dry-run
864
- direct dynamicads add --adgroup-id 33 --name "Webpage A" --condition "URL:CONTAINS_ANY:test|shop" --condition "PAGE_CONTENT:CONTAINS:baz" --bid 3 --context-bid 2 --priority HIGH --dry-run
865
- direct smartadtargets add --adgroup-id 55 --name "Audience A" --audience ALL_SEGMENTS --condition "CATEGORY_ID:EQUALS:42" --average-cpc 3 --average-cpa 4 --priority HIGH --available-items-only YES --dry-run
981
+ direct audiencetargets add --adgroup-id 100 --retargeting-list-id 200 --bid 12000000 --priority HIGH --dry-run
982
+ direct audiencetargets set-bids --id 101 --context-bid 7000000 --priority LOW --dry-run
983
+ 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
984
+ direct smartadtargets add --adgroup-id 55 --name "Audience A" --audience ALL_SEGMENTS --condition "CATEGORY_ID:EQUALS:42" --average-cpc 3000000 --average-cpa 4000000 --priority HIGH --available-items-only YES --dry-run
866
985
  direct smartadtargets update --id 456 --priority HIGH
867
- direct smartadtargets set-bids --id 456 --average-cpc 10.5 --average-cpa 15 --priority HIGH
868
- direct dynamicads set-bids --id 789 --bid 12.5 --context-bid 9 --priority HIGH
986
+ direct smartadtargets set-bids --id 456 --average-cpc 10500000 --average-cpa 15000000 --priority HIGH
987
+ direct dynamicads set-bids --id 789 --bid 12500000 --context-bid 9000000 --priority HIGH
869
988
 
870
989
  # Общие стратегии ставок
871
990
  direct strategies get --limit 5
@@ -875,8 +994,8 @@ direct strategies archive --id 42 --dry-run
875
994
 
876
995
  # Динамические таргеты по фиду
877
996
  direct dynamicfeedadtargets get --adgroup-ids 123 --limit 5
878
- direct dynamicfeedadtargets add --adgroup-id 33 --name "Срез фида А" --condition "CATEGORY:EQUALS:shoes" --bid 5 --dry-run
879
- direct dynamicfeedadtargets set-bids --id 789 --bid 6.5 --context-bid 4 --dry-run
997
+ direct dynamicfeedadtargets add --adgroup-id 33 --name "Срез фида А" --condition "CATEGORY:EQUALS:shoes" --bid 5000000 --dry-run
998
+ direct dynamicfeedadtargets set-bids --id 789 --bid 6500000 --context-bid 4000000 --dry-run
880
999
 
881
1000
  # Расширения, ассеты, фиды и клиенты
882
1001
  direct sitelinks add --sitelink "Docs|https://example.com/docs" --sitelink "Help|https://example.com/help|Desk" --dry-run
@@ -936,7 +1055,7 @@ direct campaigns get --fetch-all # все страницы
936
1055
  | `keywords delete --id` | Безвозвратно удаляет ключевое слово |
937
1056
  | `audiencetargets delete --id` | Безвозвратно удаляет условие подбора аудитории |
938
1057
 
939
- Команды, влияющие на показ рекламы: `suspend`, `resume`, `archive`, `unarchive` (доступны для `campaigns`, `ads`, `keywords`).
1058
+ Команды, влияющие на показ рекламы: `suspend`, `resume`, `archive`, `unarchive` (доступны для `campaigns`, `ads`), `suspend`, `resume` (также для `keywords`).
940
1059
 
941
1060
  Команды, влияющие на ставки и расходы: `bids set`, `keywordbids set`, `bidmodifiers set`.
942
1061
 
@@ -966,24 +1085,64 @@ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v # replay live dr
966
1085
  YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v --record-mode=rewrite # перезапись live draft-кассеты
967
1086
  ```
968
1087
 
969
- #### Перезапись write-кассет
1088
+ #### Smoke-скрипты команд
1089
+
1090
+ Каждая CLI-подкоманда классифицирована в `direct_cli/smoke_matrix.py`.
1091
+
1092
+ | Категория | Скрипт | Когда запускать |
1093
+ |---|---|---|
1094
+ | SAFE | `scripts/test_safe_commands.sh` | Production smoke-проверки только на чтение; нужны `YANDEX_DIRECT_TOKEN` и `YANDEX_DIRECT_LOGIN` |
1095
+ | WRITE_SANDBOX | `scripts/test_sandbox_write.sh` | Live sandbox write-проверки; нужны `YANDEX_DIRECT_TOKEN` и `YANDEX_DIRECT_LOGIN`; отчёт печатает `PASS`, `FAIL`, `SANDBOX_LIMITATION` или `NOT_COVERED` для каждой команды |
1096
+ | DANGEROUS | `scripts/test_dangerous_commands.sh` | Только ручной checklist; специально завершается с кодом 1 |
1097
+
1098
+ Текущая поверхность команд:
1099
+
1100
+ | Метрика | Количество |
1101
+ |---|---:|
1102
+ | WSDL-backed API services | 29 |
1103
+ | API services с учётом Reports | 30 |
1104
+ | WSDL operations | 112 |
1105
+ | CLI groups с `auth` | 31 |
1106
+ | CLI subcommands с `auth` | 122 |
1107
+ | API CLI subcommands без `auth` | 118 |
1108
+
1109
+ #### Live sandbox write smoke
1110
+
1111
+ `WRITE_SANDBOX` smoke — это live-проверка против **sandbox-окружения**
1112
+ Яндекс Директа. Она не воспроизводит сохранённый HTTP-трафик и не создаёт
1113
+ новые записи. Запускайте её только когда намеренно хотите обратиться к
1114
+ `api-sandbox.direct.yandex.com`:
970
1115
 
971
- Write-тесты воспроизводят HTTP-трафик, записанный против **sandbox-окружения**
972
- Яндекс Директа (`--sandbox` подставляется автоматически). Кассеты лежат в
973
- `tests/cassettes/test_integration_write/` и закоммичены в git.
1116
+ ```bash
1117
+ set -a && source .env && set +a
1118
+ scripts/test_sandbox_write.sh
1119
+ ```
1120
+
1121
+ Runner выполняет команды matrix через `direct --sandbox ...`, создаёт
1122
+ временные sandbox prerequisites там, где это безопасно, и удаляет их
1123
+ best-effort. Отчёт содержит одну строку на каждую команду `WRITE_SANDBOX`:
1124
+
1125
+ - `PASS`: команда прошла против live sandbox API.
1126
+ - `SANDBOX_LIMITATION`: запрос дошёл до API и получил известное ограничение
1127
+ sandbox, например коды `8800`, `1000`, `3500` или `5004`.
1128
+ - `FAIL`: неожиданный CLI/API error.
1129
+ - `NOT_COVERED`: runner пока не умеет безопасно построить prerequisites.
1130
+
1131
+ Один и тот же OAuth-токен работает и для продакшена, и для sandbox; отдельный
1132
+ sandbox-токен не нужен.
1133
+
1134
+ #### Перезапись write-кассет
974
1135
 
975
- Если меняется payload какой-то из write-команд (например, добавили поле),
976
- соответствующая кассета перестанет воспроизводиться, тест упадёт с
977
- body-mismatch. Перезапись:
1136
+ Уровень `integration_write` в pytest всё ещё воспроизводит сохранённый
1137
+ write-трафик для регрессионного покрытия. Если вы меняете эти тесты или их
1138
+ payload и намеренно хотите обновить fixtures, перезаписывайте их отдельно:
978
1139
 
979
1140
  ```bash
980
1141
  set -a && source .env && set +a # загрузить YANDEX_DIRECT_TOKEN / LOGIN
981
1142
  pytest -m integration_write -v --record-mode=rewrite
982
1143
  ```
983
1144
 
984
- **Один и тот же OAuth-токен работает и для продакшена, и для sandbox** —
985
- отдельный sandbox-токен не нужен. После перезаписи **обязательно проверьте
986
- YAML-ы на утечку секретов**:
1145
+ После перезаписи **обязательно проверьте YAML-ы на утечку секретов**:
987
1146
 
988
1147
  ```bash
989
1148
  grep -r "$YANDEX_DIRECT_TOKEN" tests/cassettes/ # должно быть пусто