direct-cli 0.2.9.1__tar.gz → 0.2.10__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.1 → direct_cli-0.2.10}/.github/copilot-instructions.md +1 -1
  2. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/.gitignore +1 -0
  3. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/CLAUDE.md +14 -5
  4. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/PKG-INFO +163 -61
  5. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/README.md +162 -60
  6. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/_vendor/tapi_yandex_direct/__init__.py +1 -1
  7. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/_vendor/tapi_yandex_direct/resource_mapping.py +38 -1
  8. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.py +2 -2
  9. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/auth.py +19 -0
  10. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/audiencetargets.py +5 -5
  11. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/auth.py +16 -5
  12. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/bidmodifiers.py +22 -20
  13. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/bids.py +5 -5
  14. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/businesses.py +1 -1
  15. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/campaigns.py +8 -8
  16. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/changes.py +5 -8
  17. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/dynamicads.py +9 -9
  18. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/dynamicfeedadtargets.py +9 -9
  19. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/feeds.py +3 -6
  20. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/keywordbids.py +24 -20
  21. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/keywords.py +5 -65
  22. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/keywordsresearch.py +24 -3
  23. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/leads.py +8 -6
  24. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/smartadtargets.py +13 -13
  25. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/strategies.py +1 -1
  26. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/turbopages.py +1 -1
  27. direct_cli-0.2.10/direct_cli/smoke_matrix.py +252 -0
  28. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/utils.py +43 -11
  29. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/wsdl_coverage.py +0 -6
  30. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli.egg-info/PKG-INFO +163 -61
  31. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli.egg-info/SOURCES.txt +9 -0
  32. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/pyproject.toml +1 -1
  33. direct_cli-0.2.10/scripts/build_api_coverage_checklist.py +444 -0
  34. direct_cli-0.2.10/scripts/patch_vendor_imports.py +142 -0
  35. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/scripts/release_pypi.sh +1 -1
  36. direct_cli-0.2.10/scripts/sandbox_write_live.py +1324 -0
  37. direct_cli-0.2.10/scripts/test_dangerous_commands.sh +50 -0
  38. direct_cli-0.2.10/scripts/test_safe_commands.sh +233 -0
  39. direct_cli-0.2.10/scripts/test_sandbox_write.sh +23 -0
  40. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/scripts/update_vendor.sh +11 -0
  41. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/API_COVERAGE.md +20 -0
  42. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/MANUAL_COVERAGE.md +1 -1
  43. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/conftest.py +19 -5
  44. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/test_api_coverage.py +13 -15
  45. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/test_auth_bw.py +14 -7
  46. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/test_auth_op.py +12 -6
  47. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/test_cli.py +12 -2
  48. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/test_comprehensive.py +7 -4
  49. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/test_dry_run.py +111 -75
  50. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/test_integration.py +3 -9
  51. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/test_integration_live_write.py +16 -15
  52. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/test_integration_write.py +4 -4
  53. direct_cli-0.2.10/tests/test_smoke_matrix.py +224 -0
  54. direct_cli-0.2.10/tests/test_vendor_imports.py +150 -0
  55. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/.env.example +0 -0
  56. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/.github/workflows/api-coverage.yml +0 -0
  57. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/.github/workflows/claude-code-review.yml +0 -0
  58. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/.github/workflows/claude.yml +0 -0
  59. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/AGENTS.md +0 -0
  60. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/MANIFEST.in +0 -0
  61. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/__init__.py +0 -0
  62. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/_deprecated.py +0 -0
  63. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/_vendor/__init__.py +0 -0
  64. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/_vendor/tapi_yandex_direct/exceptions.py +0 -0
  65. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/api.py +0 -0
  66. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/cli.py +0 -0
  67. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/__init__.py +0 -0
  68. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/adextensions.py +0 -0
  69. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/adgroups.py +0 -0
  70. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/adimages.py +0 -0
  71. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/ads.py +0 -0
  72. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/advideos.py +0 -0
  73. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/agencyclients.py +0 -0
  74. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/clients.py +0 -0
  75. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/creatives.py +0 -0
  76. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/dictionaries.py +0 -0
  77. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/negativekeywordsharedsets.py +0 -0
  78. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/reports.py +0 -0
  79. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/retargeting.py +0 -0
  80. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/sitelinks.py +0 -0
  81. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/commands/vcards.py +0 -0
  82. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/output.py +0 -0
  83. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli/reports_coverage.py +0 -0
  84. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli.egg-info/dependency_links.txt +0 -0
  85. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli.egg-info/entry_points.txt +0 -0
  86. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli.egg-info/requires.txt +0 -0
  87. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/direct_cli.egg-info/top_level.txt +0 -0
  88. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/docs/superpowers/plans/2026-04-12-issue-32-completion.md +0 -0
  89. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/docs/superpowers/specs/2026-04-23-vendor-tapi-yandex-direct-design.md +0 -0
  90. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/scripts/anonymize_cassettes.py +0 -0
  91. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/scripts/build_api_coverage_report.py +0 -0
  92. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/scripts/check_reports_drift.py +0 -0
  93. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/scripts/check_wsdl_drift.py +0 -0
  94. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/scripts/refresh_reports_cache.py +0 -0
  95. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/scripts/refresh_wsdl_cache.py +0 -0
  96. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/setup.cfg +0 -0
  97. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/setup.py +0 -0
  98. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/API_ISSUE_AUDIT.md +0 -0
  99. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/__init__.py +0 -0
  100. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_live_write/test_live_draft_adgroups_add_update_delete.yaml +0 -0
  101. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_live_write/test_live_draft_adimages_add_get_delete.yaml +0 -0
  102. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_live_write/test_live_draft_ads_add_update_delete.yaml +0 -0
  103. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_live_write/test_live_draft_ads_suspend_resume_archive_unarchive.yaml +0 -0
  104. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_live_write/test_live_draft_advideos_add_get.yaml +0 -0
  105. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_live_write/test_live_draft_audiencetargets_add_delete.yaml +0 -0
  106. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_live_write/test_live_draft_audiencetargets_suspend_resume.yaml +0 -0
  107. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_live_write/test_live_draft_bids_set.yaml +0 -0
  108. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_live_write/test_live_draft_campaign_create_get_delete.yaml +0 -0
  109. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_live_write/test_live_draft_creatives_chain_advideo_to_creative.yaml +0 -0
  110. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_live_write/test_live_draft_dynamicads_add_delete.yaml +0 -0
  111. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_live_write/test_live_draft_dynamicads_suspend_resume.yaml +0 -0
  112. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_live_write/test_live_draft_keywordbids_set.yaml +0 -0
  113. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_live_write/test_live_draft_keywords_add_update_delete.yaml +0 -0
  114. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_live_write/test_live_draft_keywords_suspend_resume.yaml +0 -0
  115. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_live_write/test_live_draft_sitelinks_add_get_delete.yaml +0 -0
  116. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_live_write/test_live_draft_smartadtargets_add_update_delete.yaml +0 -0
  117. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_live_write/test_live_draft_smartadtargets_suspend_resume.yaml +0 -0
  118. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_write/TestWriteAdExtensions.test_add_delete.yaml +0 -0
  119. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_write/TestWriteAdGroups.test_add_update_delete.yaml +0 -0
  120. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_write/TestWriteAdImages.test_add_delete.yaml +0 -0
  121. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_write/TestWriteAds.test_add_text_ad_update_delete.yaml +0 -0
  122. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_write/TestWriteAudienceTargets.test_add_delete.yaml +0 -0
  123. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_write/TestWriteBidModifiersAdd.test_add_delete_mobile.yaml +0 -0
  124. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_write/TestWriteBidModifiersSet.test_set_without_id_is_rejected.yaml +0 -0
  125. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_write/TestWriteBids.test_set_bid.yaml +0 -0
  126. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_write/TestWriteCampaignDraftLifecycle.test_draft_create_get_delete.yaml +0 -0
  127. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_write/TestWriteCampaigns.test_campaign_lifecycle.yaml +0 -0
  128. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_write/TestWriteDynamicAds.test_add_update_delete.yaml +0 -0
  129. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_write/TestWriteFeeds.test_add_update_delete.yaml +0 -0
  130. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_write/TestWriteKeywordBids.test_set_keyword_bid.yaml +0 -0
  131. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_write/TestWriteKeywords.test_add_update_delete.yaml +0 -0
  132. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_write/TestWriteNegativeKeywordSharedSets.test_add_update_delete.yaml +0 -0
  133. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_write/TestWriteRetargeting.test_add_delete.yaml +0 -0
  134. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_write/TestWriteSitelinks.test_add_delete.yaml +0 -0
  135. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_write/TestWriteSmartAdTargets.test_add_update_delete.yaml +0 -0
  136. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/cassettes/test_integration_write/TestWriteVCards.test_add_delete.yaml +0 -0
  137. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/fixtures/test-video.mp4 +0 -0
  138. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/reports_cache/raw/fields-list.html +0 -0
  139. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/reports_cache/raw/headers.html +0 -0
  140. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/reports_cache/raw/spec.html +0 -0
  141. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/reports_cache/raw/type.html +0 -0
  142. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/reports_cache/spec.json +0 -0
  143. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/test_auth_oauth.py +0 -0
  144. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/test_reports_drift.py +0 -0
  145. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/test_transport_contract.py +0 -0
  146. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/adextensions.xml +0 -0
  147. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/adgroups.xml +0 -0
  148. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/adimages.xml +0 -0
  149. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/ads.xml +0 -0
  150. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/advideos.xml +0 -0
  151. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/agencyclients.xml +0 -0
  152. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/audiencetargets.xml +0 -0
  153. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/bidmodifiers.xml +0 -0
  154. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/bids.xml +0 -0
  155. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/businesses.xml +0 -0
  156. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/campaigns.xml +0 -0
  157. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/changes.xml +0 -0
  158. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/clients.xml +0 -0
  159. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/creatives.xml +0 -0
  160. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/dictionaries.xml +0 -0
  161. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/dynamicfeedadtargets.xml +0 -0
  162. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/dynamictextadtargets.xml +0 -0
  163. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/feeds.xml +0 -0
  164. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/keywordbids.xml +0 -0
  165. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/keywords.xml +0 -0
  166. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/keywordsresearch.xml +0 -0
  167. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/leads.xml +0 -0
  168. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/negativekeywordsharedsets.xml +0 -0
  169. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/retargetinglists.xml +0 -0
  170. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/sitelinks.xml +0 -0
  171. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/smartadtargets.xml +0 -0
  172. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/strategies.xml +0 -0
  173. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/tests/wsdl_cache/turbopages.xml +0 -0
  174. {direct_cli-0.2.9.1 → direct_cli-0.2.10}/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.1
