direct-cli 0.2.7__tar.gz → 0.2.9__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 (168) hide show
  1. {direct_cli-0.2.7 → direct_cli-0.2.9}/.github/workflows/api-coverage.yml +12 -0
  2. {direct_cli-0.2.7 → direct_cli-0.2.9}/AGENTS.md +1 -0
  3. {direct_cli-0.2.7 → direct_cli-0.2.9}/PKG-INFO +166 -34
  4. {direct_cli-0.2.7 → direct_cli-0.2.9}/README.md +162 -32
  5. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/__init__.py +4 -3
  6. direct_cli-0.2.9/direct_cli/_deprecated.py +11 -0
  7. direct_cli-0.2.9/direct_cli/_vendor/__init__.py +0 -0
  8. direct_cli-0.2.9/direct_cli/_vendor/tapi_yandex_direct/__init__.py +8 -0
  9. direct_cli-0.2.9/direct_cli/_vendor/tapi_yandex_direct/exceptions.py +78 -0
  10. direct_cli-0.2.9/direct_cli/_vendor/tapi_yandex_direct/resource_mapping.py +127 -0
  11. direct_cli-0.2.9/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.py +387 -0
  12. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/api.py +8 -2
  13. direct_cli-0.2.9/direct_cli/auth.py +450 -0
  14. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/cli.py +30 -4
  15. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/advideos.py +14 -10
  16. direct_cli-0.2.9/direct_cli/commands/auth.py +125 -0
  17. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/bidmodifiers.py +0 -61
  18. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/creatives.py +2 -5
  19. direct_cli-0.2.9/direct_cli/commands/dynamicfeedadtargets.py +280 -0
  20. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/keywords.py +35 -11
  21. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/keywordsresearch.py +2 -2
  22. direct_cli-0.2.9/direct_cli/commands/strategies.py +307 -0
  23. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/wsdl_coverage.py +21 -3
  24. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli.egg-info/PKG-INFO +166 -34
  25. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli.egg-info/SOURCES.txt +40 -0
  26. direct_cli-0.2.9/direct_cli.egg-info/entry_points.txt +3 -0
  27. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli.egg-info/requires.txt +3 -1
  28. direct_cli-0.2.9/docs/superpowers/specs/2026-04-23-vendor-tapi-yandex-direct-design.md +85 -0
  29. {direct_cli-0.2.7 → direct_cli-0.2.9}/pyproject.toml +10 -4
  30. direct_cli-0.2.9/scripts/anonymize_cassettes.py +155 -0
  31. direct_cli-0.2.9/scripts/build_api_coverage_report.py +236 -0
  32. {direct_cli-0.2.7 → direct_cli-0.2.9}/scripts/release_pypi.sh +3 -0
  33. direct_cli-0.2.9/scripts/update_vendor.sh +50 -0
  34. direct_cli-0.2.9/tests/API_COVERAGE.md +92 -0
  35. direct_cli-0.2.9/tests/API_ISSUE_AUDIT.md +60 -0
  36. direct_cli-0.2.9/tests/MANUAL_COVERAGE.md +75 -0
  37. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_adgroups_add_update_delete.yaml +374 -0
  38. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_adimages_add_get_delete.yaml +68 -0
  39. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_ads_add_update_delete.yaml +502 -0
  40. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_ads_suspend_resume_archive_unarchive.yaml +628 -0
  41. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_advideos_add_get.yaml +126 -0
  42. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_audiencetargets_add_delete.yaml +313 -0
  43. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_audiencetargets_suspend_resume.yaml +313 -0
  44. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_bids_set.yaml +437 -0
  45. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_campaign_create_get_delete.yaml +250 -0
  46. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_creatives_chain_advideo_to_creative.yaml +189 -0
  47. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_dynamicads_add_delete.yaml +67 -0
  48. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_dynamicads_suspend_resume.yaml +67 -0
  49. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_keywordbids_set.yaml +439 -0
  50. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_keywords_add_update_delete.yaml +439 -0
  51. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_keywords_suspend_resume.yaml +499 -0
  52. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_sitelinks_add_get_delete.yaml +191 -0
  53. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_smartadtargets_add_update_delete.yaml +191 -0
  54. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_smartadtargets_suspend_resume.yaml +191 -0
  55. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteAdExtensions.test_add_delete.yaml +11 -11
  56. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteAdGroups.test_add_update_delete.yaml +27 -27
  57. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteAdImages.test_add_delete.yaml +6 -6
  58. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteAds.test_add_text_ad_update_delete.yaml +27 -27
  59. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteAudienceTargets.test_add_delete.yaml +38 -38
  60. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteBidModifiersAdd.test_add_delete_mobile.yaml +22 -22
  61. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteBidModifiersSet.test_set_without_id_is_rejected.yaml +18 -18
  62. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteBids.test_set_bid.yaml +27 -27
  63. direct_cli-0.2.9/tests/cassettes/test_integration_write/TestWriteCampaignDraftLifecycle.test_draft_create_get_delete.yaml +218 -0
  64. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteCampaigns.test_campaign_lifecycle.yaml +33 -33
  65. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteDynamicAds.test_add_update_delete.yaml +4 -4
  66. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteFeeds.test_add_update_delete.yaml +16 -16
  67. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteKeywordBids.test_set_keyword_bid.yaml +27 -27
  68. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteKeywords.test_add_update_delete.yaml +27 -27
  69. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteNegativeKeywordSharedSets.test_add_update_delete.yaml +17 -17
  70. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteRetargeting.test_add_delete.yaml +10 -10
  71. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteSitelinks.test_add_delete.yaml +5 -5
  72. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteSmartAdTargets.test_add_update_delete.yaml +15 -15
  73. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteVCards.test_add_delete.yaml +23 -23
  74. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/conftest.py +26 -1
  75. direct_cli-0.2.9/tests/fixtures/test-video.mp4 +0 -0
  76. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/test_api_coverage.py +182 -43
  77. direct_cli-0.2.9/tests/test_auth_oauth.py +206 -0
  78. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/test_cli.py +46 -4
  79. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/test_comprehensive.py +3 -0
  80. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/test_dry_run.py +125 -27
  81. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/test_integration.py +150 -30
  82. direct_cli-0.2.9/tests/test_integration_live_write.py +1252 -0
  83. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/test_integration_write.py +369 -197
  84. direct_cli-0.2.9/tests/test_transport_contract.py +23 -0
  85. direct_cli-0.2.9/tests/wsdl_cache/dynamicfeedadtargets.xml +328 -0
  86. direct_cli-0.2.9/tests/wsdl_cache/strategies.xml +1100 -0
  87. direct_cli-0.2.7/direct_cli/auth.py +0 -167
  88. direct_cli-0.2.7/direct_cli.egg-info/entry_points.txt +0 -3
  89. direct_cli-0.2.7/scripts/build_api_coverage_report.py +0 -108
  90. {direct_cli-0.2.7 → direct_cli-0.2.9}/.env.example +0 -0
  91. {direct_cli-0.2.7 → direct_cli-0.2.9}/.github/copilot-instructions.md +0 -0
  92. {direct_cli-0.2.7 → direct_cli-0.2.9}/.github/workflows/claude-code-review.yml +0 -0
  93. {direct_cli-0.2.7 → direct_cli-0.2.9}/.github/workflows/claude.yml +0 -0
  94. {direct_cli-0.2.7 → direct_cli-0.2.9}/.gitignore +0 -0
  95. {direct_cli-0.2.7 → direct_cli-0.2.9}/CLAUDE.md +0 -0
  96. {direct_cli-0.2.7 → direct_cli-0.2.9}/MANIFEST.in +0 -0
  97. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/__init__.py +0 -0
  98. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/adextensions.py +0 -0
  99. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/adgroups.py +0 -0
  100. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/adimages.py +0 -0
  101. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/ads.py +0 -0
  102. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/agencyclients.py +0 -0
  103. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/audiencetargets.py +0 -0
  104. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/bids.py +0 -0
  105. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/businesses.py +0 -0
  106. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/campaigns.py +0 -0
  107. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/changes.py +0 -0
  108. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/clients.py +0 -0
  109. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/dictionaries.py +0 -0
  110. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/dynamicads.py +0 -0
  111. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/feeds.py +0 -0
  112. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/keywordbids.py +0 -0
  113. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/leads.py +0 -0
  114. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/negativekeywordsharedsets.py +0 -0
  115. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/reports.py +0 -0
  116. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/retargeting.py +0 -0
  117. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/sitelinks.py +0 -0
  118. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/smartadtargets.py +0 -0
  119. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/turbopages.py +0 -0
  120. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/commands/vcards.py +0 -0
  121. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/output.py +0 -0
  122. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/reports_coverage.py +0 -0
  123. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli/utils.py +0 -0
  124. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli.egg-info/dependency_links.txt +0 -0
  125. {direct_cli-0.2.7 → direct_cli-0.2.9}/direct_cli.egg-info/top_level.txt +0 -0
  126. {direct_cli-0.2.7 → direct_cli-0.2.9}/docs/superpowers/plans/2026-04-12-issue-32-completion.md +0 -0
  127. {direct_cli-0.2.7 → direct_cli-0.2.9}/scripts/check_reports_drift.py +0 -0
  128. {direct_cli-0.2.7 → direct_cli-0.2.9}/scripts/check_wsdl_drift.py +0 -0
  129. {direct_cli-0.2.7 → direct_cli-0.2.9}/scripts/refresh_reports_cache.py +0 -0
  130. {direct_cli-0.2.7 → direct_cli-0.2.9}/scripts/refresh_wsdl_cache.py +0 -0
  131. {direct_cli-0.2.7 → direct_cli-0.2.9}/setup.cfg +0 -0
  132. {direct_cli-0.2.7 → direct_cli-0.2.9}/setup.py +0 -0
  133. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/__init__.py +0 -0
  134. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/reports_cache/raw/fields-list.html +0 -0
  135. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/reports_cache/raw/headers.html +0 -0
  136. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/reports_cache/raw/spec.html +0 -0
  137. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/reports_cache/raw/type.html +0 -0
  138. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/reports_cache/spec.json +0 -0
  139. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/test_auth_bw.py +0 -0
  140. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/test_auth_op.py +0 -0
  141. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/test_reports_drift.py +0 -0
  142. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/adextensions.xml +0 -0
  143. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/adgroups.xml +0 -0
  144. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/adimages.xml +0 -0
  145. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/ads.xml +0 -0
  146. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/advideos.xml +0 -0
  147. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/agencyclients.xml +0 -0
  148. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/audiencetargets.xml +0 -0
  149. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/bidmodifiers.xml +0 -0
  150. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/bids.xml +0 -0
  151. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/businesses.xml +0 -0
  152. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/campaigns.xml +0 -0
  153. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/changes.xml +0 -0
  154. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/clients.xml +0 -0
  155. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/creatives.xml +0 -0
  156. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/dictionaries.xml +0 -0
  157. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/dynamictextadtargets.xml +0 -0
  158. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/feeds.xml +0 -0
  159. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/keywordbids.xml +0 -0
  160. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/keywords.xml +0 -0
  161. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/keywordsresearch.xml +0 -0
  162. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/leads.xml +0 -0
  163. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/negativekeywordsharedsets.xml +0 -0
  164. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/retargetinglists.xml +0 -0
  165. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/sitelinks.xml +0 -0
  166. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/smartadtargets.xml +0 -0
  167. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/turbopages.xml +0 -0
  168. {direct_cli-0.2.7 → direct_cli-0.2.9}/tests/wsdl_cache/vcards.xml +0 -0
