direct-cli 0.2.8__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 (167) hide show
  1. {direct_cli-0.2.8 → direct_cli-0.2.9}/.github/workflows/api-coverage.yml +12 -0
  2. {direct_cli-0.2.8 → direct_cli-0.2.9}/PKG-INFO +133 -21
  3. {direct_cli-0.2.8 → direct_cli-0.2.9}/README.md +129 -19
  4. direct_cli-0.2.9/direct_cli/_vendor/__init__.py +0 -0
  5. direct_cli-0.2.9/direct_cli/_vendor/tapi_yandex_direct/__init__.py +8 -0
  6. direct_cli-0.2.9/direct_cli/_vendor/tapi_yandex_direct/exceptions.py +78 -0
  7. direct_cli-0.2.9/direct_cli/_vendor/tapi_yandex_direct/resource_mapping.py +127 -0
  8. direct_cli-0.2.9/direct_cli/_vendor/tapi_yandex_direct/tapi_yandex_direct.py +387 -0
  9. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/api.py +8 -2
  10. direct_cli-0.2.9/direct_cli/auth.py +450 -0
  11. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/cli.py +28 -4
  12. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/advideos.py +14 -10
  13. direct_cli-0.2.9/direct_cli/commands/auth.py +125 -0
  14. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/creatives.py +2 -5
  15. direct_cli-0.2.9/direct_cli/commands/dynamicfeedadtargets.py +280 -0
  16. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/keywords.py +35 -11
  17. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/keywordsresearch.py +2 -2
  18. direct_cli-0.2.9/direct_cli/commands/strategies.py +307 -0
  19. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/wsdl_coverage.py +22 -1
  20. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli.egg-info/PKG-INFO +133 -21
  21. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli.egg-info/SOURCES.txt +39 -0
  22. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli.egg-info/requires.txt +3 -1
  23. direct_cli-0.2.9/docs/superpowers/specs/2026-04-23-vendor-tapi-yandex-direct-design.md +85 -0
  24. {direct_cli-0.2.8 → direct_cli-0.2.9}/pyproject.toml +9 -3
  25. direct_cli-0.2.9/scripts/anonymize_cassettes.py +155 -0
  26. direct_cli-0.2.9/scripts/build_api_coverage_report.py +236 -0
  27. {direct_cli-0.2.8 → direct_cli-0.2.9}/scripts/release_pypi.sh +3 -0
  28. direct_cli-0.2.9/scripts/update_vendor.sh +50 -0
  29. direct_cli-0.2.9/tests/API_COVERAGE.md +92 -0
  30. direct_cli-0.2.9/tests/API_ISSUE_AUDIT.md +60 -0
  31. direct_cli-0.2.9/tests/MANUAL_COVERAGE.md +75 -0
  32. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_adgroups_add_update_delete.yaml +374 -0
  33. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_adimages_add_get_delete.yaml +68 -0
  34. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_ads_add_update_delete.yaml +502 -0
  35. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_ads_suspend_resume_archive_unarchive.yaml +628 -0
  36. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_advideos_add_get.yaml +126 -0
  37. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_audiencetargets_add_delete.yaml +313 -0
  38. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_audiencetargets_suspend_resume.yaml +313 -0
  39. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_bids_set.yaml +437 -0
  40. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_campaign_create_get_delete.yaml +250 -0
  41. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_creatives_chain_advideo_to_creative.yaml +189 -0
  42. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_dynamicads_add_delete.yaml +67 -0
  43. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_dynamicads_suspend_resume.yaml +67 -0
  44. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_keywordbids_set.yaml +439 -0
  45. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_keywords_add_update_delete.yaml +439 -0
  46. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_keywords_suspend_resume.yaml +499 -0
  47. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_sitelinks_add_get_delete.yaml +191 -0
  48. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_smartadtargets_add_update_delete.yaml +191 -0
  49. direct_cli-0.2.9/tests/cassettes/test_integration_live_write/test_live_draft_smartadtargets_suspend_resume.yaml +191 -0
  50. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteAdExtensions.test_add_delete.yaml +11 -11
  51. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteAdGroups.test_add_update_delete.yaml +27 -27
  52. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteAdImages.test_add_delete.yaml +6 -6
  53. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteAds.test_add_text_ad_update_delete.yaml +27 -27
  54. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteAudienceTargets.test_add_delete.yaml +38 -38
  55. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteBidModifiersAdd.test_add_delete_mobile.yaml +22 -22
  56. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteBidModifiersSet.test_set_without_id_is_rejected.yaml +18 -18
  57. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteBids.test_set_bid.yaml +27 -27
  58. direct_cli-0.2.9/tests/cassettes/test_integration_write/TestWriteCampaignDraftLifecycle.test_draft_create_get_delete.yaml +218 -0
  59. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteCampaigns.test_campaign_lifecycle.yaml +33 -33
  60. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteDynamicAds.test_add_update_delete.yaml +4 -4
  61. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteFeeds.test_add_update_delete.yaml +16 -16
  62. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteKeywordBids.test_set_keyword_bid.yaml +27 -27
  63. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteKeywords.test_add_update_delete.yaml +27 -27
  64. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteNegativeKeywordSharedSets.test_add_update_delete.yaml +17 -17
  65. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteRetargeting.test_add_delete.yaml +10 -10
  66. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteSitelinks.test_add_delete.yaml +5 -5
  67. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteSmartAdTargets.test_add_update_delete.yaml +15 -15
  68. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/cassettes/test_integration_write/TestWriteVCards.test_add_delete.yaml +23 -23
  69. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/conftest.py +26 -1
  70. direct_cli-0.2.9/tests/fixtures/test-video.mp4 +0 -0
  71. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/test_api_coverage.py +183 -43
  72. direct_cli-0.2.9/tests/test_auth_oauth.py +206 -0
  73. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/test_cli.py +20 -3
  74. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/test_comprehensive.py +3 -0
  75. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/test_dry_run.py +124 -3
  76. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/test_integration.py +150 -30
  77. direct_cli-0.2.9/tests/test_integration_live_write.py +1252 -0
  78. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/test_integration_write.py +369 -130
  79. direct_cli-0.2.9/tests/test_transport_contract.py +23 -0
  80. direct_cli-0.2.9/tests/wsdl_cache/dynamicfeedadtargets.xml +328 -0
  81. direct_cli-0.2.9/tests/wsdl_cache/strategies.xml +1100 -0
  82. direct_cli-0.2.8/direct_cli/auth.py +0 -167
  83. direct_cli-0.2.8/scripts/build_api_coverage_report.py +0 -108
  84. {direct_cli-0.2.8 → direct_cli-0.2.9}/.env.example +0 -0
  85. {direct_cli-0.2.8 → direct_cli-0.2.9}/.github/copilot-instructions.md +0 -0
  86. {direct_cli-0.2.8 → direct_cli-0.2.9}/.github/workflows/claude-code-review.yml +0 -0
  87. {direct_cli-0.2.8 → direct_cli-0.2.9}/.github/workflows/claude.yml +0 -0
  88. {direct_cli-0.2.8 → direct_cli-0.2.9}/.gitignore +0 -0
  89. {direct_cli-0.2.8 → direct_cli-0.2.9}/AGENTS.md +0 -0
  90. {direct_cli-0.2.8 → direct_cli-0.2.9}/CLAUDE.md +0 -0
  91. {direct_cli-0.2.8 → direct_cli-0.2.9}/MANIFEST.in +0 -0
  92. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/__init__.py +0 -0
  93. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/_deprecated.py +0 -0
  94. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/__init__.py +0 -0
  95. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/adextensions.py +0 -0
  96. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/adgroups.py +0 -0
  97. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/adimages.py +0 -0
  98. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/ads.py +0 -0
  99. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/agencyclients.py +0 -0
  100. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/audiencetargets.py +0 -0
  101. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/bidmodifiers.py +0 -0
  102. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/bids.py +0 -0
  103. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/businesses.py +0 -0
  104. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/campaigns.py +0 -0
  105. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/changes.py +0 -0
  106. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/clients.py +0 -0
  107. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/dictionaries.py +0 -0
  108. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/dynamicads.py +0 -0
  109. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/feeds.py +0 -0
  110. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/keywordbids.py +0 -0
  111. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/leads.py +0 -0
  112. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/negativekeywordsharedsets.py +0 -0
  113. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/reports.py +0 -0
  114. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/retargeting.py +0 -0
  115. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/sitelinks.py +0 -0
  116. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/smartadtargets.py +0 -0
  117. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/turbopages.py +0 -0
  118. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/commands/vcards.py +0 -0
  119. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/output.py +0 -0
  120. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/reports_coverage.py +0 -0
  121. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli/utils.py +0 -0
  122. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli.egg-info/dependency_links.txt +0 -0
  123. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli.egg-info/entry_points.txt +0 -0
  124. {direct_cli-0.2.8 → direct_cli-0.2.9}/direct_cli.egg-info/top_level.txt +0 -0
  125. {direct_cli-0.2.8 → direct_cli-0.2.9}/docs/superpowers/plans/2026-04-12-issue-32-completion.md +0 -0
  126. {direct_cli-0.2.8 → direct_cli-0.2.9}/scripts/check_reports_drift.py +0 -0
  127. {direct_cli-0.2.8 → direct_cli-0.2.9}/scripts/check_wsdl_drift.py +0 -0
  128. {direct_cli-0.2.8 → direct_cli-0.2.9}/scripts/refresh_reports_cache.py +0 -0
  129. {direct_cli-0.2.8 → direct_cli-0.2.9}/scripts/refresh_wsdl_cache.py +0 -0
  130. {direct_cli-0.2.8 → direct_cli-0.2.9}/setup.cfg +0 -0
  131. {direct_cli-0.2.8 → direct_cli-0.2.9}/setup.py +0 -0
  132. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/__init__.py +0 -0
  133. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/reports_cache/raw/fields-list.html +0 -0
  134. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/reports_cache/raw/headers.html +0 -0
  135. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/reports_cache/raw/spec.html +0 -0
  136. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/reports_cache/raw/type.html +0 -0
  137. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/reports_cache/spec.json +0 -0
  138. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/test_auth_bw.py +0 -0
  139. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/test_auth_op.py +0 -0
  140. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/test_reports_drift.py +0 -0
  141. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/adextensions.xml +0 -0
  142. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/adgroups.xml +0 -0
  143. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/adimages.xml +0 -0
  144. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/ads.xml +0 -0
  145. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/advideos.xml +0 -0
  146. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/agencyclients.xml +0 -0
  147. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/audiencetargets.xml +0 -0
  148. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/bidmodifiers.xml +0 -0
  149. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/bids.xml +0 -0
  150. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/businesses.xml +0 -0
  151. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/campaigns.xml +0 -0
  152. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/changes.xml +0 -0
  153. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/clients.xml +0 -0
  154. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/creatives.xml +0 -0
  155. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/dictionaries.xml +0 -0
  156. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/dynamictextadtargets.xml +0 -0
  157. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/feeds.xml +0 -0
  158. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/keywordbids.xml +0 -0
  159. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/keywords.xml +0 -0
  160. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/keywordsresearch.xml +0 -0
  161. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/leads.xml +0 -0
  162. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/negativekeywordsharedsets.xml +0 -0
  163. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/retargetinglists.xml +0 -0
  164. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/sitelinks.xml +0 -0
  165. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/smartadtargets.xml +0 -0
  166. {direct_cli-0.2.8 → direct_cli-0.2.9}/tests/wsdl_cache/turbopages.xml +0 -0
  167. {direct_cli-0.2.8 → 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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: direct-cli
3
- Version: 0.2.8
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,6 +68,35 @@ 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`.
70
101
  Invoking the deprecated `direct-cli` entrypoint exits with
71
102
  `use direct instead of direct-cli`.
@@ -75,7 +106,8 @@ Invoking the deprecated `direct-cli` entrypoint exits with
75
106
  | Option | Description |
76
107
  |--------|-------------|
77
108
  | `--token` | API access token |
78
- | `--login` | Yandex advertiser login |
109
+ | `--login` | Direct client login |
110
+ | `--profile` | Credential profile name |
79
111
  | `--sandbox` | Use sandbox API |
80
112
 
81
113
  ### CLI Convention
@@ -255,7 +287,7 @@ direct ads delete --id 99999
255
287
  ```bash
256
288
  direct keywords get --campaign-ids 1,2,3
257
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
258
- direct keywords update --id 88888 --bid 15.00 --context-bid 6.00 --status SUSPENDED
290
+ direct keywords update --id 88888 --keyword "updated keyword text"
259
291
  direct keywords delete --id 88888
260
292
  ```
261
293
 
@@ -309,6 +341,17 @@ direct smartadtargets update --id 456 --priority HIGH
309
341
  direct smartadtargets set-bids --id 456 --average-cpc 10.5 --average-cpa 15 --priority HIGH
310
342
  direct dynamicads set-bids --id 789 --bid 12.5 --context-bid 9 --priority HIGH
311
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
+
312
355
  # Extensions, assets, feeds, and clients
313
356
  direct sitelinks add --sitelink "Docs|https://example.com/docs" --sitelink "Help|https://example.com/help|Desk" --dry-run
314
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
@@ -324,12 +367,12 @@ direct agencyclients add-passport-organization-member --passport-organization-lo
324
367
  direct agencyclients update --client-id 42 --phone +70000000000 --email user@example.com --grant EDIT_CAMPAIGNS --grant IMPORT_XLS --dry-run
325
368
  ```
326
369
 
327
- ### Known Transport Gap
370
+ ### Known Unsupported API Operation
328
371
 
329
- `dynamicads update` is still a transport gap. Downstream wrappers may assume
330
- that operation exists, but the current installed CLI help surface does not
331
- expose `dynamicads update`. Treat it as unsupported until a real command is
332
- 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.
333
376
 
334
377
  ### Output Formats
335
378
 
@@ -378,19 +421,22 @@ direct campaigns add --name "Test" --start-date 2024-01-01 --dry-run
378
421
 
379
422
  ### Testing
380
423
 
381
- Three tiers of tests live under `tests/`:
424
+ Four tiers of tests live under `tests/`:
382
425
 
383
426
  | Tier | Marker | Network | Token required |
384
427
  |---|---|---|---|
385
428
  | Unit / CLI wiring / dry-run | *(none)* | No | No |
386
429
  | Read-only integration | `-m integration` | Yes (production API, read-only) | Yes |
387
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` |
388
432
 
389
433
  ```bash
390
434
  pip install -e ".[dev]"
391
435
  pytest # fast tier — no token
392
436
  pytest -m integration -v # read-only integration tests (needs token)
393
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
394
440
  ```
395
441
 
396
442
  ### API Coverage And Drift Monitoring
@@ -400,13 +446,16 @@ The project now distinguishes four surfaces:
400
446
  | Surface | Coverage strategy |
401
447
  |---|---|
402
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 |
403
450
  | Non-WSDL services (`reports`) | Explicit contract tests |
404
451
  | Historical aliases retained by exception | None currently retained |
405
452
  | Intentional CLI-only helpers | Explicitly allowlisted with reasons in `direct_cli/wsdl_coverage.py` |
406
453
 
407
454
  `100% coverage` in this project means full coverage of the supported
408
- **canonical API surface**. Alias groups and CLI-only helpers remain supported,
409
- 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.
410
459
 
411
460
  Useful maintenance commands:
412
461
 
@@ -418,7 +467,8 @@ python scripts/check_wsdl_drift.py
418
467
 
419
468
  CI runs a scheduled API coverage workflow that:
420
469
  - runs the fast coverage suites;
421
- - 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;
422
472
  - checks the cached WSDL files against the live Yandex Direct API on schedule.
423
473
 
424
474
  #### Re-recording write cassettes
@@ -449,6 +499,29 @@ The VCR config in `tests/conftest.py` already strips `Authorization`,
449
499
  `Client-Login`, cookies and any response header containing the substring
450
500
  `login`, but manual verification is mandatory before committing.
451
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
+
452
525
  ### Release Process
453
526
 
454
527
  Build, validate and upload to PyPI:
@@ -519,7 +592,7 @@ direct --token ВАШ_ТОКЕН --login ВАШ_ЛОГИН campaigns get
519
592
  | Опция | Описание |
520
593
  |-------|----------|
521
594
  | `--token` | OAuth-токен доступа к API |
522
- | `--login` | Логин рекламодателя на Яндексе |
595
+ | `--login` | Direct client login |
523
596
  | `--sandbox` | Использовать тестовое API (песочница) |
524
597
 
525
598
  ### Использование
@@ -740,7 +813,7 @@ direct ads delete --id 99999
740
813
  ```bash
741
814
  direct keywords get --campaign-ids 1,2,3
742
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
743
- direct keywords update --id 88888 --bid 15.00 --context-bid 6.00 --status SUSPENDED
816
+ direct keywords update --id 88888 --keyword "updated keyword text"
744
817
  direct keywords delete --id 88888
745
818
  ```
746
819
 
@@ -794,6 +867,17 @@ direct smartadtargets update --id 456 --priority HIGH
794
867
  direct smartadtargets set-bids --id 456 --average-cpc 10.5 --average-cpa 15 --priority HIGH
795
868
  direct dynamicads set-bids --id 789 --bid 12.5 --context-bid 9 --priority HIGH
796
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
+
797
881
  # Расширения, ассеты, фиды и клиенты
798
882
  direct sitelinks add --sitelink "Docs|https://example.com/docs" --sitelink "Help|https://example.com/help|Desk" --dry-run
799
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
@@ -809,12 +893,13 @@ direct agencyclients add-passport-organization-member --passport-organization-lo
809
893
  direct agencyclients update --client-id 42 --phone +70000000000 --email user@example.com --grant EDIT_CAMPAIGNS --grant IMPORT_XLS --dry-run
810
894
  ```
811
895
 
812
- ### Известный Transport Gap
896
+ ### Известная неподдерживаемая API-операция
813
897
 
814
- `dynamicads update` пока остаётся transport gap. Downstream-обёртки могут
815
- ожидать эту операцию, но текущая help-поверхность установленного CLI не
816
- экспортирует `dynamicads update`. До появления реальной команды считайте её
817
- неподдерживаемой.
898
+ `dynamicads update` unsupported by API. Сервис Яндекс Директа
899
+ `dynamictextadtargets` экспортирует `add`, `get`, `delete`, `suspend`,
900
+ `resume` и `setBids`, но не экспортирует `update`. Не добавляйте и не
901
+ используйте `direct dynamicads update`, пока Яндекс не предоставит реальный
902
+ API-метод.
818
903
 
819
904
  ### Форматы вывода
820
905
 
@@ -863,19 +948,22 @@ direct campaigns add --name "Тест" --start-date 2024-01-01 --dry-run
863
948
 
864
949
  ### Тестирование
865
950
 
866
- В `tests/` три уровня тестов:
951
+ В `tests/` четыре уровня тестов:
867
952
 
868
953
  | Уровень | Маркер | Сеть | Нужен токен |
869
954
  |---|---|---|---|
870
955
  | Юнит / CLI / dry-run | *(без маркера)* | Нет | Нет |
871
956
  | Read-only интеграция | `-m integration` | Да (prod API, только чтение) | Да |
872
957
  | Write интеграция | `-m integration_write` | Нет (replay VCR-кассет) | Нет |
958
+ | Live draft write интеграция | `-m integration_live_write` | Да при записи, иначе VCR replay | Да + `YANDEX_DIRECT_LIVE_WRITE=1` |
873
959
 
874
960
  ```bash
875
961
  pip install -e ".[dev]"
876
962
  pytest # быстрый уровень — без токена
877
963
  pytest -m integration -v # read-only интеграция (нужен токен)
878
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-кассеты
879
967
  ```
880
968
 
881
969
  #### Перезапись write-кассет
@@ -906,6 +994,30 @@ VCR-конфиг в `tests/conftest.py` уже стрипает `Authorization`,
906
994
  куки и любые response-заголовки с подстрокой `login`, но ручная проверка
907
995
  перед коммитом обязательна.
908
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
+
909
1021
  ### Публикация на PyPI
910
1022
 
911
1023
  Сборка, проверка и загрузка на PyPI:
@@ -29,6 +29,35 @@ Or pass credentials directly per command:
29
29
  direct --token YOUR_TOKEN --login YOUR_LOGIN campaigns get
30
30
  ```
31
31
 
32
+ Use profile-specific credentials from `.env`:
33
+
34
+ ```env
35
+ YANDEX_DIRECT_TOKEN_AGENCY1=token-1
36
+ YANDEX_DIRECT_LOGIN_AGENCY1=client-login-1
37
+ YANDEX_DIRECT_TOKEN_AGENCY2=token-2
38
+ YANDEX_DIRECT_LOGIN_AGENCY2=client-login-2
39
+ ```
40
+
41
+ OAuth and profile commands:
42
+
43
+ ```bash
44
+ direct auth login
45
+ direct auth login --profile agency1
46
+ direct auth login --code abc123 --profile agency1
47
+ direct auth login --oauth-token y0_example --profile agency1
48
+ direct auth list
49
+ direct auth use --profile agency1
50
+ direct auth status --profile agency1
51
+ direct --profile agency1 campaigns get
52
+ ```
53
+
54
+ Notes:
55
+ - Legacy profile environment variable is not used.
56
+ - Select credentials with `--profile`.
57
+ - `--login` remains Direct client login.
58
+ - Authorization is performed via `direct auth login`.
59
+ - Alias `auth_login` is not supported.
60
+
32
61
  Install with `pip install direct-cli`, then run commands with `direct`.
33
62
  Invoking the deprecated `direct-cli` entrypoint exits with
34
63
  `use direct instead of direct-cli`.
@@ -38,7 +67,8 @@ Invoking the deprecated `direct-cli` entrypoint exits with
38
67
  | Option | Description |
39
68
  |--------|-------------|
40
69
  | `--token` | API access token |
41
- | `--login` | Yandex advertiser login |
70
+ | `--login` | Direct client login |
71
+ | `--profile` | Credential profile name |
42
72
  | `--sandbox` | Use sandbox API |
43
73
 
44
74
  ### CLI Convention
@@ -218,7 +248,7 @@ direct ads delete --id 99999
218
248
  ```bash
219
249
  direct keywords get --campaign-ids 1,2,3
220
250
  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
221
- direct keywords update --id 88888 --bid 15.00 --context-bid 6.00 --status SUSPENDED
251
+ direct keywords update --id 88888 --keyword "updated keyword text"
222
252
  direct keywords delete --id 88888
223
253
  ```
224
254
 
@@ -272,6 +302,17 @@ direct smartadtargets update --id 456 --priority HIGH
272
302
  direct smartadtargets set-bids --id 456 --average-cpc 10.5 --average-cpa 15 --priority HIGH
273
303
  direct dynamicads set-bids --id 789 --bid 12.5 --context-bid 9 --priority HIGH
274
304
 
305
+ # Shared bidding strategies
306
+ direct strategies get --limit 5
307
+ direct strategies add --name "Shared Clicks" --type WbMaximumClicks --params '{"SpendLimit":1000000000,"AverageCpc":30000000}' --dry-run
308
+ direct strategies update --id 42 --params '{"AverageCpc":35000000}' --dry-run
309
+ direct strategies archive --id 42 --dry-run
310
+
311
+ # Dynamic feed ad targets
312
+ direct dynamicfeedadtargets get --adgroup-ids 123 --limit 5
313
+ direct dynamicfeedadtargets add --adgroup-id 33 --name "Feed slice A" --condition "CATEGORY:EQUALS:shoes" --bid 5 --dry-run
314
+ direct dynamicfeedadtargets set-bids --id 789 --bid 6.5 --context-bid 4 --dry-run
315
+
275
316
  # Extensions, assets, feeds, and clients
276
317
  direct sitelinks add --sitelink "Docs|https://example.com/docs" --sitelink "Help|https://example.com/help|Desk" --dry-run
277
318
  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
@@ -287,12 +328,12 @@ direct agencyclients add-passport-organization-member --passport-organization-lo
287
328
  direct agencyclients update --client-id 42 --phone +70000000000 --email user@example.com --grant EDIT_CAMPAIGNS --grant IMPORT_XLS --dry-run
288
329
  ```
289
330
 
290
- ### Known Transport Gap
331
+ ### Known Unsupported API Operation
291
332
 
292
- `dynamicads update` is still a transport gap. Downstream wrappers may assume
293
- that operation exists, but the current installed CLI help surface does not
294
- expose `dynamicads update`. Treat it as unsupported until a real command is
295
- added.
333
+ `dynamicads update` is unsupported by API. The Yandex Direct
334
+ `dynamictextadtargets` service exposes `add`, `get`, `delete`, `suspend`,
335
+ `resume`, and `setBids`, but no `update` operation. Do not add or rely on
336
+ `direct dynamicads update` unless Yandex exposes a real API method.
296
337
 
297
338
  ### Output Formats
298
339
 
@@ -341,19 +382,22 @@ direct campaigns add --name "Test" --start-date 2024-01-01 --dry-run
341
382
 
342
383
  ### Testing
343
384
 
344
- Three tiers of tests live under `tests/`:
385
+ Four tiers of tests live under `tests/`:
345
386
 
346
387
  | Tier | Marker | Network | Token required |
347
388
  |---|---|---|---|
348
389
  | Unit / CLI wiring / dry-run | *(none)* | No | No |
349
390
  | Read-only integration | `-m integration` | Yes (production API, read-only) | Yes |
350
391
  | Write integration | `-m integration_write` | No (replays VCR cassettes) | No |
392
+ | Live draft write integration | `-m integration_live_write` | Yes when recording, otherwise VCR replay | Yes + `YANDEX_DIRECT_LIVE_WRITE=1` |
351
393
 
352
394
  ```bash
353
395
  pip install -e ".[dev]"
354
396
  pytest # fast tier — no token
355
397
  pytest -m integration -v # read-only integration tests (needs token)
356
398
  pytest -m integration_write -v # write cassette replay (no token needed)
399
+ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v # live draft cassette replay
400
+ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v --record-mode=rewrite # re-record live draft cassette
357
401
  ```
358
402
 
359
403
  ### API Coverage And Drift Monitoring
@@ -363,13 +407,16 @@ The project now distinguishes four surfaces:
363
407
  | Surface | Coverage strategy |
364
408
  |---|---|
365
409
  | Canonical WSDL-backed SOAP services | `tests/test_api_coverage.py` verifies strict service/method parity and dry-run request-schema coverage or explicit exclusions |
410
+ | 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 |
366
411
  | Non-WSDL services (`reports`) | Explicit contract tests |
367
412
  | Historical aliases retained by exception | None currently retained |
368
413
  | Intentional CLI-only helpers | Explicitly allowlisted with reasons in `direct_cli/wsdl_coverage.py` |
369
414
 
370
415
  `100% coverage` in this project means full coverage of the supported
371
- **canonical API surface**. Alias groups and CLI-only helpers remain supported,
372
- but they are tracked outside the strict parity metric.
416
+ **declared canonical API surface**. The API coverage report also includes a
417
+ `model_gaps` section for live-discovered Yandex Direct services that are not
418
+ yet part of that declared model. Alias groups and CLI-only helpers remain
419
+ supported, but they are tracked outside the strict parity metric.
373
420
 
374
421
  Useful maintenance commands:
375
422
 
@@ -381,7 +428,8 @@ python scripts/check_wsdl_drift.py
381
428
 
382
429
  CI runs a scheduled API coverage workflow that:
383
430
  - runs the fast coverage suites;
384
- - uploads a machine-readable API coverage report artifact;
431
+ - uploads a machine-readable API coverage report artifact, including declared
432
+ parity and live-discovered model gap counts;
385
433
  - checks the cached WSDL files against the live Yandex Direct API on schedule.
386
434
 
387
435
  #### Re-recording write cassettes
@@ -412,6 +460,29 @@ The VCR config in `tests/conftest.py` already strips `Authorization`,
412
460
  `Client-Login`, cookies and any response header containing the substring
413
461
  `login`, but manual verification is mandatory before committing.
414
462
 
463
+ #### Live draft write tests
464
+
465
+ The `integration_live_write` tier is manual-only and intentionally separate
466
+ from sandbox cassette tests. In rewrite mode it runs against the production
467
+ Yandex Direct API, but it may only create disposable draft resources and
468
+ delete the exact IDs it created in the same test run. Current coverage is
469
+ limited to a guarded campaign draft create -> get -> delete check.
470
+
471
+ Replay the checked-in cassette:
472
+
473
+ ```bash
474
+ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v
475
+ ```
476
+
477
+ Re-record it only when you intentionally want to verify live draft behavior:
478
+
479
+ ```bash
480
+ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v --record-mode=rewrite
481
+ ```
482
+
483
+ Do not add tests to this tier that accept external IDs, resume/suspend/archive
484
+ existing resources, mutate bids, or touch serving campaigns.
485
+
415
486
  ### Release Process
416
487
 
417
488
  Build, validate and upload to PyPI:
@@ -482,7 +553,7 @@ direct --token ВАШ_ТОКЕН --login ВАШ_ЛОГИН campaigns get
482
553
  | Опция | Описание |
483
554
  |-------|----------|
484
555
  | `--token` | OAuth-токен доступа к API |
485
- | `--login` | Логин рекламодателя на Яндексе |
556
+ | `--login` | Direct client login |
486
557
  | `--sandbox` | Использовать тестовое API (песочница) |
487
558
 
488
559
  ### Использование
@@ -703,7 +774,7 @@ direct ads delete --id 99999
703
774
  ```bash
704
775
  direct keywords get --campaign-ids 1,2,3
705
776
  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
706
- direct keywords update --id 88888 --bid 15.00 --context-bid 6.00 --status SUSPENDED
777
+ direct keywords update --id 88888 --keyword "updated keyword text"
707
778
  direct keywords delete --id 88888
708
779
  ```
709
780
 
@@ -757,6 +828,17 @@ direct smartadtargets update --id 456 --priority HIGH
757
828
  direct smartadtargets set-bids --id 456 --average-cpc 10.5 --average-cpa 15 --priority HIGH
758
829
  direct dynamicads set-bids --id 789 --bid 12.5 --context-bid 9 --priority HIGH
759
830
 
831
+ # Общие стратегии ставок
832
+ direct strategies get --limit 5
833
+ direct strategies add --name "Общая стратегия" --type WbMaximumClicks --params '{"SpendLimit":1000000000,"AverageCpc":30000000}' --dry-run
834
+ direct strategies update --id 42 --params '{"AverageCpc":35000000}' --dry-run
835
+ direct strategies archive --id 42 --dry-run
836
+
837
+ # Динамические таргеты по фиду
838
+ direct dynamicfeedadtargets get --adgroup-ids 123 --limit 5
839
+ direct dynamicfeedadtargets add --adgroup-id 33 --name "Срез фида А" --condition "CATEGORY:EQUALS:shoes" --bid 5 --dry-run
840
+ direct dynamicfeedadtargets set-bids --id 789 --bid 6.5 --context-bid 4 --dry-run
841
+
760
842
  # Расширения, ассеты, фиды и клиенты
761
843
  direct sitelinks add --sitelink "Docs|https://example.com/docs" --sitelink "Help|https://example.com/help|Desk" --dry-run
762
844
  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
@@ -772,12 +854,13 @@ direct agencyclients add-passport-organization-member --passport-organization-lo
772
854
  direct agencyclients update --client-id 42 --phone +70000000000 --email user@example.com --grant EDIT_CAMPAIGNS --grant IMPORT_XLS --dry-run
773
855
  ```
774
856
 
775
- ### Известный Transport Gap
857
+ ### Известная неподдерживаемая API-операция
776
858
 
777
- `dynamicads update` пока остаётся transport gap. Downstream-обёртки могут
778
- ожидать эту операцию, но текущая help-поверхность установленного CLI не
779
- экспортирует `dynamicads update`. До появления реальной команды считайте её
780
- неподдерживаемой.
859
+ `dynamicads update` unsupported by API. Сервис Яндекс Директа
860
+ `dynamictextadtargets` экспортирует `add`, `get`, `delete`, `suspend`,
861
+ `resume` и `setBids`, но не экспортирует `update`. Не добавляйте и не
862
+ используйте `direct dynamicads update`, пока Яндекс не предоставит реальный
863
+ API-метод.
781
864
 
782
865
  ### Форматы вывода
783
866
 
@@ -826,19 +909,22 @@ direct campaigns add --name "Тест" --start-date 2024-01-01 --dry-run
826
909
 
827
910
  ### Тестирование
828
911
 
829
- В `tests/` три уровня тестов:
912
+ В `tests/` четыре уровня тестов:
830
913
 
831
914
  | Уровень | Маркер | Сеть | Нужен токен |
832
915
  |---|---|---|---|
833
916
  | Юнит / CLI / dry-run | *(без маркера)* | Нет | Нет |
834
917
  | Read-only интеграция | `-m integration` | Да (prod API, только чтение) | Да |
835
918
  | Write интеграция | `-m integration_write` | Нет (replay VCR-кассет) | Нет |
919
+ | Live draft write интеграция | `-m integration_live_write` | Да при записи, иначе VCR replay | Да + `YANDEX_DIRECT_LIVE_WRITE=1` |
836
920
 
837
921
  ```bash
838
922
  pip install -e ".[dev]"
839
923
  pytest # быстрый уровень — без токена
840
924
  pytest -m integration -v # read-only интеграция (нужен токен)
841
925
  pytest -m integration_write -v # replay write-кассет (токен не нужен)
926
+ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v # replay live draft-кассеты
927
+ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v --record-mode=rewrite # перезапись live draft-кассеты
842
928
  ```
843
929
 
844
930
  #### Перезапись write-кассет
@@ -869,6 +955,30 @@ VCR-конфиг в `tests/conftest.py` уже стрипает `Authorization`,
869
955
  куки и любые response-заголовки с подстрокой `login`, но ручная проверка
870
956
  перед коммитом обязательна.
871
957
 
958
+ #### Live write только на черновиках
959
+
960
+ Уровень `integration_live_write` запускается только вручную и отделен от
961
+ sandbox/VCR-тестов. В rewrite-режиме он ходит в production API Яндекс Директа,
962
+ но может только создавать одноразовые черновики и удалять ровно те ID, которые
963
+ были созданы в этом же тестовом прогоне. Текущее покрытие: guarded create ->
964
+ get -> delete для draft-кампании.
965
+
966
+ Replay закоммиченной кассеты:
967
+
968
+ ```bash
969
+ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v
970
+ ```
971
+
972
+ Перезапись после явного решения проверить live draft-поведение:
973
+
974
+ ```bash
975
+ YANDEX_DIRECT_LIVE_WRITE=1 pytest -m integration_live_write -v --record-mode=rewrite
976
+ ```
977
+
978
+ В этот уровень нельзя добавлять тесты, которые принимают внешние ID,
979
+ возобновляют/останавливают/архивируют существующие ресурсы, меняют ставки или
980
+ трогают кампании, которые могут показываться.
981
+
872
982
  ### Публикация на PyPI
873
983
 
874
984
  Сборка, проверка и загрузка на PyPI:
File without changes
@@ -0,0 +1,8 @@
1
+
2
+ __author__ = 'Pavel Maksimov'
3
+ __email__ = 'vur21@ya.ru'
4
+ __version__ = '2026.4.22'
5
+
6
+
7
+ from .resource_mapping import *
8
+ from .tapi_yandex_direct import YandexDirect