3
+ Version: 0.2.10
4
4
  Summary: Command-line interface for Yandex Direct API
5
5
  Author: axisrow
6
6
  License: MIT
@@ -145,6 +145,17 @@ the runtime CLI by default. If compatibility is ever needed, an alias must be
145
145
  added as an explicit exception with the concrete legacy syntax that still has to
146
146
  be supported.
147
147
 
148
+ #### Removed Legacy Names
149
+
150
+ | Legacy name | Canonical name |
151
+ |----------------------------|------------------------------|
152
+ | `dynamictargets` | `dynamicads` |
153
+ | `smarttargets` | `smartadtargets` |
154
+ | `negativekeywords` | `negativekeywordsharedsets` |
155
+ | `list` | `get` |
156
+ | `checkcamp` | `check-campaigns` |
157
+ | `checkdict` | `check-dictionaries` |
158
+
148
159
  #### Input Rules
149
160
 
150
161
  - All user-facing input must be passed only through typed CLI flags.
@@ -177,7 +188,7 @@ direct dictionaries get-geo-regions \
177
188
  #### Flag Design Rules
178
189
 
179
190
  - 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.
191
+ - 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
192
  - Selector fields remain explicit flags, for example:
182
193
  - `--id`
183
194
  - `--campaign-id`
@@ -221,7 +232,7 @@ direct campaigns get --ids 1,2,3
221
232
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
222
233
  direct keywordsresearch has-search-volume --keywords "buy laptop,buy desktop"