@@ -45,12 +45,24 @@ jobs:
45
45
 
46
46
  report = json.loads(Path("api_coverage_report.json").read_text())
47
47
  summary = report["summary"]
48
+ model_gaps = report["model_gaps"]
48
49
  print("## API coverage summary")
49
50
  print()
50
51
  print(f"- Services checked: {summary['services_checked']}")
51
52
  print(f"- Missing methods: {summary['missing_service_methods']}")
52
53
  print(f"- Unexpected methods: {summary['unexpected_service_methods']}")
53
54
  print(f"- Strict parity OK: {summary['strict_parity_ok']}")
55
+ print(f"- Live model parity OK: {summary['live_model_parity_ok']}")
56
+ print(f"- Declared WSDL services: {model_gaps['declared_wsdl_services_count']}")
57
+ print(f"- Declared WSDL methods: {model_gaps['declared_wsdl_methods_count']}")
58
+ print(f"- Live-discovered services: {model_gaps['live_discovered_services_count']}")
59
+ print(f"- Total known methods: {model_gaps['total_known_methods_count']}")
60
+ print(f"- Model gaps: {model_gaps['live_model_gap_count']}")
61
+ for item in model_gaps["live_discovered_missing_services"][:10]:
62
+ methods = ", ".join(item["api_methods"])
63
+ print(f"- Missing service: {item['api_service']} ({methods})")
64
+ for item in model_gaps["live_discovery_errors"][:10]:
65
+ print(f"- Live discovery error: {item['api_service']}: {item['error']}")
54
66
  print(f"- Non-WSDL services: {len(report['non_wsdl_services'])}")
