direct-cli 0.4.0__tar.gz → 0.4.2__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 (369) hide show
  1. {direct_cli-0.4.0 → direct_cli-0.4.2}/CHANGELOG.md +197 -0
  2. {direct_cli-0.4.0 → direct_cli-0.4.2}/CLAUDE.md +3 -3
  3. {direct_cli-0.4.0 → direct_cli-0.4.2}/PKG-INFO +79 -24
  4. {direct_cli-0.4.0 → direct_cli-0.4.2}/README.md +78 -23
  5. direct_cli-0.4.2/direct_cli/_autotargeting.py +206 -0
  6. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/_bidding_strategy.py +876 -882
  7. direct_cli-0.4.2/direct_cli/_flag_validation.py +53 -0
  8. direct_cli-0.4.2/direct_cli/_smoke_probes.py +173 -0
  9. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/_vendor/tapi_yandex_direct/__init__.py +1 -1
  10. direct_cli-0.4.2/direct_cli/_vendor/tapi_yandex_direct/endpoints.py +27 -0
  11. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.py +37 -15
  12. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.pyi +9 -3
  13. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.py +10 -6
  14. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/_vendor/tapi_yandex_direct/v4/adapter.pyi +1 -0
  15. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/api.py +23 -0
  16. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/auth.py +235 -33
  17. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/cli.py +135 -24
  18. direct_cli-0.4.2/direct_cli/commands/_lifecycle.py +92 -0
  19. direct_cli-0.4.2/direct_cli/commands/adextensions.py +124 -0
  20. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/commands/adgroups.py +309 -446
  21. direct_cli-0.4.2/direct_cli/commands/adimages.py +122 -0
  22. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/commands/ads.py +574 -726
  23. direct_cli-0.4.2/direct_cli/commands/advideos.py +96 -0
  24. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/commands/agencyclients.py +149 -203
  25. direct_cli-0.4.2/direct_cli/commands/audiencetargets.py +198 -0
  26. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/commands/auth.py +153 -18
  27. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/commands/balance.py +12 -14
  28. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/commands/bidmodifiers.py +163 -234
  29. direct_cli-0.4.2/direct_cli/commands/bids.py +226 -0
  30. direct_cli-0.4.2/direct_cli/commands/businesses.py +57 -0
  31. direct_cli-0.4.2/direct_cli/commands/campaigns.py +7205 -0
  32. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/commands/changes.py +45 -58
  33. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/commands/clients.py +94 -124
  34. direct_cli-0.4.2/direct_cli/commands/creatives.py +150 -0
  35. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/commands/dictionaries.py +26 -42
  36. direct_cli-0.4.2/direct_cli/commands/dynamicads.py +189 -0
  37. direct_cli-0.4.2/direct_cli/commands/dynamicfeedadtargets.py +190 -0
  38. direct_cli-0.4.2/direct_cli/commands/feeds.py +402 -0
  39. direct_cli-0.4.2/direct_cli/commands/keywordbids.py +299 -0
  40. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/commands/keywords.py +211 -407
  41. direct_cli-0.4.2/direct_cli/commands/keywordsresearch.py +68 -0
  42. direct_cli-0.4.2/direct_cli/commands/leads.py +80 -0
  43. direct_cli-0.4.2/direct_cli/commands/negativekeywordsharedsets.py +128 -0
  44. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/commands/reports.py +68 -67
  45. direct_cli-0.4.2/direct_cli/commands/retargeting.py +210 -0
  46. direct_cli-0.4.2/direct_cli/commands/sitelinks.py +289 -0
  47. direct_cli-0.4.2/direct_cli/commands/smartadtargets.py +281 -0
  48. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/commands/strategies.py +223 -294
  49. direct_cli-0.4.2/direct_cli/commands/turbopages.py +67 -0
  50. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/commands/v4account.py +54 -105
  51. direct_cli-0.4.2/direct_cli/commands/v4adimage.py +187 -0
  52. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/commands/v4events.py +18 -27
  53. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/commands/v4finance.py +193 -134
  54. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/commands/v4forecast.py +10 -49
  55. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/commands/v4goals.py +7 -49
  56. direct_cli-0.4.2/direct_cli/commands/v4keywords.py +64 -0
  57. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/commands/v4tags.py +34 -59
  58. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/commands/v4wordstat.py +12 -49
  59. direct_cli-0.4.2/direct_cli/commands/vcards.py +245 -0
  60. direct_cli-0.4.2/direct_cli/i18n.py +158 -0
  61. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/output.py +26 -0
  62. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/smoke_matrix.py +4 -0
  63. direct_cli-0.4.2/direct_cli/translations/adextensions.json +11 -0
  64. direct_cli-0.4.2/direct_cli/translations/adgroups.json +74 -0
  65. direct_cli-0.4.2/direct_cli/translations/adimages.json +13 -0
  66. direct_cli-0.4.2/direct_cli/translations/ads.json +134 -0
  67. direct_cli-0.4.2/direct_cli/translations/advideos.json +11 -0
  68. direct_cli-0.4.2/direct_cli/translations/agencyclients.json +32 -0
  69. direct_cli-0.4.2/direct_cli/translations/audiencetargets.json +17 -0
  70. direct_cli-0.4.2/direct_cli/translations/auth.json +25 -0
  71. direct_cli-0.4.2/direct_cli/translations/balance.json +4 -0
  72. direct_cli-0.4.2/direct_cli/translations/bidmodifiers.json +46 -0
  73. direct_cli-0.4.2/direct_cli/translations/bids.json +31 -0
  74. direct_cli-0.4.2/direct_cli/translations/businesses.json +5 -0
  75. direct_cli-0.4.2/direct_cli/translations/campaigns.json +313 -0
  76. direct_cli-0.4.2/direct_cli/translations/changes.json +18 -0
  77. direct_cli-0.4.2/direct_cli/translations/clients.json +41 -0
  78. direct_cli-0.4.2/direct_cli/translations/common.json +20 -0
  79. direct_cli-0.4.2/direct_cli/translations/creatives.json +12 -0
  80. direct_cli-0.4.2/direct_cli/translations/dictionaries.json +10 -0
  81. direct_cli-0.4.2/direct_cli/translations/dynamicads.json +10 -0
  82. direct_cli-0.4.2/direct_cli/translations/dynamicfeedadtargets.json +12 -0
  83. direct_cli-0.4.2/direct_cli/translations/feeds.json +37 -0
  84. direct_cli-0.4.2/direct_cli/translations/keywordbids.json +20 -0
  85. direct_cli-0.4.2/direct_cli/translations/keywords.json +62 -0
  86. direct_cli-0.4.2/direct_cli/translations/keywordsresearch.json +7 -0
  87. direct_cli-0.4.2/direct_cli/translations/leads.json +7 -0
  88. direct_cli-0.4.2/direct_cli/translations/negativekeywordsharedsets.json +11 -0
  89. direct_cli-0.4.2/direct_cli/translations/reports.json +23 -0
  90. direct_cli-0.4.2/direct_cli/translations/retargeting.json +16 -0
  91. direct_cli-0.4.2/direct_cli/translations/sitelinks.json +26 -0
  92. direct_cli-0.4.2/direct_cli/translations/smartadtargets.json +19 -0
  93. direct_cli-0.4.2/direct_cli/translations/strategies.json +60 -0
  94. direct_cli-0.4.2/direct_cli/translations/turbopages.json +6 -0
  95. direct_cli-0.4.2/direct_cli/translations/v4account.json +51 -0
  96. direct_cli-0.4.2/direct_cli/translations/v4adimage.json +15 -0
  97. direct_cli-0.4.2/direct_cli/translations/v4events.json +19 -0
  98. direct_cli-0.4.2/direct_cli/translations/v4finance.json +37 -0
  99. direct_cli-0.4.2/direct_cli/translations/v4forecast.json +14 -0
  100. direct_cli-0.4.2/direct_cli/translations/v4goals.json +5 -0
  101. direct_cli-0.4.2/direct_cli/translations/v4keywords.json +6 -0
  102. direct_cli-0.4.2/direct_cli/translations/v4shells.json +3 -0
  103. direct_cli-0.4.2/direct_cli/translations/v4tags.json +30 -0
  104. direct_cli-0.4.2/direct_cli/translations/v4wordstat.json +10 -0
  105. direct_cli-0.4.2/direct_cli/translations/vcards.json +34 -0
  106. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/utils.py +97 -7
  107. direct_cli-0.4.2/direct_cli/v4/emit.py +79 -0
  108. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/v4/money.py +8 -27
  109. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/v4_contracts.py +1 -2
  110. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli.egg-info/PKG-INFO +79 -24
  111. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli.egg-info/SOURCES.txt +63 -1
  112. direct_cli-0.4.0/docs/audits/PROJECT_WIRE_SHAPE_AUDIT_2026-05-29.md → direct_cli-0.4.2/docs/audits/PROJECT_WIRE_SHAPE_AUDIT_2026-05-30.md +11 -64
  113. direct_cli-0.4.2/docs/audits/WIRE_SHAPE_TRIAGE_2026-05-30.md +85 -0
  114. {direct_cli-0.4.0 → direct_cli-0.4.2}/docs/audits/wire_shape.json +17 -441
  115. {direct_cli-0.4.0 → direct_cli-0.4.2}/pyproject.toml +6 -1
  116. {direct_cli-0.4.0 → direct_cli-0.4.2}/scripts/audit_wire_shape.py +49 -10
  117. {direct_cli-0.4.0 → direct_cli-0.4.2}/scripts/build_api_coverage_report.py +16 -0
  118. {direct_cli-0.4.0 → direct_cli-0.4.2}/scripts/check_all_docs_urls.py +15 -0
  119. {direct_cli-0.4.0 → direct_cli-0.4.2}/scripts/patch_vendor_imports.py +109 -2
  120. direct_cli-0.4.2/scripts/probe_drift_urls.sh +59 -0
  121. direct_cli-0.4.2/tests/MANUAL_COVERAGE.md +147 -0
  122. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/api_coverage_payloads.py +5 -0
  123. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[creatives_get].yaml +2 -2
  124. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[retargeting_get].yaml +2 -2
  125. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[strategies_get].yaml +2 -2
  126. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_ads_add_update_delete.yaml +64 -64
  127. direct_cli-0.4.2/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_add_delete.yaml +560 -0
  128. direct_cli-0.4.2/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_suspend_resume.yaml +622 -0
  129. direct_cli-0.4.2/tests/cassettes/test_v5_live_write/test_v5_live_draft_feeds_add_update_delete.yaml +250 -0
  130. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywords_add_update_delete.yaml +122 -60
  131. direct_cli-0.4.0/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_suspend_resume.yaml → direct_cli-0.4.2/tests/cassettes/test_v5_live_write/test_v5_live_draft_retargeting_add_update_delete.yaml +40 -103
  132. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_smartadtargets_add_update_delete.yaml +24 -24
  133. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_smartadtargets_suspend_resume.yaml +24 -24
  134. direct_cli-0.4.0/tests/cassettes/test_v5_live_write/test_v5_live_draft_audiencetargets_add_delete.yaml → direct_cli-0.4.2/tests/cassettes/test_v5_live_write/test_v5_live_draft_strategies_add_update_archive_unarchive.yaml +49 -50
  135. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/conftest.py +122 -8
  136. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_api_coverage.py +13 -0
  137. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_audit_wire_shape.py +40 -0
  138. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_auth_bw.py +30 -0
  139. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_auth_oauth.py +425 -2
  140. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_auth_op.py +30 -0
  141. direct_cli-0.4.2/tests/test_autotargeting.py +228 -0
  142. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_balance.py +20 -0
  143. direct_cli-0.4.2/tests/test_bidding_strategy_constants.py +197 -0
  144. direct_cli-0.4.2/tests/test_cassette_integrity.py +87 -0
  145. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_cli.py +94 -9
  146. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_comprehensive.py +2 -0
  147. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_dry_run.py +216 -20
  148. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_env_loading.py +54 -0
  149. direct_cli-0.4.2/tests/test_field_names_option.py +85 -0
  150. direct_cli-0.4.2/tests/test_flag_validation.py +130 -0
  151. direct_cli-0.4.2/tests/test_handle_api_errors.py +101 -0
  152. direct_cli-0.4.2/tests/test_i18n.py +388 -0
  153. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_integration.py +12 -14
  154. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_low_coverage_payloads.py +71 -0
  155. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_read_cassettes.py +6 -2
  156. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_reports_parsing.py +55 -0
  157. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_transport_contract.py +20 -10
  158. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_v4_exit_codes.py +9 -9
  159. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_v4account.py +26 -26
  160. direct_cli-0.4.2/tests/test_v4adimage.py +176 -0
  161. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_v4events.py +4 -4
  162. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_v4finance_money.py +121 -16
  163. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_v4finance_read.py +9 -9
  164. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_v4forecast.py +6 -6
  165. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_v4goals.py +6 -6
  166. direct_cli-0.4.2/tests/test_v4keywords.py +112 -0
  167. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_v4tags.py +4 -4
  168. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_v4wordstat.py +7 -7
  169. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_v5_live_write.py +218 -7
  170. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_vendor_imports.py +119 -0
  171. direct_cli-0.4.0/direct_cli/_smoke_probes.py +0 -94
  172. direct_cli-0.4.0/direct_cli/_vendor/tapi_yandex_direct/endpoints.py +0 -14
  173. direct_cli-0.4.0/direct_cli/commands/adextensions.py +0 -163
  174. direct_cli-0.4.0/direct_cli/commands/adimages.py +0 -157
  175. direct_cli-0.4.0/direct_cli/commands/advideos.py +0 -116
  176. direct_cli-0.4.0/direct_cli/commands/audiencetargets.py +0 -305
  177. direct_cli-0.4.0/direct_cli/commands/bids.py +0 -247
  178. direct_cli-0.4.0/direct_cli/commands/businesses.py +0 -67
  179. direct_cli-0.4.0/direct_cli/commands/campaigns.py +0 -7547
  180. direct_cli-0.4.0/direct_cli/commands/creatives.py +0 -170
  181. direct_cli-0.4.0/direct_cli/commands/dynamicads.py +0 -292
  182. direct_cli-0.4.0/direct_cli/commands/dynamicfeedadtargets.py +0 -296
  183. direct_cli-0.4.0/direct_cli/commands/feeds.py +0 -445
  184. direct_cli-0.4.0/direct_cli/commands/keywordbids.py +0 -320
  185. direct_cli-0.4.0/direct_cli/commands/keywordsresearch.py +0 -88
  186. direct_cli-0.4.0/direct_cli/commands/leads.py +0 -84
  187. direct_cli-0.4.0/direct_cli/commands/negativekeywordsharedsets.py +0 -172
  188. direct_cli-0.4.0/direct_cli/commands/retargeting.py +0 -236
  189. direct_cli-0.4.0/direct_cli/commands/sitelinks.py +0 -302
  190. direct_cli-0.4.0/direct_cli/commands/smartadtargets.py +0 -394
  191. direct_cli-0.4.0/direct_cli/commands/turbopages.py +0 -79
  192. direct_cli-0.4.0/direct_cli/commands/vcards.py +0 -282
  193. direct_cli-0.4.0/direct_cli/i18n.py +0 -107
  194. direct_cli-0.4.0/tests/MANUAL_COVERAGE.md +0 -92
  195. direct_cli-0.4.0/tests/test_i18n.py +0 -118
  196. {direct_cli-0.4.0 → direct_cli-0.4.2}/.env.example +0 -0
  197. {direct_cli-0.4.0 → direct_cli-0.4.2}/.github/copilot-instructions.md +0 -0
  198. {direct_cli-0.4.0 → direct_cli-0.4.2}/.github/workflows/api-coverage.yml +0 -0
  199. {direct_cli-0.4.0 → direct_cli-0.4.2}/.github/workflows/claude.yml +0 -0
  200. {direct_cli-0.4.0 → direct_cli-0.4.2}/.github/workflows/quality.yml +0 -0
  201. {direct_cli-0.4.0 → direct_cli-0.4.2}/.gitignore +0 -0
  202. {direct_cli-0.4.0 → direct_cli-0.4.2}/AGENTS.md +0 -0
  203. {direct_cli-0.4.0 → direct_cli-0.4.2}/MANIFEST.in +0 -0
  204. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/__init__.py +0 -0
  205. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/_deprecated.py +0 -0
  206. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/_vendor/__init__.py +0 -0
  207. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/_vendor/tapi_yandex_direct/exceptions.py +0 -0
  208. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/_vendor/tapi_yandex_direct/resource_mapping.py +0 -0
  209. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/_vendor/tapi_yandex_direct/v4/__init__.py +0 -0
  210. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/_vendor/tapi_yandex_direct/v4/resource_mapping.py +0 -0
  211. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/commands/__init__.py +0 -0
  212. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/commands/v4shells.py +0 -0
  213. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/reports_coverage.py +0 -0
  214. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/v4/__init__.py +0 -0
  215. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli/wsdl_coverage.py +0 -0
  216. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli.egg-info/dependency_links.txt +0 -0
  217. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli.egg-info/entry_points.txt +0 -0
  218. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli.egg-info/requires.txt +0 -0
  219. {direct_cli-0.4.0 → direct_cli-0.4.2}/direct_cli.egg-info/top_level.txt +0 -0
  220. {direct_cli-0.4.0 → direct_cli-0.4.2}/docs/audits/API_COVERAGE.md +0 -0
  221. {direct_cli-0.4.0 → direct_cli-0.4.2}/docs/audits/issue-198-mutating-wsdl-audit.md +0 -0
  222. {direct_cli-0.4.0 → direct_cli-0.4.2}/docs/superpowers/plans/2026-04-12-issue-32-completion.md +0 -0
  223. {direct_cli-0.4.0 → direct_cli-0.4.2}/docs/superpowers/specs/2026-04-23-vendor-tapi-yandex-direct-design.md +0 -0
  224. {direct_cli-0.4.0 → direct_cli-0.4.2}/scripts/anonymize_cassettes.py +0 -0
  225. {direct_cli-0.4.0 → direct_cli-0.4.2}/scripts/build_api_coverage_checklist.py +0 -0
  226. {direct_cli-0.4.0 → direct_cli-0.4.2}/scripts/build_wsdl_optional_field_audit.py +0 -0
  227. {direct_cli-0.4.0 → direct_cli-0.4.2}/scripts/check_reports_drift.py +0 -0
  228. {direct_cli-0.4.0 → direct_cli-0.4.2}/scripts/check_wsdl_drift.py +0 -0
  229. {direct_cli-0.4.0 → direct_cli-0.4.2}/scripts/preflight_check.sh +0 -0
  230. {direct_cli-0.4.0 → direct_cli-0.4.2}/scripts/refresh_reports_cache.py +0 -0
  231. {direct_cli-0.4.0 → direct_cli-0.4.2}/scripts/refresh_wsdl_cache.py +0 -0
  232. {direct_cli-0.4.0 → direct_cli-0.4.2}/scripts/release_pypi.sh +0 -0
  233. {direct_cli-0.4.0 → direct_cli-0.4.2}/scripts/sandbox_write_audit.py +0 -0
  234. {direct_cli-0.4.0 → direct_cli-0.4.2}/scripts/sandbox_write_live.py +0 -0
  235. {direct_cli-0.4.0 → direct_cli-0.4.2}/scripts/test_dangerous_commands.sh +0 -0
  236. {direct_cli-0.4.0 → direct_cli-0.4.2}/scripts/test_safe_commands.sh +0 -0
  237. {direct_cli-0.4.0 → direct_cli-0.4.2}/scripts/test_sandbox_write.sh +0 -0
  238. {direct_cli-0.4.0 → direct_cli-0.4.2}/scripts/update_vendor.sh +0 -0
  239. {direct_cli-0.4.0 → direct_cli-0.4.2}/setup.cfg +0 -0
  240. {direct_cli-0.4.0 → direct_cli-0.4.2}/setup.py +0 -0
  241. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/API_COVERAGE.md +0 -0
  242. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/API_ISSUE_AUDIT.md +0 -0
  243. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/WSDL_OPTIONAL_FIELD_AUDIT.md +0 -0
  244. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/__init__.py +0 -0
  245. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/_orphan_store.py +0 -0
  246. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteAdExtensions.test_add_delete.yaml +0 -0
  247. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteAdGroups.test_add_update_delete.yaml +0 -0
  248. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteAdImages.test_add_delete.yaml +0 -0
  249. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteAds.test_add_text_ad_update_delete.yaml +0 -0
  250. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteAudienceTargets.test_add_delete.yaml +0 -0
  251. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteBidModifiersAdd.test_add_delete_mobile.yaml +0 -0
  252. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteBidModifiersSet.test_set_without_id_is_rejected.yaml +0 -0
  253. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteBids.test_set_bid.yaml +0 -0
  254. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteBidsRead.test_bids_get.yaml +0 -0
  255. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteBidsRead.test_bids_set_auto.yaml +0 -0
  256. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteCampaignDraftLifecycle.test_draft_create_get_delete.yaml +0 -0
  257. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteCampaigns.test_campaign_lifecycle.yaml +0 -0
  258. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteDynamicAds.test_add_update_delete.yaml +0 -0
  259. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteFeeds.test_add_update_delete.yaml +0 -0
  260. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteKeywordBids.test_set_keyword_bid.yaml +0 -0
  261. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteKeywords.test_add_update_delete.yaml +0 -0
  262. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteNegativeKeywordSharedSets.test_add_update_delete.yaml +0 -0
  263. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteRetargeting.test_add_delete.yaml +0 -0
  264. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteRetargetingUpdate.test_retargeting_update.yaml +0 -0
  265. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteSitelinks.test_add_delete.yaml +0 -0
  266. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteSmartAdTargets.test_add_update_delete.yaml +0 -0
  267. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteStrategies.test_strategies_lifecycle.yaml +0 -0
  268. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_integration_write/TestWriteVCards.test_add_delete.yaml +0 -0
  269. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[adextensions_get].yaml +0 -0
  270. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[adgroups_get].yaml +0 -0
  271. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[adimages_get].yaml +0 -0
  272. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[ads_get].yaml +0 -0
  273. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[advideos_get].yaml +0 -0
  274. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[audiencetargets_get].yaml +0 -0
  275. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[bidmodifiers_get].yaml +0 -0
  276. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[bids_get].yaml +0 -0
  277. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[businesses_get].yaml +0 -0
  278. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[campaigns_get].yaml +0 -0
  279. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[changes_check].yaml +0 -0
  280. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[changes_check_campaigns].yaml +0 -0
  281. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[changes_check_dictionaries].yaml +0 -0
  282. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[clients_get].yaml +0 -0
  283. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[dictionaries_get].yaml +0 -0
  284. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[dynamicads_get].yaml +0 -0
  285. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[dynamicfeedadtargets_get].yaml +0 -0
  286. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[feeds_get].yaml +0 -0
  287. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[keywordbids_get].yaml +0 -0
  288. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[keywords_get].yaml +0 -0
  289. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[keywordsresearch_deduplicate].yaml +0 -0
  290. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[keywordsresearch_has_search_volume].yaml +0 -0
  291. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[leads_get].yaml +0 -0
  292. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[negativekeywordsharedsets_get].yaml +0 -0
  293. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[reports_get].yaml +0 -0
  294. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[sitelinks_get].yaml +0 -0
  295. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[smartadtargets_get].yaml +0 -0
  296. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[turbopages_get].yaml +0 -0
  297. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[v4events_get_events_log].yaml +0 -0
  298. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[v4finance_get_clients_units].yaml +0 -0
  299. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[v4forecast_list].yaml +0 -0
  300. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[v4goals_get_stat_goals].yaml +0 -0
  301. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[v4tags_get_campaigns].yaml +0 -0
  302. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[v4wordstat_list_reports].yaml +0 -0
  303. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_read_command[vcards_get].yaml +0 -0
  304. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_read_cassettes/test_v4finance_check_payment_unknown_transaction.yaml +0 -0
  305. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_adgroups_add_update_delete.yaml +0 -0
  306. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_adimages_add_get_delete.yaml +0 -0
  307. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_ads_suspend_resume_archive_unarchive.yaml +0 -0
  308. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_advideos_add_get.yaml +0 -0
  309. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_bids_set.yaml +0 -0
  310. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_campaign_create_get_delete.yaml +0 -0
  311. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_creatives_chain_advideo_to_creative.yaml +0 -0
  312. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_dynamicads_add_delete.yaml +0 -0
  313. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_dynamicads_suspend_resume.yaml +0 -0
  314. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywordbids_set.yaml +0 -0
  315. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_keywords_suspend_resume.yaml +0 -0
  316. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/cassettes/test_v5_live_write/test_v5_live_draft_sitelinks_add_get_delete.yaml +0 -0
  317. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/fixtures/test-video.mp4 +0 -0
  318. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/reports_cache/raw/fields-list.html +0 -0
  319. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/reports_cache/raw/headers.html +0 -0
  320. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/reports_cache/raw/period.html +0 -0
  321. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/reports_cache/raw/spec.html +0 -0
  322. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/reports_cache/raw/type.html +0 -0
  323. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/reports_cache/spec.json +0 -0
  324. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_auth_write_json.py +0 -0
  325. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_changes.py +0 -0
  326. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_cli_contract.py +0 -0
  327. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_integration_write.py +0 -0
  328. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_reports_drift.py +0 -0
  329. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_sandbox_write_audit.py +0 -0
  330. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_smoke_matrix.py +0 -0
  331. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_unknown_option_hints.py +0 -0
  332. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_v4_contracts.py +0 -0
  333. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_v4_foundation.py +0 -0
  334. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_v4_live_contracts.py +0 -0
  335. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_v4_runtime_shape.py +0 -0
  336. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_v4_safety.py +0 -0
  337. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/test_wsdl_parity_gate.py +0 -0
  338. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/adextensions.xml +0 -0
  339. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/adgroups.xml +0 -0
  340. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/adimages.xml +0 -0
  341. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/ads.xml +0 -0
  342. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/advideos.xml +0 -0
  343. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/agencyclients.xml +0 -0
  344. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/audiencetargets.xml +0 -0
  345. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/bidmodifiers.xml +0 -0
  346. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/bids.xml +0 -0
  347. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/businesses.xml +0 -0
  348. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/campaigns.xml +0 -0
  349. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/changes.xml +0 -0
  350. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/clients.xml +0 -0
  351. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/creatives.xml +0 -0
  352. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/dictionaries.xml +0 -0
  353. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/dynamicfeedadtargets.xml +0 -0
  354. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/dynamictextadtargets.xml +0 -0
  355. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/feeds.xml +0 -0
  356. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/imports/adextensiontypes.xsd +0 -0
  357. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/imports/general.xsd +0 -0
  358. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/imports/generalclients.xsd +0 -0
  359. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/keywordbids.xml +0 -0
  360. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/keywords.xml +0 -0
  361. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/keywordsresearch.xml +0 -0
  362. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/leads.xml +0 -0
  363. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/negativekeywordsharedsets.xml +0 -0
  364. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/retargetinglists.xml +0 -0
  365. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/sitelinks.xml +0 -0
  366. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/smartadtargets.xml +0 -0
  367. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/strategies.xml +0 -0
  368. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/turbopages.xml +0 -0
  369. {direct_cli-0.4.0 → direct_cli-0.4.2}/tests/wsdl_cache/vcards.xml +0 -0