223
234
  direct smartadtargets update --id 456 --priority HIGH
224
- direct dynamicads set-bids --id 789 --bid 12.5 --context-bid 9 --priority HIGH
235
+ direct dynamicads set-bids --id 789 --bid 12500000 --context-bid 9000000 --priority HIGH
225
236
  direct dictionaries get-geo-regions --name Moscow --region-ids 225,187 --exact-names Москва,Санкт-Петербург --fields GeoRegionId,GeoRegionName
226
237
  ```
227
238
 
@@ -229,7 +240,7 @@ Invalid examples:
229
240
 
230
241
  ```bash
231
242
  direct dictionaries get-geo-regions --json '{"GeoRegionIds":[225]}' --fields GeoRegionId,GeoRegionName
232
- direct dynamicads set-bids --id 789 --bid 12.5 --json '{"StrategyPriority":"HIGH"}'
243
+ direct dynamicads set-bids --id 789 --bid 12500000 --json '{"StrategyPriority":"HIGH"}'
233
244
  direct dictionaries get-geo-regions \
234
245
  --region-ids 225 \
235
246
  --fields GeoRegionId,GeoRegionName
@@ -247,12 +258,12 @@ direct campaigns get --ids 1,2,3 --format table
247
258
  direct campaigns get --fetch-all --format csv --output campaigns.csv
