direct-cli 0.4.1__tar.gz → 0.4.3__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 (379) hide show
  1. {direct_cli-0.4.1 → direct_cli-0.4.3}/CHANGELOG.md +258 -0
  2. {direct_cli-0.4.1 → direct_cli-0.4.3}/CLAUDE.md +3 -3
  3. {direct_cli-0.4.1 → direct_cli-0.4.3}/PKG-INFO +45 -22
  4. {direct_cli-0.4.1 → direct_cli-0.4.3}/README.md +44 -21
  5. direct_cli-0.4.3/direct_cli/_autotargeting.py +206 -0
  6. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/_bidding_strategy.py +876 -882
  7. direct_cli-0.4.3/direct_cli/_flag_validation.py +53 -0
  8. direct_cli-0.4.3/direct_cli/_smoke_probes.py +173 -0
  9. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/_vendor/tapi_yandex_direct/resource_mapping.py +12 -4
  10. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.py +1 -1
  11. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.pyi +4 -2
  12. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/api.py +23 -0
  13. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/auth.py +179 -68
  14. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/cli.py +10 -1
  15. direct_cli-0.4.3/direct_cli/commands/_batch.py +181 -0
  16. direct_cli-0.4.3/direct_cli/commands/_lifecycle.py +99 -0
  17. direct_cli-0.4.3/direct_cli/commands/adextensions.py +124 -0
  18. direct_cli-0.4.3/direct_cli/commands/adgroups.py +2092 -0
  19. direct_cli-0.4.3/direct_cli/commands/adimages.py +122 -0
  20. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/commands/ads.py +1654 -1058
  21. direct_cli-0.4.3/direct_cli/commands/advideos.py +96 -0
  22. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/commands/agencyclients.py +150 -207
  23. direct_cli-0.4.3/direct_cli/commands/audiencetargets.py +210 -0
  24. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/commands/auth.py +139 -7
  25. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/commands/balance.py +12 -25
  26. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/commands/bidmodifiers.py +169 -248
  27. direct_cli-0.4.3/direct_cli/commands/bids.py +226 -0
  28. direct_cli-0.4.3/direct_cli/commands/businesses.py +57 -0
  29. direct_cli-0.4.3/direct_cli/commands/campaigns.py +7207 -0
  30. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/commands/changes.py +30 -49
  31. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/commands/clients.py +93 -126
  32. direct_cli-0.4.3/direct_cli/commands/creatives.py +150 -0
  33. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/commands/dictionaries.py +26 -42
  34. direct_cli-0.4.3/direct_cli/commands/dynamicads.py +203 -0
  35. direct_cli-0.4.3/direct_cli/commands/dynamicfeedadtargets.py +192 -0
  36. direct_cli-0.4.3/direct_cli/commands/feeds.py +404 -0
  37. direct_cli-0.4.3/direct_cli/commands/keywordbids.py +316 -0
  38. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/commands/keywords.py +181 -514
  39. direct_cli-0.4.3/direct_cli/commands/keywordsresearch.py +68 -0
  40. direct_cli-0.4.3/direct_cli/commands/leads.py +80 -0
  41. direct_cli-0.4.3/direct_cli/commands/negativekeywordsharedsets.py +130 -0
  42. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/commands/reports.py +63 -67
  43. direct_cli-0.4.3/direct_cli/commands/retargeting.py +216 -0
  44. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/commands/sitelinks.py +67 -105
  45. direct_cli-0.4.3/direct_cli/commands/smartadtargets.py +296 -0
  46. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/commands/strategies.py +195 -289
  47. direct_cli-0.4.3/direct_cli/commands/turbopages.py +67 -0
  48. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/commands/v4account.py +6 -77
  49. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/commands/v4adimage.py +6 -44
  50. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/commands/v4events.py +6 -31
  51. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/commands/v4finance.py +38 -136
  52. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/commands/v4forecast.py +12 -84
  53. direct_cli-0.4.3/direct_cli/commands/v4goals.py +47 -0
  54. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/commands/v4keywords.py +6 -30
  55. direct_cli-0.4.3/direct_cli/commands/v4shells.py +54 -0
  56. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/commands/v4tags.py +10 -78
  57. direct_cli-0.4.3/direct_cli/commands/v4wordstat.py +90 -0
  58. direct_cli-0.4.3/direct_cli/commands/vcards.py +247 -0
  59. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/output.py +34 -0
  60. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/smoke_matrix.py +4 -0
  61. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/adgroups.json +42 -2
  62. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/ads.json +40 -3
  63. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/audiencetargets.json +2 -1
  64. direct_cli-0.4.3/direct_cli/translations/auth.json +25 -0
  65. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/common.json +4 -3
  66. direct_cli-0.4.3/direct_cli/translations/v4shells.json +7 -0
  67. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/utils.py +166 -6
  68. direct_cli-0.4.3/direct_cli/v4/emit.py +79 -0
  69. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/v4/money.py +8 -27
  70. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli.egg-info/PKG-INFO +45 -22
  71. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli.egg-info/SOURCES.txt +21 -0
  72. {direct_cli-0.4.1 → direct_cli-0.4.3}/pyproject.toml +1 -1
  73. {direct_cli-0.4.1 → direct_cli-0.4.3}/scripts/build_api_coverage_report.py +11 -0
  74. {direct_cli-0.4.1 → direct_cli-0.4.3}/scripts/check_all_docs_urls.py +30 -13
  75. {direct_cli-0.4.1 → direct_cli-0.4.3}/scripts/patch_vendor_imports.py +109 -2
  76. direct_cli-0.4.3/tests/MANUAL_COVERAGE.md +147 -0
  77. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/WSDL_OPTIONAL_FIELD_AUDIT.md +3 -3
  78. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/api_coverage_payloads.py +5 -0
  79. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[creatives_get].yaml +2 -2
  80. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[retargeting_get].yaml +2 -2
  81. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[strategies_get].yaml +2 -2
  82. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_v5_live_write/test_v5_live_draft_ads_add_update_delete.yaml +64 -64
  83. direct_cli-0.4.3/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_add_delete.yaml +560 -0
  84. direct_cli-0.4.3/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_suspend_resume.yaml +622 -0
  85. direct_cli-0.4.3/tests/cassettes/test_v5_live_write/test_v5_live_draft_feeds_add_update_delete.yaml +250 -0
  86. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywords_add_update_delete.yaml +122 -60
  87. direct_cli-0.4.1/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_suspend_resume.yaml → direct_cli-0.4.3/tests/cassettes/test_v5_live_write/test_v5_live_draft_retargeting_add_update_delete.yaml +40 -103
  88. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_v5_live_write/test_v5_live_draft_smartadtargets_add_update_delete.yaml +24 -24
  89. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_v5_live_write/test_v5_live_draft_smartadtargets_suspend_resume.yaml +24 -24
  90. direct_cli-0.4.1/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_add_delete.yaml → direct_cli-0.4.3/tests/cassettes/test_v5_live_write/test_v5_live_draft_strategies_add_update_archive_unarchive.yaml +49 -50
  91. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/conftest.py +77 -6
  92. direct_cli-0.4.3/tests/test_adgroups_build_adgroup_object.py +215 -0
  93. direct_cli-0.4.3/tests/test_adgroups_build_adgroup_update_object.py +129 -0
  94. direct_cli-0.4.3/tests/test_ads_build_ad_object.py +201 -0
  95. direct_cli-0.4.3/tests/test_ads_build_ad_update_object.py +163 -0
  96. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_audit_wire_shape.py +37 -0
  97. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_auth_bw.py +30 -0
  98. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_auth_oauth.py +293 -5
  99. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_auth_op.py +30 -0
  100. direct_cli-0.4.3/tests/test_autotargeting.py +228 -0
  101. direct_cli-0.4.3/tests/test_bidding_strategy_constants.py +197 -0
  102. direct_cli-0.4.3/tests/test_cassette_integrity.py +87 -0
  103. direct_cli-0.4.3/tests/test_check_all_docs_urls.py +16 -0
  104. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_cli.py +6 -2
  105. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_dry_run.py +1858 -20
  106. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_env_loading.py +54 -0
  107. direct_cli-0.4.3/tests/test_field_names_option.py +85 -0
  108. direct_cli-0.4.3/tests/test_flag_validation.py +130 -0
  109. direct_cli-0.4.3/tests/test_handle_api_errors.py +101 -0
  110. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_integration.py +87 -14
  111. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_low_coverage_payloads.py +71 -0
  112. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_read_cassettes.py +6 -2
  113. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_transport_contract.py +20 -10
  114. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_v4_exit_codes.py +9 -9
  115. direct_cli-0.4.3/tests/test_v4_output_options.py +134 -0
  116. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_v4account.py +26 -26
  117. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_v4adimage.py +2 -2
  118. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_v4events.py +4 -4
  119. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_v4finance_money.py +42 -10
  120. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_v4finance_read.py +9 -9
  121. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_v4forecast.py +6 -6
  122. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_v4goals.py +6 -6
  123. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_v4keywords.py +2 -2
  124. direct_cli-0.4.3/tests/test_v4meta.py +71 -0
  125. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_v4tags.py +4 -4
  126. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_v4wordstat.py +7 -7
  127. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_v5_live_write.py +218 -7
  128. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_vendor_imports.py +119 -0
  129. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_wsdl_parity_gate.py +62 -9
  130. direct_cli-0.4.1/direct_cli/_smoke_probes.py +0 -94
  131. direct_cli-0.4.1/direct_cli/commands/adextensions.py +0 -164
  132. direct_cli-0.4.1/direct_cli/commands/adgroups.py +0 -1392
  133. direct_cli-0.4.1/direct_cli/commands/adimages.py +0 -158
  134. direct_cli-0.4.1/direct_cli/commands/advideos.py +0 -119
  135. direct_cli-0.4.1/direct_cli/commands/audiencetargets.py +0 -306
  136. direct_cli-0.4.1/direct_cli/commands/bids.py +0 -262
  137. direct_cli-0.4.1/direct_cli/commands/businesses.py +0 -67
  138. direct_cli-0.4.1/direct_cli/commands/campaigns.py +0 -7610
  139. direct_cli-0.4.1/direct_cli/commands/creatives.py +0 -173
  140. direct_cli-0.4.1/direct_cli/commands/dynamicads.py +0 -296
  141. direct_cli-0.4.1/direct_cli/commands/dynamicfeedadtargets.py +0 -299
  142. direct_cli-0.4.1/direct_cli/commands/feeds.py +0 -457
  143. direct_cli-0.4.1/direct_cli/commands/keywordbids.py +0 -331
  144. direct_cli-0.4.1/direct_cli/commands/keywordsresearch.py +0 -88
  145. direct_cli-0.4.1/direct_cli/commands/leads.py +0 -84
  146. direct_cli-0.4.1/direct_cli/commands/negativekeywordsharedsets.py +0 -180
  147. direct_cli-0.4.1/direct_cli/commands/retargeting.py +0 -244
  148. direct_cli-0.4.1/direct_cli/commands/smartadtargets.py +0 -397
  149. direct_cli-0.4.1/direct_cli/commands/turbopages.py +0 -79
  150. direct_cli-0.4.1/direct_cli/commands/v4goals.py +0 -106
  151. direct_cli-0.4.1/direct_cli/commands/v4shells.py +0 -20
  152. direct_cli-0.4.1/direct_cli/commands/v4wordstat.py +0 -160
  153. direct_cli-0.4.1/direct_cli/commands/vcards.py +0 -289
  154. direct_cli-0.4.1/direct_cli/translations/auth.json +0 -23
  155. direct_cli-0.4.1/direct_cli/translations/v4shells.json +0 -3
  156. direct_cli-0.4.1/tests/MANUAL_COVERAGE.md +0 -92
  157. {direct_cli-0.4.1 → direct_cli-0.4.3}/.env.example +0 -0
  158. {direct_cli-0.4.1 → direct_cli-0.4.3}/.github/copilot-instructions.md +0 -0
  159. {direct_cli-0.4.1 → direct_cli-0.4.3}/.github/workflows/api-coverage.yml +0 -0
  160. {direct_cli-0.4.1 → direct_cli-0.4.3}/.github/workflows/claude.yml +0 -0
  161. {direct_cli-0.4.1 → direct_cli-0.4.3}/.github/workflows/quality.yml +0 -0
  162. {direct_cli-0.4.1 → direct_cli-0.4.3}/.gitignore +0 -0
  163. {direct_cli-0.4.1 → direct_cli-0.4.3}/AGENTS.md +0 -0
  164. {direct_cli-0.4.1 → direct_cli-0.4.3}/MANIFEST.in +0 -0
  165. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/__init__.py +0 -0
  166. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/_deprecated.py +0 -0
  167. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/_vendor/__init__.py +0 -0
  168. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/_vendor/tapi_yandex_direct/__init__.py +0 -0
  169. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/_vendor/tapi_yandex_direct/endpoints.py +0 -0
  170. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/_vendor/tapi_yandex_direct/exceptions.py +0 -0
  171. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/_vendor/tapi_yandex_direct/v4/__init__.py +0 -0
  172. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.py +0 -0
  173. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.pyi +0 -0
  174. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/_vendor/tapi_yandex_direct/v4/resource_mapping.py +0 -0
  175. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/commands/__init__.py +0 -0
  176. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/i18n.py +0 -0
  177. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/reports_coverage.py +0 -0
  178. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/adextensions.json +0 -0
  179. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/adimages.json +0 -0
  180. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/advideos.json +0 -0
  181. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/agencyclients.json +0 -0
  182. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/balance.json +0 -0
  183. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/bidmodifiers.json +0 -0
  184. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/bids.json +0 -0
  185. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/businesses.json +0 -0
  186. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/campaigns.json +0 -0
  187. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/changes.json +0 -0
  188. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/clients.json +0 -0
  189. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/creatives.json +0 -0
  190. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/dictionaries.json +0 -0
  191. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/dynamicads.json +0 -0
  192. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/dynamicfeedadtargets.json +0 -0
  193. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/feeds.json +0 -0
  194. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/keywordbids.json +0 -0
  195. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/keywords.json +0 -0
  196. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/keywordsresearch.json +0 -0
  197. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/leads.json +0 -0
  198. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/negativekeywordsharedsets.json +0 -0
  199. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/reports.json +0 -0
  200. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/retargeting.json +0 -0
  201. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/sitelinks.json +0 -0
  202. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/smartadtargets.json +0 -0
  203. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/strategies.json +0 -0
  204. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/turbopages.json +0 -0
  205. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/v4account.json +0 -0
  206. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/v4adimage.json +0 -0
  207. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/v4events.json +0 -0
  208. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/v4finance.json +0 -0
  209. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/v4forecast.json +0 -0
  210. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/v4goals.json +0 -0
  211. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/v4keywords.json +0 -0
  212. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/v4tags.json +0 -0
  213. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/v4wordstat.json +0 -0
  214. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/translations/vcards.json +0 -0
  215. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/v4/__init__.py +0 -0
  216. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/v4_contracts.py +0 -0
  217. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli/wsdl_coverage.py +0 -0
  218. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli.egg-info/dependency_links.txt +0 -0
  219. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli.egg-info/entry_points.txt +0 -0
  220. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli.egg-info/requires.txt +0 -0
  221. {direct_cli-0.4.1 → direct_cli-0.4.3}/direct_cli.egg-info/top_level.txt +0 -0
  222. {direct_cli-0.4.1 → direct_cli-0.4.3}/docs/audits/API_COVERAGE.md +0 -0
  223. {direct_cli-0.4.1 → direct_cli-0.4.3}/docs/audits/PROJECT_WIRE_SHAPE_AUDIT_2026-05-30.md +0 -0
  224. {direct_cli-0.4.1 → direct_cli-0.4.3}/docs/audits/WIRE_SHAPE_TRIAGE_2026-05-30.md +0 -0
  225. {direct_cli-0.4.1 → direct_cli-0.4.3}/docs/audits/issue-198-mutating-wsdl-audit.md +0 -0
  226. {direct_cli-0.4.1 → direct_cli-0.4.3}/docs/audits/wire_shape.json +0 -0
  227. {direct_cli-0.4.1 → direct_cli-0.4.3}/docs/superpowers/plans/2026-04-12-issue-32-completion.md +0 -0
  228. {direct_cli-0.4.1 → direct_cli-0.4.3}/docs/superpowers/specs/2026-04-23-vendor-tapi-yandex-direct-design.md +0 -0
  229. {direct_cli-0.4.1 → direct_cli-0.4.3}/scripts/anonymize_cassettes.py +0 -0
  230. {direct_cli-0.4.1 → direct_cli-0.4.3}/scripts/audit_wire_shape.py +0 -0
  231. {direct_cli-0.4.1 → direct_cli-0.4.3}/scripts/build_api_coverage_checklist.py +0 -0
  232. {direct_cli-0.4.1 → direct_cli-0.4.3}/scripts/build_wsdl_optional_field_audit.py +0 -0
  233. {direct_cli-0.4.1 → direct_cli-0.4.3}/scripts/check_reports_drift.py +0 -0
  234. {direct_cli-0.4.1 → direct_cli-0.4.3}/scripts/check_wsdl_drift.py +0 -0
  235. {direct_cli-0.4.1 → direct_cli-0.4.3}/scripts/preflight_check.sh +0 -0
  236. {direct_cli-0.4.1 → direct_cli-0.4.3}/scripts/probe_drift_urls.sh +0 -0
  237. {direct_cli-0.4.1 → direct_cli-0.4.3}/scripts/refresh_reports_cache.py +0 -0
  238. {direct_cli-0.4.1 → direct_cli-0.4.3}/scripts/refresh_wsdl_cache.py +0 -0
  239. {direct_cli-0.4.1 → direct_cli-0.4.3}/scripts/release_pypi.sh +0 -0
  240. {direct_cli-0.4.1 → direct_cli-0.4.3}/scripts/sandbox_write_audit.py +0 -0
  241. {direct_cli-0.4.1 → direct_cli-0.4.3}/scripts/sandbox_write_live.py +0 -0
  242. {direct_cli-0.4.1 → direct_cli-0.4.3}/scripts/test_dangerous_commands.sh +0 -0
  243. {direct_cli-0.4.1 → direct_cli-0.4.3}/scripts/test_safe_commands.sh +0 -0
  244. {direct_cli-0.4.1 → direct_cli-0.4.3}/scripts/test_sandbox_write.sh +0 -0
  245. {direct_cli-0.4.1 → direct_cli-0.4.3}/scripts/update_vendor.sh +0 -0
  246. {direct_cli-0.4.1 → direct_cli-0.4.3}/setup.cfg +0 -0
  247. {direct_cli-0.4.1 → direct_cli-0.4.3}/setup.py +0 -0
  248. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/API_COVERAGE.md +0 -0
  249. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/API_ISSUE_AUDIT.md +0 -0
  250. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/__init__.py +0 -0
  251. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/_orphan_store.py +0 -0
  252. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteAdExtensions.test_add_delete.yaml +0 -0
  253. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteAdGroups.test_add_update_delete.yaml +0 -0
  254. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteAdImages.test_add_delete.yaml +0 -0
  255. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteAds.test_add_text_ad_update_delete.yaml +0 -0
  256. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteAudienceTargets.test_add_delete.yaml +0 -0
  257. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteBidModifiersAdd.test_add_delete_mobile.yaml +0 -0
  258. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteBidModifiersSet.test_set_without_id_is_rejected.yaml +0 -0
  259. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteBids.test_set_bid.yaml +0 -0
  260. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteBidsRead.test_bids_get.yaml +0 -0
  261. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteBidsRead.test_bids_set_auto.yaml +0 -0
  262. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteCampaignDraftLifecycle.test_draft_create_get_delete.yaml +0 -0
  263. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteCampaigns.test_campaign_lifecycle.yaml +0 -0
  264. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteDynamicAds.test_add_update_delete.yaml +0 -0
  265. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteFeeds.test_add_update_delete.yaml +0 -0
  266. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteKeywordBids.test_set_keyword_bid.yaml +0 -0
  267. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteKeywords.test_add_update_delete.yaml +0 -0
  268. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteNegativeKeywordSharedSets.test_add_update_delete.yaml +0 -0
  269. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteRetargeting.test_add_delete.yaml +0 -0
  270. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteRetargetingUpdate.test_retargeting_update.yaml +0 -0
  271. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteSitelinks.test_add_delete.yaml +0 -0
  272. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteSmartAdTargets.test_add_update_delete.yaml +0 -0
  273. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteStrategies.test_strategies_lifecycle.yaml +0 -0
  274. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_integration_write/TestWriteVCards.test_add_delete.yaml +0 -0
  275. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[adextensions_get].yaml +0 -0
  276. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[adgroups_get].yaml +0 -0
  277. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[adimages_get].yaml +0 -0
  278. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[ads_get].yaml +0 -0
  279. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[advideos_get].yaml +0 -0
  280. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[audiencetargets_get].yaml +0 -0
  281. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[bidmodifiers_get].yaml +0 -0
  282. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[bids_get].yaml +0 -0
  283. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[businesses_get].yaml +0 -0
  284. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[campaigns_get].yaml +0 -0
  285. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[changes_check].yaml +0 -0
  286. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[changes_check_campaigns].yaml +0 -0
  287. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[changes_check_dictionaries].yaml +0 -0
  288. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[clients_get].yaml +0 -0
  289. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[dictionaries_get].yaml +0 -0
  290. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[dynamicads_get].yaml +0 -0
  291. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[dynamicfeedadtargets_get].yaml +0 -0
  292. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[feeds_get].yaml +0 -0
  293. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[keywordbids_get].yaml +0 -0
  294. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[keywords_get].yaml +0 -0
  295. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[keywordsresearch_deduplicate].yaml +0 -0
  296. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[keywordsresearch_has_search_volume].yaml +0 -0
  297. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[leads_get].yaml +0 -0
  298. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[negativekeywordsharedsets_get].yaml +0 -0
  299. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[reports_get].yaml +0 -0
  300. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[sitelinks_get].yaml +0 -0
  301. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[smartadtargets_get].yaml +0 -0
  302. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[turbopages_get].yaml +0 -0
  303. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[v4events_get_events_log].yaml +0 -0
  304. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[v4finance_get_clients_units].yaml +0 -0
  305. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[v4forecast_list].yaml +0 -0
  306. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[v4goals_get_stat_goals].yaml +0 -0
  307. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[v4tags_get_campaigns].yaml +0 -0
  308. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[v4wordstat_list_reports].yaml +0 -0
  309. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_read_command[vcards_get].yaml +0 -0
  310. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_read_cassettes/test_v4finance_check_payment_unknown_transaction.yaml +0 -0
  311. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_v5_live_write/test_v5_live_draft_adgroups_add_update_delete.yaml +0 -0
  312. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_v5_live_write/test_v5_live_draft_adimages_add_get_delete.yaml +0 -0
  313. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_v5_live_write/test_v5_live_draft_ads_suspend_resume_archive_unarchive.yaml +0 -0
  314. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_v5_live_write/test_v5_live_draft_advideos_add_get.yaml +0 -0
  315. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_v5_live_write/test_v5_live_draft_bids_set.yaml +0 -0
  316. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_v5_live_write/test_v5_live_draft_campaign_create_get_delete.yaml +0 -0
  317. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_v5_live_write/test_v5_live_draft_creatives_chain_advideo_to_creative.yaml +0 -0
  318. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_v5_live_write/test_v5_live_draft_dynamicads_add_delete.yaml +0 -0
  319. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_v5_live_write/test_v5_live_draft_dynamicads_suspend_resume.yaml +0 -0
  320. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywordbids_set.yaml +0 -0
  321. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywords_suspend_resume.yaml +0 -0
  322. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/cassettes/test_v5_live_write/test_v5_live_draft_sitelinks_add_get_delete.yaml +0 -0
  323. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/fixtures/test-video.mp4 +0 -0
  324. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/reports_cache/raw/fields-list.html +0 -0
  325. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/reports_cache/raw/headers.html +0 -0
  326. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/reports_cache/raw/period.html +0 -0
  327. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/reports_cache/raw/spec.html +0 -0
  328. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/reports_cache/raw/type.html +0 -0
  329. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/reports_cache/spec.json +0 -0
  330. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_api_coverage.py +0 -0
  331. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_auth_write_json.py +0 -0
  332. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_balance.py +0 -0
  333. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_changes.py +0 -0
  334. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_cli_contract.py +0 -0
  335. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_comprehensive.py +0 -0
  336. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_i18n.py +0 -0
  337. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_integration_write.py +0 -0
  338. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_reports_drift.py +0 -0
  339. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_reports_parsing.py +0 -0
  340. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_sandbox_write_audit.py +0 -0
  341. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_smoke_matrix.py +0 -0
  342. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_unknown_option_hints.py +0 -0
  343. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_v4_contracts.py +0 -0
  344. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_v4_foundation.py +0 -0
  345. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_v4_live_contracts.py +0 -0
  346. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_v4_runtime_shape.py +0 -0
  347. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/test_v4_safety.py +0 -0
  348. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/adextensions.xml +0 -0
  349. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/adgroups.xml +0 -0
  350. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/adimages.xml +0 -0
  351. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/ads.xml +0 -0
  352. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/advideos.xml +0 -0
  353. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/agencyclients.xml +0 -0
  354. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/audiencetargets.xml +0 -0
  355. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/bidmodifiers.xml +0 -0
  356. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/bids.xml +0 -0
  357. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/businesses.xml +0 -0
  358. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/campaigns.xml +0 -0
  359. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/changes.xml +0 -0
  360. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/clients.xml +0 -0
  361. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/creatives.xml +0 -0
  362. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/dictionaries.xml +0 -0
  363. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/dynamicfeedadtargets.xml +0 -0
  364. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/dynamictextadtargets.xml +0 -0
  365. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/feeds.xml +0 -0
  366. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/imports/adextensiontypes.xsd +0 -0
  367. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/imports/general.xsd +0 -0
  368. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/imports/generalclients.xsd +0 -0
  369. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/keywordbids.xml +0 -0
  370. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/keywords.xml +0 -0
  371. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/keywordsresearch.xml +0 -0
  372. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/leads.xml +0 -0
  373. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/negativekeywordsharedsets.xml +0 -0
  374. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/retargetinglists.xml +0 -0
  375. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/sitelinks.xml +0 -0
  376. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/smartadtargets.xml +0 -0
  377. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/strategies.xml +0 -0
  378. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/turbopages.xml +0 -0
  379. {direct_cli-0.4.1 → direct_cli-0.4.3}/tests/wsdl_cache/vcards.xml +0 -0