55
67
  print(f"- CLI helpers: {len(report['cli_helpers'])}")
56
68
  PY
@@ -106,6 +106,7 @@ Valid canonical examples:
106
106
  direct campaigns get --ids 1,2,3
107
107
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
108
108
  direct keywordsresearch has-search-volume --keywords "buy laptop,buy desktop"
109
+ direct smartadtargets update --id 456 --priority HIGH
109
110
  direct dynamicads set-bids --id 789 --bid 12.5
110
111
  direct dictionaries get-geo-regions --region-ids 225 --fields GeoRegionId,GeoRegionName
111
112
  ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: direct-cli
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Summary: Command-line interface for Yandex Direct API
5
5
  Author: axisrow
6
6
  License: MIT
@@ -18,7 +18,9 @@ Classifier: Programming Language :: Python :: 3.12
18
18
  Classifier: Programming Language :: Python :: 3.13
19
19
  Requires-Python: >=3.9
20
20
  Description-Content-Type: text/markdown
21
- Requires-Dist: tapi-yandex-direct>=2021.5.29
21
+ Requires-Dist: orjson
22
+ Requires-Dist: requests
23
+ Requires-Dist: tapi-wrapper2
22
24
  Requires-Dist: click>=8.0.0
23
25
  Requires-Dist: python-dotenv>=0.19.0