248
259
 
249
260
  # 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
261
+ 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
262
  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
263
+ 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
264
 
254
265
  # 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
266
+ direct campaigns update --id 12345 --name "New Name" --status SUSPENDED --budget 100000000 --start-date 2024-02-10 --end-date 2024-03-01
256
267
  direct campaigns suspend --id 12345
257
268
  direct campaigns resume --id 12345
258
269
  direct campaigns archive --id 12345
@@ -286,7 +297,7 @@ direct ads delete --id 99999
286
297
 
287
298
  ```bash
288
299
  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
300
+ 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
301
  direct keywords update --id 88888 --keyword "updated keyword text"
291
302
  direct keywords delete --id 88888
292
303
  ```
@@ -324,22 +335,22 @@ direct retargeting add --name "List A" --type AUDIENCE --rule "ALL:12345:30|6789
324
335
  direct retargeting update --id 55 --name "Renamed" --rule "ANY:12345:30" --dry-run
325
336
 
326
337
  # 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
338
+ direct bids set --keyword-id 123 --bid 15000000
339
+ direct bids set-auto --keyword-id 123 --max-bid 20000000 --position PREMIUMBLOCK --scope SEARCH --dry-run
340
+ direct keywordbids set --keyword-id 321 --search-bid 8000000 --network-bid 3000000
341
+ direct keywordbids set-auto --keyword-id 321 --target-traffic-volume 100 --increase-percent 10 --bid-ceiling 12500000 --dry-run
331
342
  direct bidmodifiers add --campaign-id 123 --type DEMOGRAPHICS_ADJUSTMENT --value 150 --gender GENDER_MALE --age AGE_25_34 --dry-run
332
343
  direct bidmodifiers set --id 99 --value 130 --dry-run
333
344
 
334
345
  # Canonical multiword groups
335
346
  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
347
+ direct audiencetargets add --adgroup-id 100 --retargeting-list-id 200 --bid 12000000 --priority HIGH --dry-run
348
+ direct audiencetargets set-bids --id 101 --context-bid 7000000 --priority LOW --dry-run
349
+ 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
350
+ 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
351
  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
352
+ direct smartadtargets set-bids --id 456 --average-cpc 10500000 --average-cpa 15000000 --priority HIGH
353
+ direct dynamicads set-bids --id 789 --bid 12500000 --context-bid 9000000 --priority HIGH
343
354
 
344
355
  # Shared bidding strategies
345
356
  direct strategies get --limit 5
@@ -349,8 +360,8 @@ direct strategies archive --id 42 --dry-run
349
360
 
350
361
  # Dynamic feed ad targets
351
362
  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