@@ -1,5 +1,263 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.3
4
+
5
+ **Features — batch `ads add` via `--from-file` / `--ads-json` (#562, #558 follow-up):**
6
+
7
+ - `ads add` now accepts a batch of flag-form ad rows from a JSONL file
8
+ (`--from-file`) or an inline JSON array (`--ads-json`); each row is the same
9
+ flag set keyed by the kebab flag name without the leading dashes (e.g.
10
+ `{"type":"TEXT_AD","title":"...","text":"...","href":"...","adgroup-id":1}`).
11
+ `--adgroup-id` becomes the batch default and may be overridden per row. Single
12
+ typed-flag mode is unchanged.
13
+ - The ~400-line flag→object logic of `ads add` was extracted into a reusable,
14
+ ctx-free `build_ad_object()` so the single-flag command and the batch
15
+ normalizer emit byte-identical ad objects (golden-tested across every
16
+ subtype).
17
+ - New shared `direct_cli/commands/_batch.py` engine (JSONL/inline loading,
18
+ chunking, per-chunk send with partial-success reporting, dry-run preview,
19
+ `add`/`update`-aware result key). `keywords add` was migrated onto it with no
20
+ behavior change (its existing batch suite is the proof).
21
+ - Chunk size `ADS_ADD_MAX_BATCH = 100` (conservative chunk, not the 1000-object
22
+ API ceiling — a partial failure rolls back at most 100 ads).
23
+
24
+ **Features — batch `ads update` via `--from-file` / `--ads-json` (#563, #558 follow-up):**
25
+
26
+ - `ads update` now accepts a batch of flag-form ad-update rows from a JSONL file
27
+ (`--from-file`) or an inline JSON array (`--ads-json`); each row is the same
28
+ flag set keyed by the kebab flag name without the leading dashes plus its own
29
+ `id` and `type` (e.g. `{"id":5,"type":"TEXT_AD","title":"New"}`). The
30
+ `--clear-image-hash` mechanic works per row as a JSON boolean. Single
31
+ typed-flag mode is unchanged.
32
+ - The subtype-dispatch body of `ads update` (type validation, the
33
+ incompatible-flag / "does not convert between subtypes" guard, per-subtype
34
+ assembly, and the empty-subtype no-op guard) was extracted into a reusable,
35
+ ctx-free `build_ad_update_object()` so the single-flag command and the batch
36
+ normalizer emit byte-identical ad-update objects (golden-tested across every
37
+ subtype). Reuses the shared `_batch.py` engine with `method="update"` /
38
+ `result_key="UpdateResults"`.
39
+ - `--id` and `--type` become per-row in batch mode (each row carries its own);
40
+ single-item mode still requires both. The per-row normalizer reproduces the
41
+ command's `--id`/`--type` required checks, the `--image-hash` /
42
+ `--clear-image-hash` mutex, and the same Click-type coercion as the single
43
+ path (a JSON float `id` is rejected, not truncated).
44
+
45
+ **Features — batch `adgroups add` via `--from-file` / `--adgroups-json` (#564, #558 follow-up):**
46
+
47
+ - `adgroups add` now accepts a batch of flag-form ad-group rows from a JSONL
48
+ file (`--from-file`) or an inline JSON array (`--adgroups-json`); each row is
49
+ the same flag set keyed by the kebab flag name without the leading dashes
50
+ (e.g. `{"name":"G","campaign-id":12,"region-ids":"225","type":"TEXT_AD_GROUP"}`).
51
+ `--campaign-id` becomes the batch default and may be overridden per row.
52
+ Single typed-flag mode is unchanged.
53
+ - The flag→object logic of `adgroups add` (type validation, the
54
+ incompatible-flag guard, the negative-keyword compatibility check, region IDs,
55
+ and per-subtype assembly) was extracted into a reusable, ctx-free
56
+ `build_adgroup_object()` so the single-flag command and the batch normalizer
57
+ emit byte-identical ad-group objects (golden-tested across every subtype).
58
+ `--name` / `--campaign-id` / `--region-ids` become per-row in batch mode;
59
+ single-item mode still requires them (parity-gate `INTERNAL_VALIDATION`
60
+ entries). Per-row coercion runs every typed field through its single-flag
61
+ Click type (a JSON float `campaign-id` is rejected, not truncated).
62
+ - The shared `_batch.send_batch` gained an optional `post` callable so
63
+ `adgroups` keeps its endpoint routing: a `UnifiedAdGroup` payload must use API
64
+ v501 (`_post_adgroups`). Because that routing keys off the whole body, a batch
65
+ may **not** mix `UNIFIED_AD_GROUP` with other ad-group types — the CLI refuses
66
+ the mix up front with a clear `UsageError` rather than send non-unified groups
67
+ to the v501 endpoint.
68
+
69
+ **Features — batch `adgroups update` via `--from-file` / `--adgroups-json` (#565, #558 follow-up):**
70
+
71
+ - `adgroups update` now accepts a batch of flag-form ad-group-update rows from a
72
+ JSONL file (`--from-file`) or an inline JSON array (`--adgroups-json`); each
73
+ row is the same flag set keyed by the kebab flag name without the leading
74
+ dashes plus its own `id` (e.g. `{"id":5,"name":"New"}`). The `--dynamic-feed`
75
+ routing works per row as a JSON boolean. Single typed-flag mode is unchanged.
76
+ - The subtype-dispatch body of `adgroups update` (the mixed-subtype reject
77
+ guard, per-subtype assembly, the `--dynamic-feed` DynamicTextAdGroup ↔
78
+ DynamicTextFeedAdGroup routing, and the empty-payload no-op guard) was
79
+ extracted into a reusable, ctx-free `build_adgroup_update_object()` so the
80
+ single-flag command and the batch normalizer emit byte-identical objects
81
+ (golden-tested across every subtype). `--id` becomes per-row in batch mode;
82
+ single-item mode still requires it (parity-gate `INTERNAL_VALIDATION` entry).
83
+ Per-row coercion runs every typed field through its single-flag Click type (a
84
+ JSON float `id` is rejected, not truncated).
85
+ - Reuses the shared `_batch.send_batch` with `method="update"` /
86
+ `result_key="UpdateResults"` and the `post=_post_adgroups` endpoint routing.
87
+ As with `adgroups add`, a batch may **not** mix `UNIFIED_AD_GROUP` with other
88
+ ad-group types (unified groups use API v501) — the CLI refuses the mix up
89
+ front with a clear `UsageError`.
90
+
91
+ **Fixes — reject non-positive IDs before the request (#558):**
92
+
93
+ - Mutating commands and lifecycle ops took their object-ID selector
94
+ (`--id` / `--adgroup-id` / `--campaign-id` / `--keyword-id` / `--client-id`)
95
+ as a bare `int`, which accepted `0` and negatives and forwarded them to the
96
+ API (opaque rejection). Every such selector now uses `click.IntRange(min=1)`
97
+ and rejects a non-positive id with a clear `UsageError` (exit 2) before any
98
+ request. Coverage is the full mutation surface, not a subset:
99
+ - every `delete` / `suspend` / `resume` / `archive` / `unarchive` /
100
+ `moderate` lifecycle command (via the shared `_lifecycle.py` factory);
101
+ - `ads add` / `ads update`, `adgroups add` / `adgroups update`,
102
+ `keywords add` / `keywords update`;
103
+ - `campaigns update`, `feeds update`, `strategies update`,
104
+ `retargeting update`, `negativekeywordsharedsets update`, `vcards add`;
105
+ - `smartadtargets add` / `update` / `set-bids`,
106
+ `audiencetargets add` / `set-bids`, `dynamicads add` / `set-bids`,
107
+ `dynamicfeedadtargets add` / `set-bids`;
108
+ - the bid setters `bids set` / `set-auto`, `keywordbids set` / `set-auto`
109
+ (the `campaign-id` / `adgroup-id` / `keyword-id` "exactly one of" trios),
110
+ `bidmodifiers add` / `set`;
111
+ - `agencyclients update --client-id`.
112
+
113
+ The ad-image lifecycle (`--hash`, a string) is unchanged. Secondary
114
+ reference-ID flags that point at *other* objects inside a write payload
115
+ (e.g. `--feed-id`, `--counter-id`, `--vcard-id`, `--region-id`,
116
+ `--retargeting-list-id`) are left as-is for now and tracked as follow-up.
117
+ - Batch-size caps (the docs allow up to 1000 objects per add/update and 10000
118
+ ids per delete) are intentionally **not** added: the CLI builds a
119
+ single-item payload for every mutation, so there is no caller-controllable
120
+ array to overflow. Multi-item batch mode (`--from-file`) for ads/adgroups is
121
+ tracked as follow-up work.
122
+ - De-staled the `KEYWORDS_ADD_MAX_BATCH` comment: it claimed the API caps a
123
+ `keywords.add` request at 10 (citing an outdated doc page that states no such
124
+ number). The real documented per-call limit is 1000; the value `10` is a
125
+ conservative chunk size for batch add, not the API ceiling — comment fixed,
126
+ value unchanged.
127
+
128
+ **Fixes — explain Error 8300 on delete/moderate (#548):**
129
+
130
+ - `raise_for_api_result_errors` now appends a hint when the API returns code
131
+ 8300, mirroring the existing 8800 hint: the ad is likely not in `DRAFT`
132
+ status, and `Status=UNKNOWN` is an API fallback value (a status outside the
133
+ v5 enum), not a business status — such ads can only be archived/unarchived,
134
+ not deleted or sent to moderation. Covers `ads delete` / `ads moderate` and
135
+ any command routing through `format_output`. English-only, matching the 8800
136
+ hint (`output.py` does not import i18n).
137
+
138
+ **Docs — audiencetargets get requires a filter (#554):**
139
+
140
+ - Clarified that `audiencetargets get` cannot page the whole account: unlike
141
+ `retargeting get --fetch-all`, the live API hard-rejects an empty
142
+ `SelectionCriteria` (error 8000 with no criteria, 4001 with `{}`). The
143
+ required-filter guard now explains this and recommends the `campaigns get` →
144
+ batched `campaign_ids` sweep instead. No API behavior change; message only.
145
+
146
+ **Fixes — preflight SelectionCriteria array limits on get (#555, P0):**
147
+
148
+ - `keywordbids get` now rejects `--campaign-ids` >10, `--adgroup-ids` >1000,
149
+ `--keyword-ids` >10000; `dynamicads get` / `smartadtargets get` reject
150
+ `--campaign-ids` >2 — before the request, with a clear `UsageError` (exit 2)
151
+ naming the array and ceiling, instead of the opaque API `error_code=4001`.
152
+ These are runtime ceilings (the WSDL declares the arrays `unbounded`), pinned
153
+ next to each command with a doc/live-4001 citation, the same discipline as
154
+ `KEYWORDS_ADD_MAX_BATCH`. Verified live 2026-06-16. Other `get` arrays
155
+ (`AdGroupIds`/`Ids` on dynamic/smart, etc.) are intentionally **not** capped
156
+ because the live API accepts them.
157
+
158
+ **Internal — dedup v4 Live output-option stack (#550):**
159
+
160
+ - Replaced the byte-identical `--format`/`--output`/`--dry-run` trio across the
161
+ standard v4 Live and `balance` commands with a shared `v4_output_options`
162
+ decorator (the v4 analogue of `get_options`, epic #491). The CLI surface is
163
+ unchanged — same option order, names, `click.Choice(["json","table","csv",
164
+ "tsv"])` format, defaults, and help. `v4account enable-shared-account` /
165
+ `account-management` (reversed order, custom `--dry-run` help) and the
166
+ dry-run-only `v4finance transfer-money` / `pay-campaigns` /
167
+ `pay-campaigns-by-card` (no `--format`/`--output`) keep their divergent
168
+ stacks and are intentionally excluded.
169
+
170
+ **Fixes — `ads update` can now clear AdImageHash (#552):**
171
+
172
+ - Added `--clear-image-hash` to `ads update`. The flag sends
173
+ `AdImageHash: null` so an image can be removed from an existing ad — e.g.
174
+ unblocking a `TEXT_AD` whose image was restricted in moderation — without
175
+ recreating the ad. Supported for the three subtypes whose WSDL `AdImageHash`
176
+ is nillable: `TEXT_AD`, `DYNAMIC_TEXT_AD`, `MOBILE_APP_AD`. It is **rejected**
177
+ for `TEXT_IMAGE_AD` and `MOBILE_APP_IMAGE_AD`, which share the non-nillable
178
+ `ImageAdUpdateBase.AdImageHash` — the live API returns error 8000
179
+ (`AdImageHash cannot have the null value`) for those, verified directly.
180
+ `--image-hash` and `--clear-image-hash` are mutually exclusive.
181
+ Previously there was no way to reset the image: `--image-hash ""` was dropped
182
+ by a truthy check, and `--image-hash null` sent the literal string `"null"`.
183
+
184
+ **Fixes — docs-URL drift regression (re-fixes #463):**
185
+
186
+ - Restored the four WSDL `docs` URLs for `dynamicads`,
187
+ `dynamicfeedadtargets`, `smartadtargets` and `vcards` that the
188
+ `tapi-yandex-direct` 2026.5.29 vendor update silently reverted back to the
189
+ removed `…/dev/direct/doc/ru/<service>` HTML pages (which 404 since Yandex
190
+ dropped those pages in September 2025). The fix from #464 was overwritten by
191
+ the `rm -rf` + `cp -R` vendor sync; preflight
192
+ (`scripts/check_all_docs_urls.py`) caught it. URLs now point back at the live
193
+ `https://api.direct.yandex.com/v5/<service>?wsdl` endpoints — the only
194
+ authoritative source still served.
195
+ - Fixed the same URLs at the source in the `axisrow/tapi-yandex-direct` fork so
196
+ the next vendor update no longer re-introduces the dead pages.
197
+ - Added an offline regression guard
198
+ (`tests/test_audit_wire_shape.py::test_removed_doc_services_pin_wsdl_url`):
199
+ the four doc-removed services must keep WSDL `docs` URLs, failing in CI before
200
+ the network preflight ever runs.
201
+
202
+ **Bug Fixes — reject empty-string CSV-ID flags in `adgroups` (#570):**
203
+
204
+ - `adgroups add` and `adgroups update` now reject an explicitly-provided
205
+ empty/whitespace value for `--region-ids`, `--negative-keyword-shared-set-ids`
206
+ and `--feed-category-ids` (e.g. `--region-ids ""`, `--region-ids " "`,
207
+ `--region-ids ","`, or a batch row `{"region-ids":""}`) with a clear
208
+ `UsageError` instead of silently dropping the field. Previously `parse_ids("")`
209
+ returned `None` and the `if region_ids:` guards treated a provided-but-empty
210
+ value identically to an omitted option; for `RegionIds` (WSDL `minOccurs=1` on
211
+ add) that stripped a required field and sent an invalid body to the live API.
212
+ - The fix is centralized in a new `_require_nonempty_ids_option` helper that
213
+ distinguishes `None` (option omitted) from an all-blank value, so single mode
214
+ and `--from-file` / `--adgroups-json` batch mode behave identically for both
215
+ add and update. A genuinely malformed value with real tokens
216
+ (e.g. `225,,226`) still reports the precise `Invalid ID` error, unchanged.
217
+
218
+ ## 0.4.2
219
+
220
+ **BREAKING CHANGES - get requires SelectionCriteria (#498):**
221
+
222
+ - `adgroups` / `ads` / `keywords` / `strategies` / `creatives` / `dynamicads` /
223
+ `smartadtargets` / `audiencetargets` `get` now refuse an empty
224
+ `SelectionCriteria` before the API call, raising a `UsageError` that asks for
225
+ at least one filter — instead of sending `{"SelectionCriteria": {}}` (which the
226
+ API rejects with the opaque error 4001 for ad-group/ad/keyword resources).
227
+ Extends the same guard already shipped for `bids` / `keywordbids` in 0.4.1
228
+ (#483). WSDL declares `GetRequest.SelectionCriteria` as `minOccurs=1` for all
229
+ eight resources.
230
+ - `retargeting get` gains `--dry-run` and the shared read/pagination option
231
+ stack; its `SelectionCriteria` stays optional (WSDL `minOccurs=0`), so a
232
+ no-filter call is still valid and now omits the empty criteria from the
233
+ payload.
234
+ - All eight commands and `retargeting get` build their request via the shared
235
+ `build_common_params` helper, completing the dedup epic #491 (B3c).
236
+
237
+ **BREAKING CHANGES - auth precedence (#489):**
238
+
239
+ - Base `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` credentials from the
240
+ environment or current-directory `.env` now win over the active OAuth profile
241
+ selected by `direct auth use` when `--profile` is not passed. Explicit
242
+ `--token`, `--login`, and `--profile` still take priority.
243
+ - `direct auth status` reports the selected effective credentials, including
244
+ base env/`.env` and secret-manager fallbacks, instead of reporting only the
245
+ active OAuth profile.
246
+ - `direct auth login` can now ask interactive users whether to save the OAuth
247
+ access token and resolved login into the current-directory `.env`; the default
248
+ answer is no.
249
+
250
+ **Docs — live-write coverage limitation (#538):**
251
+
252
+ - Documented why the SMART_CAMPAIGN / DYNAMIC_TEXT_CAMPAIGN / `adimages`
253
+ live-write lifecycle (`dynamicads`, `smartadtargets`, `adimages`) stays
254
+ recorded only as 3500/5004 error cassettes. Verified via direct API calls that
255
+ the available sandbox **agency** account has no client accounts under it
256
+ (`agencyclients.get` → empty), cannot create one (3001 "No rights to create
257
+ clients", access by request only), and that without a client login every
258
+ agency-scoped mutation returns 8000. Closed #538 as a documented account-tier
259
+ limitation; no CLI code change. See `tests/MANUAL_COVERAGE.md`.
260
+
3
261
  ## 0.4.1
4
262
 
5
263
  Russian-default CLI localization across all command modules (epic #466).
@@ -24,9 +24,9 @@ Click group-of-groups. Each Yandex Direct API resource = one file in `direct_cli
24
24
 
25
25
  **Request flow:** `cli.py` → `auth.py` (resolves token/login) → `api.py` (`create_client`) → `tapi_yandex_direct.YandexDirect` → Yandex API → `output.py` (format/print).
26
26
 
27
- **Credentials priority (CLI):** CLI flags (`--token`, `--login`) > active profile from `direct auth login` > env vars (`YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN`) > `.env` file > 1Password/Bitwarden refs. See `direct_cli/auth.py:600` (`get_credentials`) and README table for the full chain. `load_dotenv()` runs at `cli.py` import time.
27
+ **Credentials priority (CLI):** explicit CLI flags (`--token`, `--login`, `--profile`) > base env/current working directory `.env` (`YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN`) > active profile from `direct auth login` / `direct auth use` > 1Password/Bitwarden refs. Explicit `--profile` is isolated and does not fall back to base `.env` login. See `direct_cli/auth.py` (`get_credentials`) and README table for the full chain.
28
28
 
29
- **Credentials priority (tests):** **inverted** env vars > active profile > skip. Tests must not silently hit production when a developer has an active `direct auth` profile, so env vars take precedence over the profile (see `tests/test_v4_live_contracts.py::_credentials`).
29
+ **Credentials priority (tests):** env vars/current working directory `.env` > active profile > skip. Tests must not silently hit production when a developer has an active `direct auth` profile, so env vars take precedence over the profile (see `tests/test_v4_live_contracts.py::_credentials`).
30
30
 
31
31
  **Shared utilities** (`utils.py`): `parse_ids`, `parse_json`, `build_selection_criteria`, `build_common_params`, `get_default_fields`, `COMMON_FIELDS` dict. All command modules import from here — don't duplicate.
32
32
 
@@ -106,7 +106,7 @@ Builds dist artifacts, runs twine checks, uploads to TestPyPI + PyPI. Does **not
106
106
 
107
107
  - **Unit** (`test_cli.py`, `test_comprehensive.py`) — no API calls, no token needed.
108
108
  - **Integration** (`test_integration.py`, `@pytest.mark.integration`) — require `.env` with `YANDEX_DIRECT_TOKEN` and `YANDEX_DIRECT_LOGIN`. Auto-skip if absent.
109
- - **Credential resolution in tests:** env vars first, then active `direct auth` profile, then skip. This is **inverted** vs. CLI (where the profile wins) on purpose: a developer machine with an active profile must not silently hit production on a plain `pytest`.
109
+ - **Credential resolution in tests:** env vars/current working directory `.env` first, then active `direct auth` profile, then skip. This matches the safe CLI default for base env vs. active profile: a developer machine with an active profile must not silently hit production on a plain `pytest`.
110
110
 
111
111
  ## Dangerous Commands — Never Auto-Test
112
112
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: direct-cli
3
- Version: 0.4.1
3
+ Version: 0.4.3
4
4
  Summary: Command-line interface for Yandex Direct API
5
5
  Author: axisrow
6
6
  License: MIT
@@ -106,6 +106,9 @@ Notes:
106
106
  - If the first non-interactive step includes `--client-secret`, the secret is remembered for the matching completion step.
107
107
  - If a profile already stores a confidential OAuth client, `direct auth login --code CODE --profile NAME` reuses the saved `client_id` and `client_secret`.
108
108
  - `direct auth login --oauth-token TOKEN` is a manual access-token import and does not auto-refresh.
109
+ - After a successful interactive login, Direct CLI asks whether to save the
110
+ access token and login to the current working directory `.env`; non-interactive
111
+ login flows do not prompt.
109
112
  - Alias `auth_login` is not supported.
110
113
 
111
114
  Credential resolution priority:
@@ -113,22 +116,23 @@ Credential resolution priority:
113
116
  | Priority | Source | Example |
114
117
  |----------|--------|---------|
115
118
  | 1 | Explicit CLI options | `direct --token TOKEN --login LOGIN campaigns get` |
116
- | 2 | OAuth profile storage | `direct --profile agency1 campaigns get` |
117
- | 3 | Profile-specific env vars | `YANDEX_DIRECT_TOKEN_AGENCY1`, `YANDEX_DIRECT_LOGIN_AGENCY1` |
118
- | 4 | Base env vars or current working directory `.env` | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
119
+ | 2 | Explicit profile credentials | `direct --profile agency1 campaigns get` |
120
+ | 3 | Base env vars or current working directory `.env` | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
121
+ | 4 | Active profile credentials | `direct auth use --profile agency1` |
119
122
  | 5 | 1Password references | `--op-token-ref`, `YANDEX_DIRECT_OP_TOKEN_REF` |
120
123
  | 6 | Bitwarden references | `--bw-token-ref`, `YANDEX_DIRECT_BW_TOKEN_REF` |
121
124
 
122
125
  Direct CLI automatically loads only the `.env` file from the current working
123
126
  directory, i.e. the directory where you run `direct`. It does not search for
124
- `.env` from the installed package or source-code location. If a profile is
125
- selected with `--profile` or `direct auth use --profile NAME`, Direct CLI does
126
- not fall back to base `YANDEX_DIRECT_LOGIN`; this prevents mixing a profile
127
- token with a login from the current working directory `.env`. For multi-account
128
- setups, prefer OAuth profiles or profile-specific env vars instead of base
129
- credentials.
127
+ `.env` from the installed package or source-code location. Without an explicit
128
+ `--profile`, base `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` from env or cwd
129
+ `.env` win over the active OAuth profile. With explicit `--profile`, Direct CLI
130
+ uses only that profile's OAuth/profile-env credentials and does not fall back to
131
+ base `YANDEX_DIRECT_LOGIN`; this prevents mixing accounts. `direct auth status`
132
+ without `--profile` reports the effective credential source; with `--profile` it
133
+ reports that profile.
130
134
 
131
- > **Tests use the inverted order.** Live-API test suites (e.g. `tests/test_v4_live_contracts.py`) read `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` from the environment first, only then fall back to the active `direct auth` profile, and skip the test if neither is set. This is intentional: a developer machine with an active profile must not silently hit production on a plain `pytest` invocation. See `CLAUDE.md` for the contract.
135
+ > **Tests follow the safe credential order.** Live-API test suites (e.g. `tests/test_v4_live_contracts.py`) read `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` from the environment first, only then fall back to the active `direct auth` profile, and skip the test if neither is set. This prevents a developer machine with an active profile from silently hitting production on a plain `pytest` invocation. See `CLAUDE.md` for the contract.
132
136
 
133
137
  Install with `pip install direct-cli`, then run commands with `direct`.
134
138
  Invoking the deprecated `direct-cli` entrypoint exits with
@@ -539,6 +543,7 @@ direct ads add --adgroup-id 12345 --type MOBILE_APP_IMAGE_AD --image-hash abcdef
539
543
  direct ads add --adgroup-id 12345 --type SMART_AD_BUILDER_AD --logo-extension-hash logoabcdefghijklmnop --dry-run
540
544
  direct ads update --id 99999 --type TEXT_AD --title "New Title" --text "New text" --href "https://example.com"
541
545
  direct ads update --id 99999 --type TEXT_AD --image-hash abcdefghijklmnopqrst
546
+ direct ads update --id 99999 --type TEXT_AD --clear-image-hash # remove the image (AdImageHash: null; TEXT_AD / DYNAMIC_TEXT_AD / MOBILE_APP_AD only)
542
547
  direct ads update --id 99999 --type TEXT_AD --title2 "New second headline" --vcard-id 222
543
548
  direct ads update --id 99999 --type TEXT_AD --callouts-add "111,222" --callouts-remove "333"
544
549
  direct ads update --id 99999 --type TEXT_AD --callouts-set "444,555"
@@ -557,7 +562,10 @@ direct ads delete --id 99999
557
562
  ```
558
563
 
559
564
  Available TEXT_AD typed flags for `ads add` / `ads update`: `--title`, `--text`,
560
- `--href`, `--image-hash`, `--title2`, `--display-url-path`, `--vcard-id`,
565
+ `--href`, `--image-hash`, `--clear-image-hash` (update only — sets
566
+ `AdImageHash: null`; TEXT_AD / DYNAMIC_TEXT_AD / MOBILE_APP_AD only, since
567
+ TEXT_IMAGE_AD / MOBILE_APP_IMAGE_AD have a non-nillable `AdImageHash`),
568
+ `--title2`, `--display-url-path`, `--vcard-id`,
561
569
  `--sitelink-set-id`, `--turbo-page-id`, `--final-url`,
562
570
  `--video-extension-creative-id`, `--price-extension-*`, `--business-id`,
563
571
  `--prefer-vcard-over-business`, and `--erir-ad-description`. For `ads add`,
@@ -704,6 +712,9 @@ direct bidmodifiers set --id 99 --value 130 --dry-run
704
712
 
705
713
  # Canonical multiword groups
706
714
  direct negativekeywordsharedsets update --id 123 --keywords "foo,bar"
715
+ # audiencetargets get always needs a filter — the API rejects an empty
716
+ # SelectionCriteria, so there is no whole-account paging. To sweep the account,
717
+ # run `campaigns get` first, then page audiencetargets get in batches of campaign ids.
707
718
  direct audiencetargets get --campaign-ids 123 --fields Id,AdGroupId,RetargetingListId,State,ContextBid
708
719
  direct audiencetargets add --adgroup-id 100 --retargeting-list-id 200 --bid 12000000 --priority HIGH --dry-run
709
720
  direct audiencetargets set-bids --id 101 --context-bid 7000000 --priority LOW --dry-run
@@ -1065,27 +1076,32 @@ direct --profile agency1 campaigns get
1065
1076
  - Если первый non-interactive шаг включает `--client-secret`, secret запоминается для последующего completion step.
1066
1077
  - Если profile уже хранит confidential OAuth client, `direct auth login --code CODE --profile NAME` использует сохраненные `client_id` и `client_secret`.
1067
1078
  - `direct auth login --oauth-token TOKEN` импортирует access token вручную и не включает auto-refresh.
1079
+ - После успешного интерактивного входа Direct CLI спрашивает, сохранить ли
1080
+ access token и login в `.env` текущей рабочей папки; non-interactive вход этот
1081
+ вопрос не задаёт.
1068
1082
 
1069
1083
  Порядок выбора credentials:
1070
1084
 
1071
1085
  | Приоритет | Источник | Пример |
1072
1086
  |-----------|----------|--------|
1073
1087
  | 1 | Явные CLI-опции | `direct --token TOKEN --login LOGIN campaigns get` |
1074
- | 2 | OAuth profile storage | `direct --profile agency1 campaigns get` |
1075
- | 3 | Профильные env vars | `YANDEX_DIRECT_TOKEN_AGENCY1`, `YANDEX_DIRECT_LOGIN_AGENCY1` |
1076
- | 4 | Базовые env vars или `.env` из папки запуска | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
1088
+ | 2 | Явно выбранный profile | `direct --profile agency1 campaigns get` |
1089
+ | 3 | Базовые env vars или `.env` из папки запуска | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
1090
+ | 4 | Активный profile | `direct auth use --profile agency1` |
1077
1091
  | 5 | 1Password references | `--op-token-ref`, `YANDEX_DIRECT_OP_TOKEN_REF` |
1078
1092
  | 6 | Bitwarden references | `--bw-token-ref`, `YANDEX_DIRECT_BW_TOKEN_REF` |
1079
1093
 
1080
1094
  Direct CLI автоматически читает только `.env` из текущей рабочей папки, то есть
1081
1095
  из папки, где запущена команда `direct`. Он не ищет `.env` от папки
1082
- установленного пакета или исходного кода. Если профиль выбран через `--profile`
1083
- или `direct auth use --profile NAME`, Direct CLI не подставляет base
1084
- `YANDEX_DIRECT_LOGIN`; это защищает от смешивания токена из профиля с логином из
1085
- `.env` текущей рабочей папки. Для нескольких аккаунтов используйте OAuth
1086
- profiles или профильные env vars, а не базовые credentials.
1096
+ установленного пакета или исходного кода. Без явного `--profile` базовые
1097
+ `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` из окружения или cwd `.env`
1098
+ побеждают активный OAuth profile. С явным `--profile` Direct CLI использует
1099
+ только OAuth/profile-env credentials этого профиля и не подставляет base
1100
+ `YANDEX_DIRECT_LOGIN`; это защищает от смешивания аккаунтов. `direct auth status`
1101
+ без `--profile` показывает реально выбранный источник credentials, а с
1102
+ `--profile` показывает этот профиль.
1087
1103
 
1088
- > **В тестах порядок инвертирован.** Live-API тесты (например `tests/test_v4_live_contracts.py`) сначала читают `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` из окружения, затем падают на активный профиль `direct auth`, и скипают тест если ни того ни другого нет. Это сделано специально: на машине разработчика с активным профилем обычный `pytest` не должен молча идти в боевой API. Контракт зафиксирован в `CLAUDE.md`.
1104
+ > **Тесты используют безопасный порядок credentials.** Live-API тесты (например `tests/test_v4_live_contracts.py`) сначала читают `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` из окружения, затем падают на активный профиль `direct auth`, и скипают тест если ни того ни другого нет. Это защищает от случайного обращения к production API на машине разработчика с активным profile. Контракт зафиксирован в `CLAUDE.md`.
1089
1105
 
1090
1106
  Установка остаётся через `pip install direct-cli`, а запуск команд теперь идет
1091
1107
  через `direct`. Вызов deprecated entrypoint `direct-cli` завершается ошибкой с
@@ -1362,6 +1378,7 @@ direct ads add --adgroup-id 12345 --type MOBILE_APP_IMAGE_AD --image-hash abcdef
1362
1378
  direct ads add --adgroup-id 12345 --type SMART_AD_BUILDER_AD --logo-extension-hash logoabcdefghijklmnop --dry-run
1363
1379
  direct ads update --id 99999 --type TEXT_AD --title "Новый заголовок" --text "Новый текст" --href "https://example.com"
1364
1380
  direct ads update --id 99999 --type TEXT_AD --image-hash abcdefghijklmnopqrst
1381
+ direct ads update --id 99999 --type TEXT_AD --clear-image-hash # удалить изображение (AdImageHash: null; только TEXT_AD / DYNAMIC_TEXT_AD / MOBILE_APP_AD)
1365
1382
  direct ads update --id 99999 --type TEXT_AD --title2 "Новый второй заголовок" --vcard-id 222
1366
1383
  direct ads update --id 99999 --type TEXT_AD --callouts-add "111,222" --callouts-remove "333"
1367
1384
  direct ads update --id 99999 --type TEXT_AD --callouts-set "444,555"
@@ -1380,7 +1397,10 @@ direct ads delete --id 99999
1380
1397
  ```
1381
1398
 
1382
1399
  Доступные типизированные флаги TEXT_AD для `ads add` / `ads update`:
1383
- `--title`, `--text`, `--href`, `--image-hash`, `--title2`, `--display-url-path`,
1400
+ `--title`, `--text`, `--href`, `--image-hash`, `--clear-image-hash`
1401
+ (только update — устанавливает `AdImageHash: null`; только TEXT_AD /
1402
+ DYNAMIC_TEXT_AD / MOBILE_APP_AD, так как у TEXT_IMAGE_AD / MOBILE_APP_IMAGE_AD
1403
+ поле `AdImageHash` не nillable), `--title2`, `--display-url-path`,
1384
1404
  `--vcard-id`, `--sitelink-set-id`, `--turbo-page-id`, `--final-url`,
1385
1405
  `--video-extension-creative-id`, `--price-extension-*`, `--business-id`,
1386
1406
  `--prefer-vcard-over-business` и `--erir-ad-description`. Для `ads add`
@@ -1529,6 +1549,9 @@ direct bidmodifiers set --id 99 --value 130 --dry-run
1529
1549
 
1530
1550
  # Канонические многословные группы
1531
1551
  direct negativekeywordsharedsets update --id 123 --keywords "foo,bar"
1552
+ # audiencetargets get всегда требует фильтр — API отклоняет пустой
1553
+ # SelectionCriteria, поэтому обхода всего аккаунта нет. Чтобы собрать аккаунт,
1554
+ # сначала выполните `campaigns get`, затем запрашивайте audiencetargets get батчами campaign id.
1532
1555
  direct audiencetargets get --campaign-ids 123 --fields Id,AdGroupId,RetargetingListId,State,ContextBid
1533
1556
  direct audiencetargets add --adgroup-id 100 --retargeting-list-id 200 --bid 12000000 --priority HIGH --dry-run
1534
1557
  direct audiencetargets set-bids --id 101 --context-bid 7000000 --priority LOW --dry-run
@@ -63,6 +63,9 @@ Notes:
63
63
  - If the first non-interactive step includes `--client-secret`, the secret is remembered for the matching completion step.
64
64
  - If a profile already stores a confidential OAuth client, `direct auth login --code CODE --profile NAME` reuses the saved `client_id` and `client_secret`.
65
65
  - `direct auth login --oauth-token TOKEN` is a manual access-token import and does not auto-refresh.
66
+ - After a successful interactive login, Direct CLI asks whether to save the
67
+ access token and login to the current working directory `.env`; non-interactive
68
+ login flows do not prompt.
66
69
  - Alias `auth_login` is not supported.
67
70
 
68
71
  Credential resolution priority:
@@ -70,22 +73,23 @@ Credential resolution priority:
70
73
  | Priority | Source | Example |
71
74
  |----------|--------|---------|
72
75
  | 1 | Explicit CLI options | `direct --token TOKEN --login LOGIN campaigns get` |
73
- | 2 | OAuth profile storage | `direct --profile agency1 campaigns get` |
74
- | 3 | Profile-specific env vars | `YANDEX_DIRECT_TOKEN_AGENCY1`, `YANDEX_DIRECT_LOGIN_AGENCY1` |
75
- | 4 | Base env vars or current working directory `.env` | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
76
+ | 2 | Explicit profile credentials | `direct --profile agency1 campaigns get` |
77
+ | 3 | Base env vars or current working directory `.env` | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
78
+ | 4 | Active profile credentials | `direct auth use --profile agency1` |
76
79
  | 5 | 1Password references | `--op-token-ref`, `YANDEX_DIRECT_OP_TOKEN_REF` |
77
80
  | 6 | Bitwarden references | `--bw-token-ref`, `YANDEX_DIRECT_BW_TOKEN_REF` |
78
81
 
79
82
  Direct CLI automatically loads only the `.env` file from the current working
80
83
  directory, i.e. the directory where you run `direct`. It does not search for
81
- `.env` from the installed package or source-code location. If a profile is
82
- selected with `--profile` or `direct auth use --profile NAME`, Direct CLI does
83
- not fall back to base `YANDEX_DIRECT_LOGIN`; this prevents mixing a profile
84
- token with a login from the current working directory `.env`. For multi-account
85
- setups, prefer OAuth profiles or profile-specific env vars instead of base
86
- credentials.
84
+ `.env` from the installed package or source-code location. Without an explicit
85
+ `--profile`, base `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` from env or cwd
86
+ `.env` win over the active OAuth profile. With explicit `--profile`, Direct CLI
87
+ uses only that profile's OAuth/profile-env credentials and does not fall back to
88
+ base `YANDEX_DIRECT_LOGIN`; this prevents mixing accounts. `direct auth status`
89
+ without `--profile` reports the effective credential source; with `--profile` it
90
+ reports that profile.
87
91
 
88
- > **Tests use the inverted order.** Live-API test suites (e.g. `tests/test_v4_live_contracts.py`) read `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` from the environment first, only then fall back to the active `direct auth` profile, and skip the test if neither is set. This is intentional: a developer machine with an active profile must not silently hit production on a plain `pytest` invocation. See `CLAUDE.md` for the contract.
92
+ > **Tests follow the safe credential order.** Live-API test suites (e.g. `tests/test_v4_live_contracts.py`) read `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` from the environment first, only then fall back to the active `direct auth` profile, and skip the test if neither is set. This prevents a developer machine with an active profile from silently hitting production on a plain `pytest` invocation. See `CLAUDE.md` for the contract.
89
93
 
90
94
  Install with `pip install direct-cli`, then run commands with `direct`.
91
95
  Invoking the deprecated `direct-cli` entrypoint exits with
@@ -496,6 +500,7 @@ direct ads add --adgroup-id 12345 --type MOBILE_APP_IMAGE_AD --image-hash abcdef
496
500
  direct ads add --adgroup-id 12345 --type SMART_AD_BUILDER_AD --logo-extension-hash logoabcdefghijklmnop --dry-run
497
501
  direct ads update --id 99999 --type TEXT_AD --title "New Title" --text "New text" --href "https://example.com"
498
502
  direct ads update --id 99999 --type TEXT_AD --image-hash abcdefghijklmnopqrst
503
+ direct ads update --id 99999 --type TEXT_AD --clear-image-hash # remove the image (AdImageHash: null; TEXT_AD / DYNAMIC_TEXT_AD / MOBILE_APP_AD only)
499
504
  direct ads update --id 99999 --type TEXT_AD --title2 "New second headline" --vcard-id 222
500
505
  direct ads update --id 99999 --type TEXT_AD --callouts-add "111,222" --callouts-remove "333"
501
506
  direct ads update --id 99999 --type TEXT_AD --callouts-set "444,555"
@@ -514,7 +519,10 @@ direct ads delete --id 99999
514
519
  ```
515
520
 
516
521
  Available TEXT_AD typed flags for `ads add` / `ads update`: `--title`, `--text`,
517
- `--href`, `--image-hash`, `--title2`, `--display-url-path`, `--vcard-id`,
522
+ `--href`, `--image-hash`, `--clear-image-hash` (update only — sets
523
+ `AdImageHash: null`; TEXT_AD / DYNAMIC_TEXT_AD / MOBILE_APP_AD only, since
524
+ TEXT_IMAGE_AD / MOBILE_APP_IMAGE_AD have a non-nillable `AdImageHash`),
525
+ `--title2`, `--display-url-path`, `--vcard-id`,
518
526
  `--sitelink-set-id`, `--turbo-page-id`, `--final-url`,
519
527
  `--video-extension-creative-id`, `--price-extension-*`, `--business-id`,
520
528
  `--prefer-vcard-over-business`, and `--erir-ad-description`. For `ads add`,
@@ -661,6 +669,9 @@ direct bidmodifiers set --id 99 --value 130 --dry-run
661
669
 
662
670
  # Canonical multiword groups
663
671
  direct negativekeywordsharedsets update --id 123 --keywords "foo,bar"
672
+ # audiencetargets get always needs a filter — the API rejects an empty
673
+ # SelectionCriteria, so there is no whole-account paging. To sweep the account,
674
+ # run `campaigns get` first, then page audiencetargets get in batches of campaign ids.
664
675
  direct audiencetargets get --campaign-ids 123 --fields Id,AdGroupId,RetargetingListId,State,ContextBid
665
676
  direct audiencetargets add --adgroup-id 100 --retargeting-list-id 200 --bid 12000000 --priority HIGH --dry-run
666
677
  direct audiencetargets set-bids --id 101 --context-bid 7000000 --priority LOW --dry-run
@@ -1022,27 +1033,32 @@ direct --profile agency1 campaigns get
1022
1033
  - Если первый non-interactive шаг включает `--client-secret`, secret запоминается для последующего completion step.
1023
1034
  - Если profile уже хранит confidential OAuth client, `direct auth login --code CODE --profile NAME` использует сохраненные `client_id` и `client_secret`.
1024
1035
  - `direct auth login --oauth-token TOKEN` импортирует access token вручную и не включает auto-refresh.
1036
+ - После успешного интерактивного входа Direct CLI спрашивает, сохранить ли
1037
+ access token и login в `.env` текущей рабочей папки; non-interactive вход этот
1038
+ вопрос не задаёт.
1025
1039
 
1026
1040
  Порядок выбора credentials:
1027
1041
 
1028
1042
  | Приоритет | Источник | Пример |
1029
1043
  |-----------|----------|--------|
1030
1044
  | 1 | Явные CLI-опции | `direct --token TOKEN --login LOGIN campaigns get` |
1031
- | 2 | OAuth profile storage | `direct --profile agency1 campaigns get` |
1032
- | 3 | Профильные env vars | `YANDEX_DIRECT_TOKEN_AGENCY1`, `YANDEX_DIRECT_LOGIN_AGENCY1` |
1033
- | 4 | Базовые env vars или `.env` из папки запуска | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
1045
+ | 2 | Явно выбранный profile | `direct --profile agency1 campaigns get` |
1046
+ | 3 | Базовые env vars или `.env` из папки запуска | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
1047
+ | 4 | Активный profile | `direct auth use --profile agency1` |
1034
1048
  | 5 | 1Password references | `--op-token-ref`, `YANDEX_DIRECT_OP_TOKEN_REF` |
1035
1049
  | 6 | Bitwarden references | `--bw-token-ref`, `YANDEX_DIRECT_BW_TOKEN_REF` |
1036
1050
 
1037
1051
  Direct CLI автоматически читает только `.env` из текущей рабочей папки, то есть
1038
1052
  из папки, где запущена команда `direct`. Он не ищет `.env` от папки
1039
- установленного пакета или исходного кода. Если профиль выбран через `--profile`
1040
- или `direct auth use --profile NAME`, Direct CLI не подставляет base
1041
- `YANDEX_DIRECT_LOGIN`; это защищает от смешивания токена из профиля с логином из
1042
- `.env` текущей рабочей папки. Для нескольких аккаунтов используйте OAuth
1043
- profiles или профильные env vars, а не базовые credentials.
1053
+ установленного пакета или исходного кода. Без явного `--profile` базовые
1054
+ `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` из окружения или cwd `.env`
1055
+ побеждают активный OAuth profile. С явным `--profile` Direct CLI использует
1056
+ только OAuth/profile-env credentials этого профиля и не подставляет base
1057
+ `YANDEX_DIRECT_LOGIN`; это защищает от смешивания аккаунтов. `direct auth status`
1058
+ без `--profile` показывает реально выбранный источник credentials, а с
1059
+ `--profile` показывает этот профиль.
1044
1060
 
1045
- > **В тестах порядок инвертирован.** Live-API тесты (например `tests/test_v4_live_contracts.py`) сначала читают `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` из окружения, затем падают на активный профиль `direct auth`, и скипают тест если ни того ни другого нет. Это сделано специально: на машине разработчика с активным профилем обычный `pytest` не должен молча идти в боевой API. Контракт зафиксирован в `CLAUDE.md`.
1061
+ > **Тесты используют безопасный порядок credentials.** Live-API тесты (например `tests/test_v4_live_contracts.py`) сначала читают `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` из окружения, затем падают на активный профиль `direct auth`, и скипают тест если ни того ни другого нет. Это защищает от случайного обращения к production API на машине разработчика с активным profile. Контракт зафиксирован в `CLAUDE.md`.
1046
1062
 
1047
1063
  Установка остаётся через `pip install direct-cli`, а запуск команд теперь идет
1048
1064
  через `direct`. Вызов deprecated entrypoint `direct-cli` завершается ошибкой с
@@ -1319,6 +1335,7 @@ direct ads add --adgroup-id 12345 --type MOBILE_APP_IMAGE_AD --image-hash abcdef
1319
1335
  direct ads add --adgroup-id 12345 --type SMART_AD_BUILDER_AD --logo-extension-hash logoabcdefghijklmnop --dry-run
1320
1336
  direct ads update --id 99999 --type TEXT_AD --title "Новый заголовок" --text "Новый текст" --href "https://example.com"
1321
1337
  direct ads update --id 99999 --type TEXT_AD --image-hash abcdefghijklmnopqrst
1338
+ direct ads update --id 99999 --type TEXT_AD --clear-image-hash # удалить изображение (AdImageHash: null; только TEXT_AD / DYNAMIC_TEXT_AD / MOBILE_APP_AD)
1322
1339
  direct ads update --id 99999 --type TEXT_AD --title2 "Новый второй заголовок" --vcard-id 222
1323
1340
  direct ads update --id 99999 --type TEXT_AD --callouts-add "111,222" --callouts-remove "333"
1324
1341
  direct ads update --id 99999 --type TEXT_AD --callouts-set "444,555"
@@ -1337,7 +1354,10 @@ direct ads delete --id 99999
1337
1354
  ```
1338
1355
 
1339
1356
  Доступные типизированные флаги TEXT_AD для `ads add` / `ads update`:
1340
- `--title`, `--text`, `--href`, `--image-hash`, `--title2`, `--display-url-path`,
1357
+ `--title`, `--text`, `--href`, `--image-hash`, `--clear-image-hash`
1358
+ (только update — устанавливает `AdImageHash: null`; только TEXT_AD /
1359
+ DYNAMIC_TEXT_AD / MOBILE_APP_AD, так как у TEXT_IMAGE_AD / MOBILE_APP_IMAGE_AD
1360
+ поле `AdImageHash` не nillable), `--title2`, `--display-url-path`,
1341
1361
  `--vcard-id`, `--sitelink-set-id`, `--turbo-page-id`, `--final-url`,
1342
1362
  `--video-extension-creative-id`, `--price-extension-*`, `--business-id`,
1343
1363
  `--prefer-vcard-over-business` и `--erir-ad-description`. Для `ads add`
@@ -1486,6 +1506,9 @@ direct bidmodifiers set --id 99 --value 130 --dry-run
1486
1506
 
1487
1507
  # Канонические многословные группы
1488
1508
  direct negativekeywordsharedsets update --id 123 --keywords "foo,bar"
1509
+ # audiencetargets get всегда требует фильтр — API отклоняет пустой
1510
+ # SelectionCriteria, поэтому обхода всего аккаунта нет. Чтобы собрать аккаунт,
1511
+ # сначала выполните `campaigns get`, затем запрашивайте audiencetargets get батчами campaign id.
1489
1512
  direct audiencetargets get --campaign-ids 123 --fields Id,AdGroupId,RetargetingListId,State,ContextBid
1490
1513
  direct audiencetargets add --adgroup-id 100 --retargeting-list-id 200 --bid 12000000 --priority HIGH --dry-run
1491
1514
  direct audiencetargets set-bids --id 101 --context-bid 7000000 --priority LOW --dry-run