24
26
  Requires-Dist: tabulate>=0.8.0
@@ -66,14 +68,46 @@ Or pass credentials directly per command:
66
68
  direct --token YOUR_TOKEN --login YOUR_LOGIN campaigns get
67
69
  ```
68
70
 
71
+ Use profile-specific credentials from `.env`:
72
+
73
+ ```env
74
+ YANDEX_DIRECT_TOKEN_AGENCY1=token-1
75
+ YANDEX_DIRECT_LOGIN_AGENCY1=client-login-1
76
+ YANDEX_DIRECT_TOKEN_AGENCY2=token-2
77
+ YANDEX_DIRECT_LOGIN_AGENCY2=client-login-2
78
+ ```
79
+
80
+ OAuth and profile commands:
81
+
82
+ ```bash
83
+ direct auth login
84
+ direct auth login --profile agency1
85
+ direct auth login --code abc123 --profile agency1
86
+ direct auth login --oauth-token y0_example --profile agency1
87
+ direct auth list
88
+ direct auth use --profile agency1
89
+ direct auth status --profile agency1
90
+ direct --profile agency1 campaigns get
91
+ ```
92
+
93
+ Notes:
94
+ - Legacy profile environment variable is not used.
95
+ - Select credentials with `--profile`.
96
+ - `--login` remains Direct client login.
97
+ - Authorization is performed via `direct auth login`.
98
+ - Alias `auth_login` is not supported.
99
+
69
100
  Install with `pip install direct-cli`, then run commands with `direct`.
101
+ Invoking the deprecated `direct-cli` entrypoint exits with
102
+ `use direct instead of direct-cli`.
70
103
 
71
104
  ### Global Options
72
105
 
73
106
  | Option | Description |
74
107
  |--------|-------------|
75
108
  | `--token` | API access token |
76
- | `--login` | Yandex advertiser login |
109
+ | `--login` | Direct client login |
110
+ | `--profile` | Credential profile name |
77
111
  | `--sandbox` | Use sandbox API |
78
112
 
79
113
  ### CLI Convention
@@ -101,9 +135,10 @@ Naming rules:
101
135
  - multiword commands use kebab-case
102
136
  - examples: `get`, `set-bids`, `check-campaigns`, `has-search-volume`
103
137
 
104
- direct-cli owns the public naming contract. `tapi-yandex-direct` may
105
- influence the internal transport layer, but it does not define canonical CLI
106
- names.
138
+ The `direct` executable defines the public naming contract. The
139
+ `direct-cli` package name and deprecated shim do not define canonical CLI
140
+ names. `tapi-yandex-direct` may influence the internal transport layer, but it
141
+ does not define canonical CLI names.
107
142
 
108
143
  The current policy is canonical-only. Historical aliases are not preserved in
109
144
  the runtime CLI by default. If compatibility is ever needed, an alias must be
@@ -120,6 +155,7 @@ be supported.
120
155
  - If the API requires a complex object, the CLI must expose explicit flags or subcommands instead of forwarding raw JSON.
121
156
 
122
157
  #### Command Formatting Rules
158
+
123
159
  - Every canonical CLI command must be written strictly on a single line.
124
160
  - Multi-line command formatting is not allowed.
125
161
  - Shell line continuation using `\` is forbidden in canonical documentation, help text, tests, and examples.
@@ -184,6 +220,7 @@ Valid canonical examples:
184
220
  direct campaigns get --ids 1,2,3
185
221
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
186
222
  direct keywordsresearch has-search-volume --keywords "buy laptop,buy desktop"
223
+ direct smartadtargets update --id 456 --priority HIGH
187
224
  direct dynamicads set-bids --id 789 --bid 12.5 --context-bid 9 --priority HIGH
188
225
  direct dictionaries get-geo-regions --name Moscow --region-ids 225,187 --exact-names Москва,Санкт-Петербург --fields GeoRegionId,GeoRegionName
189
226
  ```
@@ -250,7 +287,7 @@ direct ads delete --id 99999
250
287
  ```bash
251
288
  direct keywords get --campaign-ids 1,2,3
252
289
  direct keywords add --adgroup-id 12345 --keyword "buy laptop" --bid 10.50 --context-bid 5.25 --user-param-1 segment-a --user-param-2 segment-b --dry-run
253
- direct keywords update --id 88888 --bid 15.00 --context-bid 6.00 --status SUSPENDED
290
+ direct keywords update --id 88888 --keyword "updated keyword text"
254
291
  direct keywords delete --id 88888
255
292
  ```