363
+ direct dynamicfeedadtargets add --adgroup-id 33 --name "Feed slice A" --condition "CATEGORY:EQUALS:shoes" --bid 5000000 --dry-run
364
+ direct dynamicfeedadtargets set-bids --id 789 --bid 6500000 --context-bid 4000000 --dry-run
354
365
 
355
366
  # Extensions, assets, feeds, and clients
356
367
  direct sitelinks add --sitelink "Docs|https://example.com/docs" --sitelink "Help|https://example.com/help|Desk" --dry-run
@@ -409,7 +420,7 @@ The following commands make **irreversible changes** — use with caution:
409
420
  | `keywords delete --id` | Permanently deletes a keyword |
410
421
  | `audiencetargets delete --id` | Permanently deletes an audience target |
411
422
 
412
- Commands that affect live ad delivery: `suspend`, `resume`, `archive`, `unarchive` (available on `campaigns`, `ads`, `keywords`).
423
+ Commands that affect live ad delivery: `suspend`, `resume`, `archive`, `unarchive` (available on `campaigns`, `ads`), `suspend`, `resume` (also on `keywords`).
413
424
 
414
425
  Commands that affect bids and spending: `bids set`, `keywordbids set`, `bidmodifiers set`.
415
426
 
@@ -439,6 +450,27 @@ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v # live draft cas
439
450
  YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v --record-mode=rewrite # re-record live draft cassette
440
451
  ```
441
452
 
453
+ #### Smoke command scripts
454
+
455
+ Every CLI subcommand is classified in `direct_cli/smoke_matrix.py`.
456
+
457
+ | Category | Script | When to run |
458
+ |---|---|---|
459
+ | SAFE | `scripts/test_safe_commands.sh` | Production read-only smoke checks; requires `YANDEX_DIRECT_TOKEN` and `YANDEX_DIRECT_LOGIN` |
460
+ | 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 |
461
+ | DANGEROUS | `scripts/test_dangerous_commands.sh` | Manual checklist only; exits with status 1 by design |
462
+
463
+ Current command surface:
464
+
465
+ | Metric | Count |
466
+ |---|---:|
467
+ | WSDL-backed API services | 29 |
468
+ | Supported API services including Reports | 30 |
469
+ | WSDL operations | 112 |
470
+ | CLI groups including `auth` | 31 |
471
+ | CLI subcommands including `auth` | 122 |
472
+ | API CLI subcommands excluding `auth` | 118 |
473
+
442
474
  ### API Coverage And Drift Monitoring
443
475
 
444
476
  The project now distinguishes four surfaces:
@@ -471,24 +503,43 @@ CI runs a scheduled API coverage workflow that:
471
503
  parity and live-discovered model gap counts;
472
504
  - checks the cached WSDL files against the live Yandex Direct API on schedule.
473
505
 
474
- #### Re-recording write cassettes
506
+ #### Live sandbox write smoke
475
507
 
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.
508
+ `WRITE_SANDBOX` smoke is a live check against the Yandex Direct **sandbox**.
509
+ It does not replay stored HTTP traffic and it does not create new recordings.
510
+ Run it only when you intentionally want to call `api-sandbox.direct.yandex.com`:
511
+
512
+ ```bash
513
+ set -a && source .env && set +a
514
+ scripts/test_sandbox_write.sh
515
+ ```
516
+
517
+ The runner executes matrix commands through `direct --sandbox ...`, creates
518
+ temporary sandbox prerequisites where possible, and cleans them up best-effort.
519
+ The report contains one row per `WRITE_SANDBOX` command:
520
+
521
+ - `PASS` means the command completed against the live sandbox API.
522
+ - `SANDBOX_LIMITATION` means the request reached the API and hit a known
523
+ sandbox-only limitation such as codes `8800`, `1000`, `3500`, or `5004`.
524
+ - `FAIL` means an unexpected CLI or API error.
525
+ - `NOT_COVERED` means the runner does not yet know how to safely build the
526
+ prerequisites for that command.
527
+
528
+ The same OAuth token works for both production and the sandbox; no separate
529
+ sandbox token is needed.
530
+
531
+ #### Re-recording write cassettes
479
532
 
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:
533
+ The `integration_write` pytest tier still replays stored write-test traffic
534
+ for regression coverage. If you change those tests or their payloads and
535
+ intentionally need to refresh the fixtures, regenerate them separately:
483
536
 
484
537
  ```bash