@@ -1,5 +1,202 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.2
4
+
5
+ **BREAKING CHANGES - get requires SelectionCriteria (#498):**
6
+
7
+ - `adgroups` / `ads` / `keywords` / `strategies` / `creatives` / `dynamicads` /
8
+ `smartadtargets` / `audiencetargets` `get` now refuse an empty
9
+ `SelectionCriteria` before the API call, raising a `UsageError` that asks for
10
+ at least one filter — instead of sending `{"SelectionCriteria": {}}` (which the
11
+ API rejects with the opaque error 4001 for ad-group/ad/keyword resources).
12
+ Extends the same guard already shipped for `bids` / `keywordbids` in 0.4.1
13
+ (#483). WSDL declares `GetRequest.SelectionCriteria` as `minOccurs=1` for all
14
+ eight resources.
15
+ - `retargeting get` gains `--dry-run` and the shared read/pagination option
16
+ stack; its `SelectionCriteria` stays optional (WSDL `minOccurs=0`), so a
17
+ no-filter call is still valid and now omits the empty criteria from the
18
+ payload.
19
+ - All eight commands and `retargeting get` build their request via the shared
20
+ `build_common_params` helper, completing the dedup epic #491 (B3c).
21
+
22
+ **BREAKING CHANGES - auth precedence (#489):**
23
+
24
+ - Base `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` credentials from the
25
+ environment or current-directory `.env` now win over the active OAuth profile
26
+ selected by `direct auth use` when `--profile` is not passed. Explicit
27
+ `--token`, `--login`, and `--profile` still take priority.
28
+ - `direct auth status` reports the selected effective credentials, including
29
+ base env/`.env` and secret-manager fallbacks, instead of reporting only the
30
+ active OAuth profile.
31
+ - `direct auth login` can now ask interactive users whether to save the OAuth
32
+ access token and resolved login into the current-directory `.env`; the default
33
+ answer is no.
34
+
35
+ **Docs — live-write coverage limitation (#538):**
36
+
37
+ - Documented why the SMART_CAMPAIGN / DYNAMIC_TEXT_CAMPAIGN / `adimages`
38
+ live-write lifecycle (`dynamicads`, `smartadtargets`, `adimages`) stays
39
+ recorded only as 3500/5004 error cassettes. Verified via direct API calls that
40
+ the available sandbox **agency** account has no client accounts under it
41
+ (`agencyclients.get` → empty), cannot create one (3001 "No rights to create
42
+ clients", access by request only), and that without a client login every
43
+ agency-scoped mutation returns 8000. Closed #538 as a documented account-tier
44
+ limitation; no CLI code change. See `tests/MANUAL_COVERAGE.md`.
45
+
46
+ ## 0.4.1
47
+
48
+ Russian-default CLI localization across all command modules (epic #466).
49
+
50
+ **Fixed — bug hunt (#483):**
51
+
52
+ - `bids get` / `keywordbids get`: refuse an empty `SelectionCriteria` before the
53
+ API call, raising a `UsageError` that asks for at least one filter
54
+ (`--campaign-ids` / `--adgroup-ids` / `--keyword-ids` / `--serving-statuses`)
55
+ instead of letting the API reject it with the opaque error 4001.
56
+ - `bids set-auto`: require exactly one of `--campaign-id`, `--adgroup-id`, or
57
+ `--keyword-id` via the shared `add_single_id_selector` (the three are mutually
58
+ exclusive per the API docs), matching `bids set`.
59
+ - `reports get`: reject a `--fields` value that parses to an empty list (for
60
+ example `",,,"`) before building the request, instead of sending an invalid
61
+ `FieldNames: []` (API error 8000).
62
+ - Error-handling consistency: `get`/lifecycle handlers across `bids`,
63
+ `keywordbids`, `negativekeywordsharedsets`, `balance`, `strategies`,
64
+ `retargeting`, `ads` (all 8 commands), and `advideos` now re-raise
65
+ `click.UsageError` / `click.ClickException` before the generic
66
+ `except Exception`, so validation errors keep their Click formatting and
67
+ exit code 2 instead of being downgraded to an `Abort`.
68
+ - Vendor `tapi_yandex_direct`: `to_columns()` no longer raises `IndexError` on
69
+ report rows shorter than the header (pads with `""`); the error handler reads
70
+ `error_detail` with `.get()` so an unfamiliar error structure no longer masks
71
+ the original API error with a `KeyError`.
72
+ - `utils.parse_priority_goals_spec`: corrected the item type annotation to
73
+ `List[Dict[str, Any]]` (items hold `"YES"/"NO"` strings, not only ints).
74
+
75
+ **Fixed — `--help` hung on a client-login network call (#480 follow-up):**
76
+
77
+ - After #480, `get_credentials` resolved the bare Client-Login via a network
78
+ `clients.get` on every CLI invocation — including `<group> --help` — whenever
79
+ an OAuth profile with an email login had not yet been migrated. That call had
80
+ no timeout, so a slow link or a Yandex SmartCaptcha gateway could hang the
81
+ CLI. Help/version passes now skip the resolver, the resolver is capped with a
82
+ hard timeout, and the unit suite neutralizes it so tests never touch the
83
+ network.
84
+
85
+ **Fixed — auth login saved Passport email, breaking v4 (#480):**
86
+
87
+ - `direct auth login` (OAuth / PKCE) stored the Passport email
88
+ (`<login>@yandex.ru`) in `auth.json`, which Direct v4 AccountManagement
89
+ rejects with `FaultCode 259` ("This client does not exist") — breaking
90
+ `direct balance` and `direct v4account ...`. Login now resolves the bare
91
+ **Client-Login** via a one-shot v5 `clients.get` (`resolve_account_login`),
92
+ falling back to the Passport login only if that call fails.
93
+ - `get_credentials` migrates older profiles in place: when a stored OAuth login
94
+ is an email whose local part matches the token owner's resolved Client-Login,
95
+ it is rewritten to the bare login (one-time, persisted). Agency profiles whose
96
+ login differs from the token owner are never clobbered, and an explicit
97
+ `--login` is never overridden.
98
+
99
+ **Localized — interpolated error messages (#478), completing epic #466:**
100
+
101
+ - Rewrote all 121 interpolated `click.UsageError` / `click.BadParameter` /
102
+ `print_*` messages (114 f-strings + 7 string concatenations) across the
103
+ command modules into the stable `t("<template>").format(**kwargs)` pattern,
104
+ with 96 unique English templates translated to Russian (6 shared templates,
105
+ e.g. `Provide a non-empty comma-separated {wsdl_key} list.`, in
106
+ `common.json`). Placeholder names, conversions, and format specs are
107
+ preserved verbatim in both locales, so the rendered English text is
108
+ byte-identical to before and the Russian render fills the same fields.
109
+ - `t()` gained `@overload` signatures (`str -> str`, `None -> None`) so
110
+ `t(...).format(...)` type-checks without touching call sites.
111
+ - `tests/test_i18n.py` now (a) flags f-string / concat — not just static —
112
+ bare literals in `_RUNTIME_MESSAGE_FUNCS` calls (also covering
113
+ `print_success`), accepting only `t(...)` / `t(...).format(...)`; and
114
+ (b) asserts placeholder parity between every English template key and its
115
+ Russian translation. This completes the error-message localization checklist
116
+ of epic #466.
117
+
118
+ **Localized — static error messages (#477):**
119
+
120
+ - Wrapped all 179 static `click.UsageError` / `click.BadParameter` string
121
+ literals (and the `auth` OAuth-code `click.prompt`) across 33 command
122
+ modules in `t(...)`, with Russian translations added to the per-module
123
+ `translations/*.json` catalogs; seven messages shared across modules
124
+ (e.g. `Provide at least one field to update`,
125
+ `--status and --statuses are mutually exclusive`) live in `common.json`.
126
+ Validation errors now render in Russian by default and in English under
127
+ `--locale en`. Flag names, enum values, and WSDL field names are unchanged.
128
+ - Extended `tests/test_i18n.py::test_localized_groups_wrap_runtime_messages`
129
+ to also scan `UsageError` / `BadParameter` / `prompt` / `confirm` (bare or
130
+ `click.<Name>`); a bare string literal first argument is rejected. Only
131
+ static `ast.Constant` literals are enforced for now — interpolated
132
+ (f-string / concat) messages are stage B (#478).
133
+ - Test suite defaults the CLI locale to English (`tests/conftest.py` autouse
134
+ fixture) so the existing English error-contract assertions stay valid; the
135
+ Russian default remains covered by `tests/test_i18n.py`.
136
+
137
+ **Added — scalable i18n mechanism (#467):**
138
+
139
+ - Source-string-keyed translation catalog: the English `help=` / docstring /
140
+ epilog text is the catalog key, with Russian translations in external
141
+ `direct_cli/translations/*.json` files (one per module, plus shared
142
+ `common.json`). No `cls=`/`help_key` edits in command modules —
143
+ `cli._apply_directcli_classes` retypes every plain `click.Option` to
144
+ `LocalizedOption` and localizes command/group docstrings and epilogs at
145
+ render time.
146
+ - `t()` is now source-keyed and context-free safe (`set_active_locale`), so
147
+ `print_*` runtime messages localize too. `--locale` is eager so the root
148
+ `--help` epilog honors an inline `--locale`.
149
+ - `tests/test_i18n.py` gains a `LOCALIZED_GROUPS` registry with two enforced
150
+ invariants per localized module: translation completeness (no silent English
151
+ leak under the Russian default) and `print_*` runtime-message wrapping.
152
+ - `v4finance` migrated to the new mechanism and fully localized as the
153
+ reference module.
154
+
155
+ **Localized — Core search (#468):**
156
+
157
+ - Russian help/docstrings for `campaigns`, `ads`, `adgroups`, `keywords`,
158
+ `keywordbids`, and `bids` (510 unique strings across the six modules).
159
+ WSDL field paths, enum values, and flag names are kept verbatim; only
160
+ human-readable text is translated. These groups join `LOCALIZED_GROUPS`,
161
+ so their translation completeness is now enforced by `test_i18n.py`.
162
+
163
+ **Localized — Targeting & creatives (#469):**
164
+
165
+ - Russian help/docstrings for `strategies`, `bidmodifiers`, `smartadtargets`,
166
+ `vcards`, `feeds`, `dynamicads`, `audiencetargets`, `dynamicfeedadtargets`,
167
+ `retargeting`, `negativekeywordsharedsets`, `adextensions`, `adimages`,
168
+ `sitelinks`, `creatives`, `advideos`, and `turbopages` (247 unique strings
169
+ across the sixteen modules). WSDL field paths, enum values, and flag names
170
+ are kept verbatim; only human-readable text is translated. These groups
171
+ join `LOCALIZED_GROUPS`, so their translation completeness is now enforced
172
+ by `test_i18n.py`.
173
+
174
+ **Localized — Account, clients, reporting (#470):**
175
+
176
+ - Russian help/docstrings for `clients`, `agencyclients`, `reports`, `changes`,
177
+ `auth`, `leads`, `dictionaries`, `keywordsresearch`, `businesses`, and
178
+ `balance` (142 source strings across the ten modules). WSDL field paths,
179
+ enum values, and flag names are kept verbatim; only human-readable text is
180
+ translated.
181
+ - First modules with localized **runtime messages**: `print_*` calls carrying a
182
+ human-readable literal (`auth` interactive prompts, the `agencyclients delete`
183
+ not-supported notice) are now wrapped in `t()` so they follow the active
184
+ locale. `print_error(str(e))` API-error passthroughs are unchanged. These
185
+ groups join `LOCALIZED_GROUPS`, enforcing both translation completeness and
186
+ the runtime-message wrapping invariant.
187
+
188
+ **Localized — v4 Live services (#471), completing epic #466:**
189
+
190
+ - Russian help/docstrings for `v4account`, `v4tags`, `v4forecast`,
191
+ `v4wordstat`, `v4events`, `v4adimage`, `v4goals`, `v4keywords`, and `v4meta`
192
+ (87 source strings across the nine modules). `v4finance` was already
193
+ localized as the reference module in #467.
194
+ - With these groups, **all 42 CLI command groups are in `LOCALIZED_GROUPS`**:
195
+ the completeness invariant now covers the entire command tree, so every
196
+ English help/docstring string ships with a Russian translation under the
197
+ Russian default. The `v4 Live` epilog (single-sourced docs URL) stays
198
+ verbatim per the URL-registry rule. This closes the localization epic #466.
199
+
3
200
  ## 0.4.0
4
201
 
5
202
  Milestone release closing the 0.4.0 roadmap (#123): typed Yandex Direct
@@ -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.0
3
+ Version: 0.4.2
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
@@ -208,6 +212,31 @@ direct v4wordstat get-report --report-id 123 --format table
208
212
  direct v4wordstat delete-report --report-id 123
209
213
  ```
210
214
 
215
+ ### V4 Live Keyword Suggestions
216
+
217
+ `get-suggestion` returns up to 20 related phrases for the seed phrases. Repeat
218
+ `--keyword` for multiple seeds. The method consumes API points.
219
+
220
+ ```bash
221
+ direct v4keywords get-suggestion --keyword холодильник --keyword камера
222
+ direct v4keywords get-suggestion --keyword "buy laptop" --format table
223
+ direct v4keywords get-suggestion --keyword холодильник --dry-run
224
+ ```
225
+
226
+ ### V4 Live Ad-Image Associations
227
+
228
+ `AdImageAssociation` is exposed as two typed commands. `get` reads ad-to-image
229
+ associations via an optional selection filter (an empty filter returns up to
230
+ 10000 associations). `set` attaches or detaches images: `--association AD_ID=HASH`
231
+ attaches an image, `--association AD_ID` (no hash) detaches the current image
232
+ (max 10000 associations per call).
233
+
234
+ ```bash
235
+ direct v4adimage get --ad-ids 123,456 --status-moderate Yes --limit 20
236
+ direct v4adimage get --campaign-ids 789 --format table
237
+ direct v4adimage set --association 123=abc123hash --association 456 --dry-run
238
+ ```
239
+
211
240
  ### V4 Live Budget Forecasts
212
241
 
213
242
  Budget forecasts are asynchronous. Direct CLI makes exactly one API call per
@@ -233,9 +262,15 @@ per-request token from `--master-token`, `--operation-num`, and
233
262
  Environment variables are
234
263
  `YANDEX_DIRECT_MASTER_TOKEN`, `YANDEX_DIRECT_FINANCE_LOGIN`,
235
264
  `YANDEX_DIRECT_FINANCE_TOKEN`, and `YANDEX_DIRECT_OPERATION_NUM`.
236
- `transfer-money` and `pay-campaigns` are dry-run-only in this release and
237
- always require `--dry-run`; `create-invoice` can be sent live when `--dry-run`
238
- is omitted. Dry-run output masks the financial token.
265
+ `transfer-money`, `pay-campaigns`, and `pay-campaigns-by-card` are dry-run-only
266
+ in this release and always require `--dry-run`; `create-invoice` can be sent
267
+ live when `--dry-run` is omitted. Dry-run output masks the financial token.
268
+ `pay-campaigns-by-card` has an undocumented request shape; its dry-run body
269
+ mirrors the documented `pay-campaigns` shape as a best-effort preview.
270
+
271
+ > ⚠ **Finance commands have not been tested against the live API.** Treat the
272
+ > request shapes as best-effort and always verify with `--dry-run` before
273
+ > sending anything that runs live (`create-invoice`).
239
274
 
240
275
  ```bash
241
276
  direct v4finance get-clients-units --logins client-login,other-client --format table
@@ -244,6 +279,7 @@ direct v4finance create-invoice --payment 123=100.50 --payment 456=25 --currency
244
279
  direct v4finance check-payment --custom-transaction-id A123456789012345678901234567890B
245
280
  direct v4finance transfer-money --from-campaign-id 123 --to-campaign-id 456 --amount 100.50 --currency RUB --master-token MASTER_TOKEN --operation-num 123 --finance-login agency-login --dry-run
246
281
  direct v4finance pay-campaigns --campaign-ids 123,456 --amount 100.50 --contract-id CONTRACT_ID --pay-method Bank --currency RUB --master-token MASTER_TOKEN --operation-num 123 --finance-login agency-login --dry-run
282
+ direct v4finance pay-campaigns-by-card --campaign-ids 123,456 --amount 100.50 --currency RUB --master-token MASTER_TOKEN --operation-num 128 --finance-login agency-login --dry-run
247
283
  ```
248
284
 
249
285
  ### V4 Live Shared Account
@@ -272,6 +308,20 @@ direct v4account account-management --action TransferMoney --from-account-id 132
272
308
  direct --sandbox v4account enable-shared-account --client-login client-login
273
309
  ```
274
310
 
311
+ ### V4 Live — Intentionally Omitted Methods
312
+
313
+ Some v4 Live methods present in the API registry are intentionally **not**
314
+ exposed as CLI commands:
315
+
316
+ - `DeleteReport` / `DeleteOfflineReport` — disabled by Yandex (the official
317
+ docs list them under "Отключенные методы" / "Метод отключен. Используйте API
318
+ версии 5"). Use the v5 reports API instead.
319
+ - `PingAPI`, `PingAPI_X`, `GetVersion`, `GetAvailableVersions` — service
320
+ diagnostics / version probes with no documented request shape; not useful as
321
+ user-facing CLI commands.
322
+ - `PayCampaignsByCard` is exposed but **dry-run-only** (undocumented,
323
+ financially sensitive — see V4 Live Finance above).
324
+
275
325
  ### CLI Convention
276
326
 
277
327
  The current CLI convention is defined as follows.
@@ -1019,27 +1069,32 @@ direct --profile agency1 campaigns get
1019
1069
  - Если первый non-interactive шаг включает `--client-secret`, secret запоминается для последующего completion step.
1020
1070
  - Если profile уже хранит confidential OAuth client, `direct auth login --code CODE --profile NAME` использует сохраненные `client_id` и `client_secret`.
1021
1071
  - `direct auth login --oauth-token TOKEN` импортирует access token вручную и не включает auto-refresh.
1072
+ - После успешного интерактивного входа Direct CLI спрашивает, сохранить ли
1073
+ access token и login в `.env` текущей рабочей папки; non-interactive вход этот
1074
+ вопрос не задаёт.
1022
1075
 
1023
1076
  Порядок выбора credentials:
1024
1077
 
1025
1078
  | Приоритет | Источник | Пример |
1026
1079
  |-----------|----------|--------|
1027
1080
  | 1 | Явные CLI-опции | `direct --token TOKEN --login LOGIN campaigns get` |
1028
- | 2 | OAuth profile storage | `direct --profile agency1 campaigns get` |
1029
- | 3 | Профильные env vars | `YANDEX_DIRECT_TOKEN_AGENCY1`, `YANDEX_DIRECT_LOGIN_AGENCY1` |
1030
- | 4 | Базовые env vars или `.env` из папки запуска | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
1081
+ | 2 | Явно выбранный profile | `direct --profile agency1 campaigns get` |
1082
+ | 3 | Базовые env vars или `.env` из папки запуска | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
1083
+ | 4 | Активный profile | `direct auth use --profile agency1` |
1031
1084
  | 5 | 1Password references | `--op-token-ref`, `YANDEX_DIRECT_OP_TOKEN_REF` |
1032
1085
  | 6 | Bitwarden references | `--bw-token-ref`, `YANDEX_DIRECT_BW_TOKEN_REF` |
1033
1086
 
1034
1087
  Direct CLI автоматически читает только `.env` из текущей рабочей папки, то есть
1035
1088
  из папки, где запущена команда `direct`. Он не ищет `.env` от папки
1036
- установленного пакета или исходного кода. Если профиль выбран через `--profile`
1037
- или `direct auth use --profile NAME`, Direct CLI не подставляет base
1038
- `YANDEX_DIRECT_LOGIN`; это защищает от смешивания токена из профиля с логином из
1039
- `.env` текущей рабочей папки. Для нескольких аккаунтов используйте OAuth
1040
- profiles или профильные env vars, а не базовые credentials.
1041
-
1042
- > **В тестах порядок инвертирован.** Live-API тесты (например `tests/test_v4_live_contracts.py`) сначала читают `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` из окружения, затем падают на активный профиль `direct auth`, и скипают тест если ни того ни другого нет. Это сделано специально: на машине разработчика с активным профилем обычный `pytest` не должен молча идти в боевой API. Контракт зафиксирован в `CLAUDE.md`.
1089
+ установленного пакета или исходного кода. Без явного `--profile` базовые
1090
+ `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` из окружения или cwd `.env`
1091
+ побеждают активный OAuth profile. С явным `--profile` Direct CLI использует
1092
+ только OAuth/profile-env credentials этого профиля и не подставляет base
1093
+ `YANDEX_DIRECT_LOGIN`; это защищает от смешивания аккаунтов. `direct auth status`
1094
+ без `--profile` показывает реально выбранный источник credentials, а с
1095
+ `--profile` показывает этот профиль.
1096
+
1097
+ > **Тесты используют безопасный порядок credentials.** Live-API тесты (например `tests/test_v4_live_contracts.py`) сначала читают `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` из окружения, затем падают на активный профиль `direct auth`, и скипают тест если ни того ни другого нет. Это защищает от случайного обращения к production API на машине разработчика с активным profile. Контракт зафиксирован в `CLAUDE.md`.
1043
1098
 
1044
1099
  Установка остаётся через `pip install direct-cli`, а запуск команд теперь идет
1045
1100
  через `direct`. Вызов deprecated entrypoint `direct-cli` завершается ошибкой с
@@ -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
@@ -165,6 +169,31 @@ direct v4wordstat get-report --report-id 123 --format table
165
169
  direct v4wordstat delete-report --report-id 123
166
170
  ```
167
171
 
172
+ ### V4 Live Keyword Suggestions
173
+
174
+ `get-suggestion` returns up to 20 related phrases for the seed phrases. Repeat
175
+ `--keyword` for multiple seeds. The method consumes API points.
176
+
177
+ ```bash
178
+ direct v4keywords get-suggestion --keyword холодильник --keyword камера
179
+ direct v4keywords get-suggestion --keyword "buy laptop" --format table
180
+ direct v4keywords get-suggestion --keyword холодильник --dry-run
181
+ ```
182
+
183
+ ### V4 Live Ad-Image Associations
184
+
185
+ `AdImageAssociation` is exposed as two typed commands. `get` reads ad-to-image
186
+ associations via an optional selection filter (an empty filter returns up to
187
+ 10000 associations). `set` attaches or detaches images: `--association AD_ID=HASH`
188
+ attaches an image, `--association AD_ID` (no hash) detaches the current image
189
+ (max 10000 associations per call).
190
+
191
+ ```bash
192
+ direct v4adimage get --ad-ids 123,456 --status-moderate Yes --limit 20
193
+ direct v4adimage get --campaign-ids 789 --format table
194
+ direct v4adimage set --association 123=abc123hash --association 456 --dry-run
195
+ ```
196
+
168
197
  ### V4 Live Budget Forecasts
169
198
 
170
199
  Budget forecasts are asynchronous. Direct CLI makes exactly one API call per
@@ -190,9 +219,15 @@ per-request token from `--master-token`, `--operation-num`, and
190
219
  Environment variables are
191
220
  `YANDEX_DIRECT_MASTER_TOKEN`, `YANDEX_DIRECT_FINANCE_LOGIN`,
192
221
  `YANDEX_DIRECT_FINANCE_TOKEN`, and `YANDEX_DIRECT_OPERATION_NUM`.
193
- `transfer-money` and `pay-campaigns` are dry-run-only in this release and
194
- always require `--dry-run`; `create-invoice` can be sent live when `--dry-run`
195
- is omitted. Dry-run output masks the financial token.
222
+ `transfer-money`, `pay-campaigns`, and `pay-campaigns-by-card` are dry-run-only
223
+ in this release and always require `--dry-run`; `create-invoice` can be sent
224
+ live when `--dry-run` is omitted. Dry-run output masks the financial token.
225
+ `pay-campaigns-by-card` has an undocumented request shape; its dry-run body
226
+ mirrors the documented `pay-campaigns` shape as a best-effort preview.
227
+
228
+ > ⚠ **Finance commands have not been tested against the live API.** Treat the
229
+ > request shapes as best-effort and always verify with `--dry-run` before
230
+ > sending anything that runs live (`create-invoice`).
196
231
 
197
232
  ```bash
198
233
  direct v4finance get-clients-units --logins client-login,other-client --format table
@@ -201,6 +236,7 @@ direct v4finance create-invoice --payment 123=100.50 --payment 456=25 --currency
201
236
  direct v4finance check-payment --custom-transaction-id A123456789012345678901234567890B
202
237
  direct v4finance transfer-money --from-campaign-id 123 --to-campaign-id 456 --amount 100.50 --currency RUB --master-token MASTER_TOKEN --operation-num 123 --finance-login agency-login --dry-run
203
238
  direct v4finance pay-campaigns --campaign-ids 123,456 --amount 100.50 --contract-id CONTRACT_ID --pay-method Bank --currency RUB --master-token MASTER_TOKEN --operation-num 123 --finance-login agency-login --dry-run
239
+ direct v4finance pay-campaigns-by-card --campaign-ids 123,456 --amount 100.50 --currency RUB --master-token MASTER_TOKEN --operation-num 128 --finance-login agency-login --dry-run
204
240
  ```
205
241
 
206
242
  ### V4 Live Shared Account
@@ -229,6 +265,20 @@ direct v4account account-management --action TransferMoney --from-account-id 132
229
265
  direct --sandbox v4account enable-shared-account --client-login client-login
230
266
  ```
231
267
 
268
+ ### V4 Live — Intentionally Omitted Methods
269
+
270
+ Some v4 Live methods present in the API registry are intentionally **not**
271
+ exposed as CLI commands:
272
+
273
+ - `DeleteReport` / `DeleteOfflineReport` — disabled by Yandex (the official
274
+ docs list them under "Отключенные методы" / "Метод отключен. Используйте API
275
+ версии 5"). Use the v5 reports API instead.
276
+ - `PingAPI`, `PingAPI_X`, `GetVersion`, `GetAvailableVersions` — service
277
+ diagnostics / version probes with no documented request shape; not useful as
278
+ user-facing CLI commands.
279
+ - `PayCampaignsByCard` is exposed but **dry-run-only** (undocumented,
280
+ financially sensitive — see V4 Live Finance above).
281
+
232
282
  ### CLI Convention
233
283
 
234
284
  The current CLI convention is defined as follows.
@@ -976,27 +1026,32 @@ direct --profile agency1 campaigns get
976
1026
  - Если первый non-interactive шаг включает `--client-secret`, secret запоминается для последующего completion step.
977
1027
  - Если profile уже хранит confidential OAuth client, `direct auth login --code CODE --profile NAME` использует сохраненные `client_id` и `client_secret`.
978
1028
  - `direct auth login --oauth-token TOKEN` импортирует access token вручную и не включает auto-refresh.
1029
+ - После успешного интерактивного входа Direct CLI спрашивает, сохранить ли
1030
+ access token и login в `.env` текущей рабочей папки; non-interactive вход этот
1031
+ вопрос не задаёт.
979
1032
 
980
1033
  Порядок выбора credentials:
981
1034
 
982
1035
  | Приоритет | Источник | Пример |
983
1036
  |-----------|----------|--------|
984
1037
  | 1 | Явные CLI-опции | `direct --token TOKEN --login LOGIN campaigns get` |
985
- | 2 | OAuth profile storage | `direct --profile agency1 campaigns get` |
986
- | 3 | Профильные env vars | `YANDEX_DIRECT_TOKEN_AGENCY1`, `YANDEX_DIRECT_LOGIN_AGENCY1` |
987
- | 4 | Базовые env vars или `.env` из папки запуска | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
1038
+ | 2 | Явно выбранный profile | `direct --profile agency1 campaigns get` |
1039
+ | 3 | Базовые env vars или `.env` из папки запуска | `YANDEX_DIRECT_TOKEN`, `YANDEX_DIRECT_LOGIN` |
1040
+ | 4 | Активный profile | `direct auth use --profile agency1` |
988
1041
  | 5 | 1Password references | `--op-token-ref`, `YANDEX_DIRECT_OP_TOKEN_REF` |
989
1042
  | 6 | Bitwarden references | `--bw-token-ref`, `YANDEX_DIRECT_BW_TOKEN_REF` |
990
1043
 
991
1044
  Direct CLI автоматически читает только `.env` из текущей рабочей папки, то есть
992
1045
  из папки, где запущена команда `direct`. Он не ищет `.env` от папки
993
- установленного пакета или исходного кода. Если профиль выбран через `--profile`
994
- или `direct auth use --profile NAME`, Direct CLI не подставляет base
995
- `YANDEX_DIRECT_LOGIN`; это защищает от смешивания токена из профиля с логином из
996
- `.env` текущей рабочей папки. Для нескольких аккаунтов используйте OAuth
997
- profiles или профильные env vars, а не базовые credentials.
998
-
999
- > **В тестах порядок инвертирован.** Live-API тесты (например `tests/test_v4_live_contracts.py`) сначала читают `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` из окружения, затем падают на активный профиль `direct auth`, и скипают тест если ни того ни другого нет. Это сделано специально: на машине разработчика с активным профилем обычный `pytest` не должен молча идти в боевой API. Контракт зафиксирован в `CLAUDE.md`.
1046
+ установленного пакета или исходного кода. Без явного `--profile` базовые
1047
+ `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` из окружения или cwd `.env`
1048
+ побеждают активный OAuth profile. С явным `--profile` Direct CLI использует
1049
+ только OAuth/profile-env credentials этого профиля и не подставляет base
1050
+ `YANDEX_DIRECT_LOGIN`; это защищает от смешивания аккаунтов. `direct auth status`
1051
+ без `--profile` показывает реально выбранный источник credentials, а с
1052
+ `--profile` показывает этот профиль.
1053
+
1054
+ > **Тесты используют безопасный порядок credentials.** Live-API тесты (например `tests/test_v4_live_contracts.py`) сначала читают `YANDEX_DIRECT_TOKEN` / `YANDEX_DIRECT_LOGIN` из окружения, затем падают на активный профиль `direct auth`, и скипают тест если ни того ни другого нет. Это защищает от случайного обращения к production API на машине разработчика с активным profile. Контракт зафиксирован в `CLAUDE.md`.
1000
1055
 
1001
1056
  Установка остаётся через `pip install direct-cli`, а запуск команд теперь идет
1002
1057
  через `direct`. Вызов deprecated entrypoint `direct-cli` завершается ошибкой с