256
293
 
@@ -272,10 +309,14 @@ Available report types: `CAMPAIGN_PERFORMANCE_REPORT`, `ADGROUP_PERFORMANCE_REPO
272
309
  # Reference dictionaries and changes
273
310
  direct dictionaries get --names Currencies,GeoRegions
274
311
  direct dictionaries get-geo-regions --name Moscow --region-ids 225,187 --exact-names Москва,Санкт-Петербург --fields GeoRegionId,GeoRegionName
312
+
313
+ # Client info
314
+ direct clients get --fields ClientId,Login,Currency
315
+
316
+ # Changes
275
317
  direct changes check --campaign-ids 1,2,3 --timestamp 2026-04-14T00:00:00
276
318
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
277
319
  direct changes check-dictionaries
278
- direct clients get --fields ClientId,Login,Currency
279
320
 
280
321
  # Keyword research and retargeting
281
322
  direct keywordsresearch has-search-volume --keywords "buy laptop,buy desktop"
@@ -300,6 +341,17 @@ direct smartadtargets update --id 456 --priority HIGH
300
341
  direct smartadtargets set-bids --id 456 --average-cpc 10.5 --average-cpa 15 --priority HIGH
301
342
  direct dynamicads set-bids --id 789 --bid 12.5 --context-bid 9 --priority HIGH
302
343
 
344
+ # Shared bidding strategies
345
+ direct strategies get --limit 5
346
+ direct strategies add --name "Shared Clicks" --type WbMaximumClicks --params '{"SpendLimit":1000000000,"AverageCpc":30000000}' --dry-run
347
+ direct strategies update --id 42 --params '{"AverageCpc":35000000}' --dry-run
348
+ direct strategies archive --id 42 --dry-run
349
+
350
+ # Dynamic feed ad targets
351
+ direct dynamicfeedadtargets get --adgroup-ids 123 --limit 5
352
+ direct dynamicfeedadtargets add --adgroup-id 33 --name "Feed slice A" --condition "CATEGORY:EQUALS:shoes" --bid 5 --dry-run
353
+ direct dynamicfeedadtargets set-bids --id 789 --bid 6.5 --context-bid 4 --dry-run
354
+
303
355
  # Extensions, assets, feeds, and clients
304
356
  direct sitelinks add --sitelink "Docs|https://example.com/docs" --sitelink "Help|https://example.com/help|Desk" --dry-run
305
357
  direct vcards add --campaign-id 555 --country "Russia" --city "Moscow" --company-name "Acme" --work-time 1#5#9#0#18#0 --phone-country-code +7 --phone-city-code 495 --phone-number 1234567 --dry-run
@@ -315,12 +367,12 @@ direct agencyclients add-passport-organization-member --passport-organization-lo
315
367
  direct agencyclients update --client-id 42 --phone +70000000000 --email user@example.com --grant EDIT_CAMPAIGNS --grant IMPORT_XLS --dry-run
316
368
  ```
317
369
 
318
- ### Known Transport Gap
370
+ ### Known Unsupported API Operation
319
371
 
320
- `dynamicads update` is still a transport gap. Downstream wrappers may assume
321
- that operation exists, but the current installed CLI help surface does not
322
- expose `dynamicads update`. Treat it as unsupported until a real command is
323
- added.
372
+ `dynamicads update` is unsupported by API. The Yandex Direct
373
+ `dynamictextadtargets` service exposes `add`, `get`, `delete`, `suspend`,
374
+ `resume`, and `setBids`, but no `update` operation. Do not add or rely on
375
+ `direct dynamicads update` unless Yandex exposes a real API method.
324
376
 
325
377
  ### Output Formats
326
378
 
@@ -369,19 +421,22 @@ direct campaigns add --name "Test" --start-date 2024-01-01 --dry-run
369
421
 
370
422
  ### Testing
371
423
 
372
- Three tiers of tests live under `tests/`:
424
+ Four tiers of tests live under `tests/`:
373
425
 
374
426
  | Tier | Marker | Network | Token required |
375
427
  |---|---|---|---|
376
428
  | Unit / CLI wiring / dry-run | *(none)* | No | No |
377
429
  | Read-only integration | `-m integration` | Yes (production API, read-only) | Yes |
378
430
  | Write integration | `-m integration_write` | No (replays VCR cassettes) | No |
431
+ | Live draft write integration | `-m integration_live_write` | Yes when recording, otherwise VCR replay | Yes + `YANDEX_DIRECT_LIVE_WRITE=1` |
379
432
 