485
538
  set -a && source .env && set +a # load YANDEX_DIRECT_TOKEN / LOGIN
486
539
  pytest -m integration_write -v --record-mode=rewrite
487
540
  ```
488
541
 
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**:
542
+ After recording, **always audit the generated YAMLs for leaked secrets**:
492
543
 
493
544
  ```bash
494
545
  grep -r "$YANDEX_DIRECT_TOKEN" tests/cassettes/ # must return nothing
@@ -624,6 +675,17 @@ Command naming rules:
624
675
  должен быть добавлен как явное exception-правило с конкретным legacy syntax из
625
676
  `tapi-yandex-direct`, который действительно нужно поддержать.
626
677
 
678
+ Удалённые исторические имена:
679
+
680
+ | Историческое имя | Каноническое имя |
681
+ |----------------------------|------------------------------|
682
+ | `dynamictargets` | `dynamicads` |
683
+ | `smarttargets` | `smartadtargets` |
684
+ | `negativekeywords` | `negativekeywordsharedsets` |
685
+ | `list` | `get` |
686
+ | `checkcamp` | `check-campaigns` |
687
+ | `checkdict` | `check-dictionaries` |
688
+
627
689
  `direct` — это канонический transport entrypoint над API Яндекс Директа,
628
690
  устанавливаемый пакетом `direct-cli`. Канонические имена CLI-групп следуют
629
691
  нормализованным Python-именам из `tapi-yandex-direct`, а имена подкоманд —
@@ -704,7 +766,7 @@ direct dictionaries get-geo-regions \
704
766
  #### Flag Design Rules
705
767
 
706
768
  - 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.
769
+ - 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
770
  - Selector fields remain explicit flags, for example:
709
771
  - `--id`
710
772
  - `--campaign-id`
@@ -747,7 +809,7 @@ Valid canonical examples:
747
809
  direct campaigns get --ids 1,2,3
748
810
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
749
811
  direct keywordsresearch has-search-volume --keywords "buy laptop,buy desktop"
750
- direct dynamicads set-bids --id 789 --bid 12.5
812
+ direct dynamicads set-bids --id 789 --bid 12500000
751
813
  direct dictionaries get-geo-regions --region-ids 225 --fields GeoRegionId,GeoRegionName
752
814
  ```
753
815
 
@@ -755,7 +817,7 @@ Invalid examples:
755
817
 
756
818
  ```bash
757
819
  direct dictionaries get-geo-regions --json '{"GeoRegionIds":[225]}' --fields GeoRegionId,GeoRegionName
758
- direct dynamicads set-bids --id 789 --bid 12.5 --json '{"StrategyPriority":"HIGH"}'
820
+ direct dynamicads set-bids --id 789 --bid 12500000 --json '{"StrategyPriority":"HIGH"}'
759
821
  direct dictionaries get-geo-regions \
760
822
  --region-ids 225 \
761
823
  --fields GeoRegionId,GeoRegionName
@@ -773,12 +835,12 @@ direct campaigns get --ids 1,2,3 --format table
773
835
  direct campaigns get --fetch-all --format csv --output campaigns.csv
774
836
 
775
837
  # Создать (--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
838
+ 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
839
  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
840
+ 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
841
 
780
842
  # Обновление и управление статусом
781
- direct campaigns update --id 12345 --name "Новое название" --status SUSPENDED --budget 100 --start-date 2024-02-10 --end-date 2024-03-01
843
+ direct campaigns update --id 12345 --name "Новое название" --status SUSPENDED --budget 100000000 --start-date 2024-02-10 --end-date 2024-03-01
782
844
  direct campaigns suspend --id 12345
783
845
  direct campaigns resume --id 12345
784
846
  direct campaigns archive --id 12345
@@ -812,7 +874,7 @@ direct ads delete --id 99999
812
874
 