380
433
  ```bash
381
434
  pip install -e ".[dev]"
382
435
  pytest # fast tier — no token
383
436
  pytest -m integration -v # read-only integration tests (needs token)
384
437
  pytest -m integration_write -v # write cassette replay (no token needed)
438
+ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v # live draft cassette replay
439
+ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v --record-mode=rewrite # re-record live draft cassette
385
440
  ```
386
441
 
387
442
  ### API Coverage And Drift Monitoring
@@ -391,13 +446,16 @@ The project now distinguishes four surfaces:
391
446
  | Surface | Coverage strategy |
392
447
  |---|---|
393
448
  | Canonical WSDL-backed SOAP services | `tests/test_api_coverage.py` verifies strict service/method parity and dry-run request-schema coverage or explicit exclusions |
449
+ | Live-discovered WSDL model gaps | `scripts/build_api_coverage_report.py` reports services seen in the audited live API surface but not yet declared in the CLI coverage model |
394
450
  | Non-WSDL services (`reports`) | Explicit contract tests |
395
451
  | Historical aliases retained by exception | None currently retained |
396
452
  | Intentional CLI-only helpers | Explicitly allowlisted with reasons in `direct_cli/wsdl_coverage.py` |
397
453
 
398
454
  `100% coverage` in this project means full coverage of the supported
399
- **canonical API surface**. Alias groups and CLI-only helpers remain supported,
400
- but they are tracked outside the strict parity metric.
455
+ **declared canonical API surface**. The API coverage report also includes a
456
+ `model_gaps` section for live-discovered Yandex Direct services that are not
457
+ yet part of that declared model. Alias groups and CLI-only helpers remain
458
+ supported, but they are tracked outside the strict parity metric.
401
459
 
402
460
  Useful maintenance commands:
403
461
 
@@ -409,7 +467,8 @@ python scripts/check_wsdl_drift.py
409
467
 
410
468
  CI runs a scheduled API coverage workflow that:
411
469
  - runs the fast coverage suites;
412
- - uploads a machine-readable API coverage report artifact;
470
+ - uploads a machine-readable API coverage report artifact, including declared
471
+ parity and live-discovered model gap counts;
413
472
  - checks the cached WSDL files against the live Yandex Direct API on schedule.
414
473
 
415
474
  #### Re-recording write cassettes
@@ -440,6 +499,29 @@ The VCR config in `tests/conftest.py` already strips `Authorization`,
440
499
  `Client-Login`, cookies and any response header containing the substring
441
500
  `login`, but manual verification is mandatory before committing.
442
501
 
502
+ #### Live draft write tests
503
+
504
+ The `integration_live_write` tier is manual-only and intentionally separate
505
+ from sandbox cassette tests. In rewrite mode it runs against the production
506
+ Yandex Direct API, but it may only create disposable draft resources and
507
+ delete the exact IDs it created in the same test run. Current coverage is
508
+ limited to a guarded campaign draft create -> get -> delete check.
509
+
510
+ Replay the checked-in cassette:
511
+
512
+ ```bash
513
+ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v
514
+ ```
515
+
516
+ Re-record it only when you intentionally want to verify live draft behavior:
517
+
518
+ ```bash
519
+ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v --record-mode=rewrite
520
+ ```
521
+
522
+ Do not add tests to this tier that accept external IDs, resume/suspend/archive
523
+ existing resources, mutate bids, or touch serving campaigns.
524
+
443
525
  ### Release Process
444
526
 
445
527
  Build, validate and upload to PyPI:
@@ -501,14 +583,16 @@ YANDEX_DIRECT_LOGIN=ваш_логин_на_яндексе
501
583
  direct --token ВАШ_ТОКЕН --login ВАШ_ЛОГИН campaigns get