813
875
  ```bash
814
876
  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
877
+ 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
878
  direct keywords update --id 88888 --keyword "updated keyword text"
817
879
  direct keywords delete --id 88888
818
880
  ```
@@ -850,22 +912,22 @@ direct retargeting add --name "Список A" --type AUDIENCE --rule "ALL:12345
850
912
  direct retargeting update --id 55 --name "Переименованный список" --rule "ANY:12345:30" --dry-run
851
913
 
852
914
  # Ставки и модификаторы
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
915
+ direct bids set --keyword-id 123 --bid 15000000
916
+ direct bids set-auto --keyword-id 123 --max-bid 20000000 --position PREMIUMBLOCK --scope SEARCH --dry-run
917
+ direct keywordbids set --keyword-id 321 --search-bid 8000000 --network-bid 3000000
918
+ direct keywordbids set-auto --keyword-id 321 --target-traffic-volume 100 --increase-percent 10 --bid-ceiling 12500000 --dry-run
857
919
  direct bidmodifiers add --campaign-id 123 --type DEMOGRAPHICS_ADJUSTMENT --value 150 --gender GENDER_MALE --age AGE_25_34 --dry-run
858
920
  direct bidmodifiers set --id 99 --value 130 --dry-run
859
921
 
860
922
  # Канонические многословные группы
861
923
  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
924
+ direct audiencetargets add --adgroup-id 100 --retargeting-list-id 200 --bid 12000000 --priority HIGH --dry-run
925
+ direct audiencetargets set-bids --id 101 --context-bid 7000000 --priority LOW --dry-run
926
+ 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
927
+ 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
928
  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
929
+ direct smartadtargets set-bids --id 456 --average-cpc 10500000 --average-cpa 15000000 --priority HIGH
930
+ direct dynamicads set-bids --id 789 --bid 12500000 --context-bid 9000000 --priority HIGH
869
931
 
870
932
  # Общие стратегии ставок
871
933
  direct strategies get --limit 5
@@ -875,8 +937,8 @@ direct strategies archive --id 42 --dry-run
875
937
 
876
938
  # Динамические таргеты по фиду
877
939
  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
940
+ direct dynamicfeedadtargets add --adgroup-id 33 --name "Срез фида А" --condition "CATEGORY:EQUALS:shoes" --bid 5000000 --dry-run
941
+ direct dynamicfeedadtargets set-bids --id 789 --bid 6500000 --context-bid 4000000 --dry-run
880
942
 
881
943
  # Расширения, ассеты, фиды и клиенты
882
944
  direct sitelinks add --sitelink "Docs|https://example.com/docs" --sitelink "Help|https://example.com/help|Desk" --dry-run
@@ -936,7 +998,7 @@ direct campaigns get --fetch-all # все страницы
936
998
  | `keywords delete --id` | Безвозвратно удаляет ключевое слово |
937
999
  | `audiencetargets delete --id` | Безвозвратно удаляет условие подбора аудитории |
938
1000
 
939
- Команды, влияющие на показ рекламы: `suspend`, `resume`, `archive`, `unarchive` (доступны для `campaigns`, `ads`, `keywords`).
1001
+ Команды, влияющие на показ рекламы: `suspend`, `resume`, `archive`, `unarchive` (доступны для `campaigns`, `ads`), `suspend`, `resume` (также для `keywords`).
940
1002
 
941
1003
  Команды, влияющие на ставки и расходы: `bids set`, `keywordbids set`, `bidmodifiers set`.
942
1004
 
@@ -966,24 +1028,64 @@ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v # replay live dr
966
1028
  YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v --record-mode=rewrite # перезапись live draft-кассеты
967
1029
  ```
968
1030
 
969
- #### Перезапись write-кассет
1031
+ #### Smoke-скрипты команд
1032
+
1033
+ Каждая CLI-подкоманда классифицирована в `direct_cli/smoke_matrix.py`.
1034
+
1035
+ | Категория | Скрипт | Когда запускать |
1036
+ |---|---|---|
1037
+ | SAFE | `scripts/test_safe_commands.sh` | Production smoke-проверки только на чтение; нужны `YANDEX_DIRECT_TOKEN` и `YANDEX_DIRECT_LOGIN` |
1038
+ | WRITE_SANDBOX | `scripts/test_sandbox_write.sh` | Live sandbox write-проверки; нужны `YANDEX_DIRECT_TOKEN` и `YANDEX_DIRECT_LOGIN`; отчёт печатает `PASS`, `FAIL`, `SANDBOX_LIMITATION` или `NOT_COVERED` для каждой команды |
1039
+ | DANGEROUS | `scripts/test_dangerous_commands.sh` | Только ручной checklist; специально завершается с кодом 1 |
1040
+
1041
+ Текущая поверхность команд:
1042
+
1043
+ | Метрика | Количество |
1044
+ |---|---:|
1045
+ | WSDL-backed API services | 29 |
1046
+ | API services с учётом Reports | 30 |
1047
+ | WSDL operations | 112 |
1048
+ | CLI groups с `auth` | 31 |
1049
+ | CLI subcommands с `auth` | 122 |
1050
+ | API CLI subcommands без `auth` | 118 |
1051
+
1052
+ #### Live sandbox write smoke
970
1053
 
971
- Write-тесты воспроизводят HTTP-трафик, записанный против **sandbox-окружения**
972
- Яндекс Директа (`--sandbox` подставляется автоматически). Кассеты лежат в
973
- `tests/cassettes/test_integration_write/` и закоммичены в git.
1054
+ `WRITE_SANDBOX` smoke это live-проверка против **sandbox-окружения**
1055
+ Яндекс Директа. Она не воспроизводит сохранённый HTTP-трафик и не создаёт
1056
+ новые записи. Запускайте её только когда намеренно хотите обратиться к
1057
+ `api-sandbox.direct.yandex.com`:
1058
+
1059
+ ```bash
1060
+ set -a && source .env && set +a
1061
+ scripts/test_sandbox_write.sh
1062
+ ```
1063
+
1064
+ Runner выполняет команды matrix через `direct --sandbox ...`, создаёт
1065
+ временные sandbox prerequisites там, где это безопасно, и удаляет их
1066
+ best-effort. Отчёт содержит одну строку на каждую команду `WRITE_SANDBOX`:
1067
+
1068
+ - `PASS`: команда прошла против live sandbox API.
1069
+ - `SANDBOX_LIMITATION`: запрос дошёл до API и получил известное ограничение
1070
+ sandbox, например коды `8800`, `1000`, `3500` или `5004`.
1071
+ - `FAIL`: неожиданный CLI/API error.
1072
+ - `NOT_COVERED`: runner пока не умеет безопасно построить prerequisites.
1073
+
1074
+ Один и тот же OAuth-токен работает и для продакшена, и для sandbox; отдельный
1075
+ sandbox-токен не нужен.
1076
+
1077
+ #### Перезапись write-кассет
974
1078
 
975
- Если меняется payload какой-то из write-команд (например, добавили поле),
976
- соответствующая кассета перестанет воспроизводиться, тест упадёт с
977
- body-mismatch. Перезапись:
1079
+ Уровень `integration_write` в pytest всё ещё воспроизводит сохранённый
1080
+ write-трафик для регрессионного покрытия. Если вы меняете эти тесты или их
1081
+ payload и намеренно хотите обновить fixtures, перезаписывайте их отдельно:
978
1082
 
979
1083
  ```bash
980
1084
  set -a && source .env && set +a # загрузить YANDEX_DIRECT_TOKEN / LOGIN
981
1085
  pytest -m integration_write -v --record-mode=rewrite
982
1086
  ```
983
1087
 
984
- **Один и тот же OAuth-токен работает и для продакшена, и для sandbox** —
985
- отдельный sandbox-токен не нужен. После перезаписи **обязательно проверьте
986
- YAML-ы на утечку секретов**:
1088
+ После перезаписи **обязательно проверьте YAML-ы на утечку секретов**:
987
1089
 
988
1090
  ```bash
989
1091
  grep -r "$YANDEX_DIRECT_TOKEN" tests/cassettes/ # должно быть пусто