502
584
  ```
503
585
 
504
- Установка остаётся через `pip install direct-cli`, а запуск команд теперь идет через `direct`.
586
+ Установка остаётся через `pip install direct-cli`, а запуск команд теперь идет
587
+ через `direct`. Вызов deprecated entrypoint `direct-cli` завершается ошибкой с
588
+ подсказкой `use direct instead of direct-cli`.
505
589
 
506
590
  ### Глобальные опции
507
591
 
508
592
  | Опция | Описание |
509
593
  |-------|----------|
510
594
  | `--token` | OAuth-токен доступа к API |
511
- | `--login` | Логин рекламодателя на Яндексе |
595
+ | `--login` | Direct client login |
512
596
  | `--sandbox` | Использовать тестовое API (песочница) |
513
597
 
514
598
  ### Использование
@@ -530,17 +614,20 @@ Command naming rules:
530
614
  - в документации и примерах каноническими считаются `get`,
531
615
  `check-dictionaries` и `has-search-volume`
532
616
 
533
- `direct-cli` владеет публичным naming contract. `tapi-yandex-direct` может
534
- влиять на внутренний transport layer, но не определяет канонические CLI-имена.
617
+ Публичный naming contract задаёт исполняемый файл `direct`. Имя пакета
618
+ `direct-cli` и deprecated shim не определяют канонические CLI-имена.
619
+ `tapi-yandex-direct` может влиять на внутренний transport layer, но не
620
+ определяет канонические CLI-имена.
535
621
 
536
622
  Текущая политика — canonical-only. Исторические aliases по умолчанию не
537
623
  сохраняются в runtime CLI. Если совместимость когда-нибудь понадобится, alias
538
624
  должен быть добавлен как явное exception-правило с конкретным legacy syntax из
539
625
  `tapi-yandex-direct`, который действительно нужно поддержать.
540
626
 
541
- `direct-cli` — это транспортный слой над API Яндекс Директа. Канонические
542
- имена CLI-групп следуют нормализованным Python-именам из
543
- `tapi-yandex-direct`, а имена подкоманд — это kebab-case проекции API-методов.
627
+ `direct` — это канонический transport entrypoint над API Яндекс Директа,
628
+ устанавливаемый пакетом `direct-cli`. Канонические имена CLI-групп следуют
629
+ нормализованным Python-именам из `tapi-yandex-direct`, а имена подкоманд —
630
+ это kebab-case проекции API-методов.
544
631
 
545
632
  Базовые соответствия:
546
633
 
@@ -575,8 +662,10 @@ Naming rules:
575
662
  - multiword commands use kebab-case
576
663
  - examples: `get`, `set-bids`, `check-campaigns`, `has-search-volume`
577
664
 
578
- `direct-cli` владеет публичным naming contract. `tapi-yandex-direct` может
579
- влиять на внутренний transport layer, но не определяет канонические CLI-имена.
665
+ Публичный naming contract задаёт исполняемый файл `direct`. Имя пакета
666
+ `direct-cli` и deprecated shim не определяют канонические CLI-имена.
667
+ `tapi-yandex-direct` может влиять на внутренний transport layer, но не
668
+ определяет канонические CLI-имена.
580
669
 
581
670
  Текущая политика — canonical-only. Исторические aliases по умолчанию не
582
671
  сохраняются в runtime CLI. Если совместимость когда-нибудь понадобится, alias
@@ -724,7 +813,7 @@ direct ads delete --id 99999
724
813
  ```bash
725
814
  direct keywords get --campaign-ids 1,2,3
726
815
  direct keywords add --adgroup-id 12345 --keyword "купить ноутбук" --bid 10.50 --context-bid 5.25 --user-param-1 segment-a --user-param-2 segment-b --dry-run
727
- direct keywords update --id 88888 --bid 15.00 --context-bid 6.00 --status SUSPENDED
816
+ direct keywords update --id 88888 --keyword "updated keyword text"
728
817
  direct keywords delete --id 88888
729
818
  ```
730
819
 
@@ -746,10 +835,14 @@ direct reports list-types
746
835
  # Справочники и изменения
747
836
  direct dictionaries get --names Currencies,GeoRegions
748
837
  direct dictionaries get-geo-regions --name Москва --region-ids 225,187 --exact-names Москва,Санкт-Петербург --fields GeoRegionId,GeoRegionName
838
+
839
+ # Информация о клиенте
840
+ direct clients get --fields ClientId,Login,Currency
841
+
842
+ # Изменения
749
843
  direct changes check --campaign-ids 1,2,3 --timestamp 2026-04-14T00:00:00
750
844
  direct changes check-campaigns --timestamp 2026-04-14T00:00:00
751
845
  direct changes check-dictionaries
752
- direct clients get --fields ClientId,Login,Currency
753
846
 
754
847
  # Исследование ключевых слов и ретаргетинг
755
848
  direct keywordsresearch has-search-volume --keywords "купить ноутбук,купить компьютер"
@@ -774,6 +867,17 @@ direct smartadtargets update --id 456 --priority HIGH
774
867
  direct smartadtargets set-bids --id 456 --average-cpc 10.5 --average-cpa 15 --priority HIGH
775
868
  direct dynamicads set-bids --id 789 --bid 12.5 --context-bid 9 --priority HIGH
776
869
 
870
+ # Общие стратегии ставок
871
+ direct strategies get --limit 5
872
+ direct strategies add --name "Общая стратегия" --type WbMaximumClicks --params '{"SpendLimit":1000000000,"AverageCpc":30000000}' --dry-run
873
+ direct strategies update --id 42 --params '{"AverageCpc":35000000}' --dry-run
874
+ direct strategies archive --id 42 --dry-run
875
+
876
+ # Динамические таргеты по фиду
877
+ direct dynamicfeedadtargets get --adgroup-ids 123 --limit 5
878
+ direct dynamicfeedadtargets add --adgroup-id 33 --name "Срез фида А" --condition "CATEGORY:EQUALS:shoes" --bid 5 --dry-run
879
+ direct dynamicfeedadtargets set-bids --id 789 --bid 6.5 --context-bid 4 --dry-run
880
+
777
881
  # Расширения, ассеты, фиды и клиенты
778
882
  direct sitelinks add --sitelink "Docs|https://example.com/docs" --sitelink "Help|https://example.com/help|Desk" --dry-run
779
883
  direct vcards add --campaign-id 555 --country "Россия" --city "Москва" --company-name "Acme" --work-time 1#5#9#0#18#0 --phone-country-code +7 --phone-city-code 495 --phone-number 1234567 --dry-run
@@ -789,12 +893,13 @@ direct agencyclients add-passport-organization-member --passport-organization-lo
789
893
  direct agencyclients update --client-id 42 --phone +70000000000 --email user@example.com --grant EDIT_CAMPAIGNS --grant IMPORT_XLS --dry-run
790
894
  ```
791
895
 
792
- ### Известный Transport Gap
896
+ ### Известная неподдерживаемая API-операция
793
897
 
794
- `dynamicads update` пока остаётся transport gap. Downstream-обёртки могут
795
- ожидать эту операцию, но текущая help-поверхность установленного CLI не
796
- экспортирует `dynamicads update`. До появления реальной команды считайте её
797
- неподдерживаемой.
898
+ `dynamicads update` unsupported by API. Сервис Яндекс Директа
899
+ `dynamictextadtargets` экспортирует `add`, `get`, `delete`, `suspend`,
900
+ `resume` и `setBids`, но не экспортирует `update`. Не добавляйте и не
901
+ используйте `direct dynamicads update`, пока Яндекс не предоставит реальный
902
+ API-метод.
798
903
 
799
904
  ### Форматы вывода
800
905
 
@@ -843,19 +948,22 @@ direct campaigns add --name "Тест" --start-date 2024-01-01 --dry-run
843
948
 
844
949
  ### Тестирование
845
950
 
846
- В `tests/` три уровня тестов:
951
+ В `tests/` четыре уровня тестов:
847
952
 
848
953
  | Уровень | Маркер | Сеть | Нужен токен |
849
954
  |---|---|---|---|
850
955
  | Юнит / CLI / dry-run | *(без маркера)* | Нет | Нет |
851
956
  | Read-only интеграция | `-m integration` | Да (prod API, только чтение) | Да |
852
957
  | Write интеграция | `-m integration_write` | Нет (replay VCR-кассет) | Нет |
958
+ | Live draft write интеграция | `-m integration_live_write` | Да при записи, иначе VCR replay | Да + `YANDEX_DIRECT_LIVE_WRITE=1` |
853
959
 
854
960
  ```bash
855
961
  pip install -e ".[dev]"
856
962
  pytest # быстрый уровень — без токена
857
963
  pytest -m integration -v # read-only интеграция (нужен токен)
858
964
  pytest -m integration_write -v # replay write-кассет (токен не нужен)
965
+ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v # replay live draft-кассеты
966
+ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v --record-mode=rewrite # перезапись live draft-кассеты
859
967
  ```
860
968
 
861
969
  #### Перезапись write-кассет
@@ -886,6 +994,30 @@ VCR-конфиг в `tests/conftest.py` уже стрипает `Authorization`,
886
994
  куки и любые response-заголовки с подстрокой `login`, но ручная проверка
887
995
  перед коммитом обязательна.
888
996
 
997
+ #### Live write только на черновиках
998
+
999
+ Уровень `integration_live_write` запускается только вручную и отделен от
1000
+ sandbox/VCR-тестов. В rewrite-режиме он ходит в production API Яндекс Директа,
1001
+ но может только создавать одноразовые черновики и удалять ровно те ID, которые
1002
+ были созданы в этом же тестовом прогоне. Текущее покрытие: guarded create ->
1003
+ get -> delete для draft-кампании.
1004
+
1005
+ Replay закоммиченной кассеты:
1006
+
1007
+ ```bash
1008
+ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v
1009
+ ```
1010
+
1011
+ Перезапись после явного решения проверить live draft-поведение:
1012
+
1013
+ ```bash
1014
+ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v --record-mode=rewrite
1015
+ ```
1016
+
1017
+ В этот уровень нельзя добавлять тесты, которые принимают внешние ID,
1018
+ возобновляют/останавливают/архивируют существующие ресурсы, меняют ставки или
1019
+ трогают кампании, которые могут показываться.
1020
+
889
1021
  ### Публикация на PyPI
890
1022
 
891
1023
  Сборка, проверка и загрузка на PyPI: