ynab-api-cli 0.1.0__tar.gz → 0.1.1__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 (260) hide show
  1. ynab_api_cli-0.1.1/.github/workflows/live.yml +35 -0
  2. ynab_api_cli-0.1.1/.github/workflows/release.yml +47 -0
  3. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/PKG-INFO +14 -5
  4. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/README.md +8 -0
  5. ynab_api_cli-0.1.1/docs/live-ci.md +41 -0
  6. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/docs/live-testing.md +8 -0
  7. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/pyproject.toml +6 -5
  8. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/pyproject.toml.orig +6 -5
  9. ynab_api_cli-0.1.1/src/ynab_api_cli/__init__.py +16 -0
  10. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/__main__.py +10 -0
  11. ynab_api_cli-0.1.1/src/ynab_api_cli/ci_live.py +74 -0
  12. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/live.py +7 -0
  13. ynab_api_cli-0.1.1/tests/test_ci_live.py +166 -0
  14. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/tests/test_cli.py +25 -0
  15. ynab_api_cli-0.1.0/.github/workflows/release.yml +0 -172
  16. ynab_api_cli-0.1.0/src/ynab_api_cli/__init__.py +0 -3
  17. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/.github/workflows/ci.yml +0 -0
  18. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/.release-please-manifest.json +0 -0
  19. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/CHANGELOG.md +0 -0
  20. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/docs/maintenance.md +0 -0
  21. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/docs/privacy-review.md +0 -0
  22. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/docs/publishing.md +0 -0
  23. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/docs/python-development-review.md +0 -0
  24. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/docs/verification.md +0 -0
  25. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/release-please-config.json +0 -0
  26. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/skills/ynab-api-cli/SKILL.md +0 -0
  27. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/skills/ynab-api-cli/references/operations.md +0 -0
  28. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/skills/ynab-api-cli/references/setup.md +0 -0
  29. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/cli.py +0 -0
  30. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/compat.py +0 -0
  31. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/config.py +0 -0
  32. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/contract.py +0 -0
  33. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/contract_testing.py +0 -0
  34. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/development.py +0 -0
  35. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/distribution.py +0 -0
  36. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/exceptions.py +0 -0
  37. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/__init__.py +0 -0
  38. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/__init__.py +0 -0
  39. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/accounts/__init__.py +0 -0
  40. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/accounts/create_account.py +0 -0
  41. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/accounts/get_account_by_id.py +0 -0
  42. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/accounts/get_accounts.py +0 -0
  43. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/categories/__init__.py +0 -0
  44. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/categories/create_category.py +0 -0
  45. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/categories/create_category_group.py +0 -0
  46. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/categories/get_categories.py +0 -0
  47. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/categories/get_category_by_id.py +0 -0
  48. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/categories/get_month_category_by_id.py +0 -0
  49. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/categories/update_category.py +0 -0
  50. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/categories/update_category_group.py +0 -0
  51. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/categories/update_month_category.py +0 -0
  52. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/money_movements/__init__.py +0 -0
  53. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/money_movements/get_money_movement_groups.py +0 -0
  54. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/money_movements/get_money_movement_groups_by_month.py +0 -0
  55. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/money_movements/get_money_movements.py +0 -0
  56. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/money_movements/get_money_movements_by_month.py +0 -0
  57. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/months/__init__.py +0 -0
  58. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/months/get_plan_month.py +0 -0
  59. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/months/get_plan_months.py +0 -0
  60. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/payee_locations/__init__.py +0 -0
  61. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/payee_locations/get_payee_location_by_id.py +0 -0
  62. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/payee_locations/get_payee_locations.py +0 -0
  63. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/payee_locations/get_payee_locations_by_payee.py +0 -0
  64. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/payees/__init__.py +0 -0
  65. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/payees/create_payee.py +0 -0
  66. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/payees/get_payee_by_id.py +0 -0
  67. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/payees/get_payees.py +0 -0
  68. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/payees/update_payee.py +0 -0
  69. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/plans/__init__.py +0 -0
  70. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/plans/get_plan_by_id.py +0 -0
  71. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/plans/get_plan_settings_by_id.py +0 -0
  72. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/plans/get_plans.py +0 -0
  73. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/scheduled_transactions/__init__.py +0 -0
  74. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/scheduled_transactions/create_scheduled_transaction.py +0 -0
  75. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/scheduled_transactions/delete_scheduled_transaction.py +0 -0
  76. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/scheduled_transactions/get_scheduled_transaction_by_id.py +0 -0
  77. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/scheduled_transactions/get_scheduled_transactions.py +0 -0
  78. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/scheduled_transactions/update_scheduled_transaction.py +0 -0
  79. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/transactions/__init__.py +0 -0
  80. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/transactions/create_transaction.py +0 -0
  81. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/transactions/delete_transaction.py +0 -0
  82. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/transactions/get_transaction_by_id.py +0 -0
  83. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/transactions/get_transactions.py +0 -0
  84. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/transactions/get_transactions_by_account.py +0 -0
  85. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/transactions/get_transactions_by_category.py +0 -0
  86. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/transactions/get_transactions_by_month.py +0 -0
  87. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/transactions/get_transactions_by_payee.py +0 -0
  88. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/transactions/import_transactions.py +0 -0
  89. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/transactions/update_transaction.py +0 -0
  90. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/transactions/update_transactions.py +0 -0
  91. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/user/__init__.py +0 -0
  92. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/api/user/get_user.py +0 -0
  93. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/bindings.py +0 -0
  94. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/__init__.py +0 -0
  95. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/account.py +0 -0
  96. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/account_base.py +0 -0
  97. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/account_response.py +0 -0
  98. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/account_response_data.py +0 -0
  99. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/account_type.py +0 -0
  100. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/accounts_response.py +0 -0
  101. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/accounts_response_data.py +0 -0
  102. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/bulk_response.py +0 -0
  103. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/bulk_response_data.py +0 -0
  104. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/bulk_response_data_bulk.py +0 -0
  105. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/bulk_transactions.py +0 -0
  106. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/categories_response.py +0 -0
  107. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/categories_response_data.py +0 -0
  108. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/category.py +0 -0
  109. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/category_base.py +0 -0
  110. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/category_group.py +0 -0
  111. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/category_group_with_categories.py +0 -0
  112. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/category_response.py +0 -0
  113. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/category_response_data.py +0 -0
  114. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/currency_format_type_0.py +0 -0
  115. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/date_format_type_0.py +0 -0
  116. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/debt_1.py +0 -0
  117. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/debt_2.py +0 -0
  118. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/debt_3.py +0 -0
  119. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/error_detail.py +0 -0
  120. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/error_response.py +0 -0
  121. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/existing_category.py +0 -0
  122. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/existing_transaction.py +0 -0
  123. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/flag_1.py +0 -0
  124. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/flag_2.py +0 -0
  125. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/flag_3.py +0 -0
  126. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/get_transactions_by_account_type.py +0 -0
  127. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/get_transactions_by_category_type.py +0 -0
  128. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/get_transactions_by_month_type.py +0 -0
  129. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/get_transactions_by_payee_type.py +0 -0
  130. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/get_transactions_type.py +0 -0
  131. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/goal_1.py +0 -0
  132. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/goal_2.py +0 -0
  133. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/goal_3.py +0 -0
  134. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/hybrid_transaction.py +0 -0
  135. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/hybrid_transaction_type.py +0 -0
  136. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/hybrid_transactions_response.py +0 -0
  137. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/hybrid_transactions_response_data.py +0 -0
  138. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/loan_account_periodic_value_type_0.py +0 -0
  139. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/money_movement.py +0 -0
  140. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/money_movement_base.py +0 -0
  141. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/money_movement_group.py +0 -0
  142. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/money_movement_groups_response.py +0 -0
  143. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/money_movement_groups_response_data.py +0 -0
  144. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/money_movements_response.py +0 -0
  145. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/money_movements_response_data.py +0 -0
  146. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/month_detail.py +0 -0
  147. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/month_detail_base.py +0 -0
  148. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/month_detail_response.py +0 -0
  149. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/month_detail_response_data.py +0 -0
  150. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/month_summaries_response.py +0 -0
  151. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/month_summaries_response_data.py +0 -0
  152. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/month_summary.py +0 -0
  153. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/month_summary_base.py +0 -0
  154. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/new_category.py +0 -0
  155. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/new_transaction.py +0 -0
  156. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/patch_category_group_wrapper.py +0 -0
  157. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/patch_category_wrapper.py +0 -0
  158. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/patch_month_category_wrapper.py +0 -0
  159. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/patch_payee_wrapper.py +0 -0
  160. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/patch_transactions_wrapper.py +0 -0
  161. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/payee.py +0 -0
  162. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/payee_location.py +0 -0
  163. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/payee_location_response.py +0 -0
  164. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/payee_location_response_data.py +0 -0
  165. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/payee_locations_response.py +0 -0
  166. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/payee_locations_response_data.py +0 -0
  167. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/payee_response.py +0 -0
  168. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/payee_response_data.py +0 -0
  169. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/payees_response.py +0 -0
  170. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/payees_response_data.py +0 -0
  171. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/plan_detail.py +0 -0
  172. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/plan_detail_response.py +0 -0
  173. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/plan_detail_response_data.py +0 -0
  174. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/plan_settings.py +0 -0
  175. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/plan_settings_response.py +0 -0
  176. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/plan_settings_response_data.py +0 -0
  177. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/plan_summary.py +0 -0
  178. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/plan_summary_response.py +0 -0
  179. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/plan_summary_response_data.py +0 -0
  180. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/post_account_wrapper.py +0 -0
  181. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/post_category_group_wrapper.py +0 -0
  182. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/post_category_wrapper.py +0 -0
  183. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/post_payee.py +0 -0
  184. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/post_payee_wrapper.py +0 -0
  185. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/post_scheduled_transaction_wrapper.py +0 -0
  186. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/post_transactions_wrapper.py +0 -0
  187. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/put_scheduled_transaction_wrapper.py +0 -0
  188. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/put_transaction_wrapper.py +0 -0
  189. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/save_account.py +0 -0
  190. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/save_account_type.py +0 -0
  191. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/save_category.py +0 -0
  192. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/save_category_goal_frequency.py +0 -0
  193. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/save_category_group.py +0 -0
  194. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/save_category_group_response.py +0 -0
  195. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/save_category_group_response_data.py +0 -0
  196. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/save_category_response.py +0 -0
  197. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/save_category_response_data.py +0 -0
  198. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/save_month_category.py +0 -0
  199. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/save_payee.py +0 -0
  200. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/save_payee_response.py +0 -0
  201. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/save_payee_response_data.py +0 -0
  202. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/save_scheduled_transaction.py +0 -0
  203. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/save_sub_transaction.py +0 -0
  204. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/save_transaction_with_id_or_import_id.py +0 -0
  205. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/save_transaction_with_optional_fields.py +0 -0
  206. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/save_transactions_response.py +0 -0
  207. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/save_transactions_response_data.py +0 -0
  208. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/scheduled_sub_transaction.py +0 -0
  209. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/scheduled_sub_transaction_base.py +0 -0
  210. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/scheduled_transaction_detail.py +0 -0
  211. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/scheduled_transaction_frequency.py +0 -0
  212. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/scheduled_transaction_response.py +0 -0
  213. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/scheduled_transaction_response_data.py +0 -0
  214. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/scheduled_transaction_summary.py +0 -0
  215. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/scheduled_transaction_summary_base.py +0 -0
  216. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/scheduled_transaction_summary_base_frequency.py +0 -0
  217. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/scheduled_transactions_response.py +0 -0
  218. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/scheduled_transactions_response_data.py +0 -0
  219. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/sub_transaction.py +0 -0
  220. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/sub_transaction_base.py +0 -0
  221. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/transaction_cleared_status.py +0 -0
  222. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/transaction_detail.py +0 -0
  223. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/transaction_response.py +0 -0
  224. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/transaction_response_data.py +0 -0
  225. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/transaction_summary.py +0 -0
  226. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/transaction_summary_base.py +0 -0
  227. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/transactions_import_response.py +0 -0
  228. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/transactions_import_response_data.py +0 -0
  229. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/transactions_response.py +0 -0
  230. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/transactions_response_data.py +0 -0
  231. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/user.py +0 -0
  232. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/user_response.py +0 -0
  233. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/generated/models/user_response_data.py +0 -0
  234. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/py.typed +0 -0
  235. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/resources/__init__.py +0 -0
  236. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/resources/catalog.json +0 -0
  237. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/resources/openapi.yaml +0 -0
  238. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/resources/provenance.json +0 -0
  239. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/resources/schema_features.json +0 -0
  240. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/resources/skill/SKILL.md +0 -0
  241. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/resources/skill/references/operations.md +0 -0
  242. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/resources/skill/references/setup.md +0 -0
  243. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/security.py +0 -0
  244. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/transport.py +0 -0
  245. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/types.py +0 -0
  246. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/src/ynab_api_cli/verification.py +0 -0
  247. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/templates/endpoint_module.py.jinja +0 -0
  248. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/templates/model.py.jinja +0 -0
  249. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/tests/conftest.py +0 -0
  250. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/tests/fixtures/wire_cases.json +0 -0
  251. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/tests/support.py +0 -0
  252. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/tests/test_compat.py +0 -0
  253. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/tests/test_config.py +0 -0
  254. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/tests/test_contract.py +0 -0
  255. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/tests/test_development.py +0 -0
  256. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/tests/test_distribution.py +0 -0
  257. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/tests/test_features.py +0 -0
  258. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/tests/test_live.py +0 -0
  259. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/tests/test_transport.py +0 -0
  260. {ynab_api_cli-0.1.0 → ynab_api_cli-0.1.1}/tests/test_typing.py +0 -0
@@ -0,0 +1,35 @@
1
+ name: Read-only live YNAB tests
2
+
3
+ # Environment secrets must be configured by the repository owner separately.
4
+ on:
5
+ workflow_dispatch:
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ concurrency:
11
+ group: ynab-live
12
+ cancel-in-progress: false
13
+
14
+ jobs:
15
+ live:
16
+ if: github.ref == 'refs/heads/main'
17
+ runs-on: ubuntu-latest
18
+ timeout-minutes: 10
19
+ environment: ynab-e2e
20
+ steps:
21
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
22
+ with:
23
+ persist-credentials: false
24
+ - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
25
+ with:
26
+ enable-cache: false
27
+ - name: Install reviewed locked dependencies without YNAB secrets
28
+ run: uv sync --locked --no-editable
29
+ - name: Run read-only probes with step-scoped secrets
30
+ env:
31
+ YNAB_ACCESS_TOKEN: ${{ secrets.YNAB_ACCESS_TOKEN }}
32
+ YNAB_TEST_PLAN_ID: ${{ secrets.YNAB_TEST_PLAN_ID }}
33
+ run: |
34
+ ulimit -c 0
35
+ .venv/bin/python -m ynab_api_cli.ci_live
@@ -0,0 +1,47 @@
1
+ name: Release (legacy ynab-api-cli)
2
+ # Publishes the final, deprecated ynab-api-cli release from the legacy/ynab-api-cli branch.
3
+ # The maintained package is codapact-ynab, released from main.
4
+ on:
5
+ workflow_dispatch:
6
+ permissions:
7
+ contents: read
8
+ concurrency:
9
+ group: release-legacy
10
+ cancel-in-progress: false
11
+ jobs:
12
+ build:
13
+ if: github.ref == 'refs/heads/legacy/ynab-api-cli'
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
17
+ with:
18
+ persist-credentials: false
19
+ - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
20
+ - run: uv sync --locked
21
+ - run: uv run poe check
22
+ - run: uv build
23
+ - run: uvx --from twine==6.2.0 twine check --strict dist/*.whl dist/*.tar.gz
24
+ - run: uv run poe distribution-check
25
+ - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
26
+ with:
27
+ name: python-distributions
28
+ path: |
29
+ dist/*.whl
30
+ dist/*.tar.gz
31
+ if-no-files-found: error
32
+
33
+ publish-pypi:
34
+ needs: build
35
+ runs-on: ubuntu-latest
36
+ environment:
37
+ name: pypi
38
+ url: https://pypi.org/project/ynab-api-cli/
39
+ permissions:
40
+ id-token: write
41
+ steps:
42
+ - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
43
+ with:
44
+ name: python-distributions
45
+ path: dist/
46
+ - name: Publish through trusted publishing
47
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
@@ -1,18 +1,25 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ynab-api-cli
3
- Version: 0.1.0
4
- Summary: Complete contract-driven REST CLI for YNAB
3
+ Version: 0.1.1
4
+ Summary: DEPRECATED: use codapact-ynab. Contract-driven REST CLI for YNAB
5
5
  Author: Mike Hill
6
+ Classifier: Development Status :: 7 - Inactive
6
7
  Requires-Dist: httpx>=0.28.1
7
8
  Requires-Dist: jsonschema[format]>=4.26.0
8
9
  Requires-Dist: pydantic>=2.14.1
9
10
  Requires-Dist: pyyaml>=6.0.3
10
11
  Requires-Python: >=3.12
11
- Project-URL: Homepage, https://github.com/mikejhill/ynab-api-cli
12
- Project-URL: Repository, https://github.com/mikejhill/ynab-api-cli
13
- Project-URL: Issues, https://github.com/mikejhill/ynab-api-cli/issues
12
+ Project-URL: Homepage, https://github.com/mikejhill/codapact-ynab
13
+ Project-URL: Repository, https://github.com/mikejhill/codapact-ynab
14
+ Project-URL: Issues, https://github.com/mikejhill/codapact-ynab/issues
14
15
  Description-Content-Type: text/markdown
15
16
 
17
+ > [!WARNING]
18
+ > **Deprecated.** `ynab-api-cli` receives no further updates. Use
19
+ > [`codapact-ynab`](https://pypi.org/project/codapact-ynab/) instead:
20
+ > `uv tool install codapact-ynab`, then run `ynab --help`.
21
+ > Source: https://github.com/mikejhill/codapact-ynab
22
+
16
23
  # YNAB API CLI
17
24
 
18
25
  Complete, synchronous REST CLI for the reviewed YNAB API contract. The official SDK is not a dependency. Generated, strictly typed endpoint classes and models are included for Python callers.
@@ -74,6 +81,8 @@ The contract snapshot, reviewed operation catalog, and provenance are packaged r
74
81
 
75
82
  See [maintenance](docs/maintenance.md) for reviewed upgrades, test coverage, and CI. See [live testing](docs/live-testing.md) before enabling writes against a dedicated test plan.
76
83
 
84
+ See [secure live CI](docs/live-ci.md) for the separate, manually approved read-only workflow. Ordinary CI requires no YNAB credentials. No local token is uploaded automatically.
85
+
77
86
  ## Supplementary Codex skill
78
87
 
79
88
  ```text
@@ -1,3 +1,9 @@
1
+ > [!WARNING]
2
+ > **Deprecated.** `ynab-api-cli` receives no further updates. Use
3
+ > [`codapact-ynab`](https://pypi.org/project/codapact-ynab/) instead:
4
+ > `uv tool install codapact-ynab`, then run `ynab --help`.
5
+ > Source: https://github.com/mikejhill/codapact-ynab
6
+
1
7
  # YNAB API CLI
2
8
 
3
9
  Complete, synchronous REST CLI for the reviewed YNAB API contract. The official SDK is not a dependency. Generated, strictly typed endpoint classes and models are included for Python callers.
@@ -59,6 +65,8 @@ The contract snapshot, reviewed operation catalog, and provenance are packaged r
59
65
 
60
66
  See [maintenance](docs/maintenance.md) for reviewed upgrades, test coverage, and CI. See [live testing](docs/live-testing.md) before enabling writes against a dedicated test plan.
61
67
 
68
+ See [secure live CI](docs/live-ci.md) for the separate, manually approved read-only workflow. Ordinary CI requires no YNAB credentials. No local token is uploaded automatically.
69
+
62
70
  ## Supplementary Codex skill
63
71
 
64
72
  ```text
@@ -0,0 +1,41 @@
1
+ # Live tests without publishing credentials
2
+
3
+ The ordinary PR, push, scheduled contract, and release checks do not use YNAB credentials. The separate `live.yml` workflow is manual, restricted to main, and read-only. It checks the explicit disposable plan, validates available responses against the reviewed contract, and reports operation names and gaps. It does not print plan IDs, journal paths, response bodies, or fixture IDs.
4
+
5
+ ## Security boundary
6
+
7
+ GitHub encrypts environment secrets, but the approved runner and application necessarily receive the plaintext token in memory. Secret masking is not an exfiltration barrier: malicious workflow code, actions, or dependencies can steal a token. If your requirement is that the token never leaves this computer, keep live tests local and do not configure a GitHub secret. See [GitHub's secret guidance](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets).
8
+
9
+ YNAB personal access tokens are account credentials. Selecting a dummy plan in the CLI does not restrict the token's API permissions. For CI, use a dedicated test account containing only disposable data where possible; a separately issued token on the same account is independently revocable but still has account access. See [YNAB authentication](https://api.ynab.com/#personal-access-tokens).
10
+
11
+ ## Configure the protected environment before enabling the workflow
12
+
13
+ 1. Open repository **Settings → Environments → New environment**, and name it `ynab-e2e`. Do not reuse `pypi`.
14
+ 2. Add a trusted maintainer as a required reviewer. For a single-maintainer repository, allow that maintainer to review their own manual run; preventing self-review requires another reviewer. Restrict deployment branches to the selected branch `main`, and prevent bypassing protection rules where available.
15
+ 3. Protect main through branch rules, require code review, and review changes to `live.yml`, `ci_live.py`, `live.py`, transport code, actions, and `uv.lock` before approving a live job. Pin actions to reviewed commit SHAs.
16
+ 4. Create environment secrets `YNAB_ACCESS_TOKEN` and `YNAB_TEST_PLAN_ID`. The latter must be the explicit dummy-plan UUID. The plan name must begin `YNAB API CLI TEST`.
17
+
18
+ Use GitHub's environment-secret UI or hidden local GitHub CLI prompts:
19
+
20
+ ```text
21
+ gh secret set YNAB_ACCESS_TOKEN --repo mikejhill/ynab-api-cli --env ynab-e2e
22
+ gh secret set YNAB_TEST_PLAN_ID --repo mikejhill/ynab-api-cli --env ynab-e2e
23
+ ```
24
+
25
+ Enter each value only at its local prompt. Do not use `--body TOKEN`, shell literals, command substitution that could echo it, chat, or a committed file. This setup sends the token to GitHub's encrypted secret store; it does not keep the token exclusively local.
26
+
27
+ 5. After merging the reviewed workflow into main, open **Actions → Read-only live YNAB tests → Run workflow**, choose main, inspect the exact commit and changes, then approve its environment deployment. [Environment protection rules](https://docs.github.com/en/actions/reference/workflows-and-actions/deployments-and-environments) gate when environment secrets become available.
28
+
29
+ The workflow has no PR trigger, no `pull_request_target`, no user-selected checkout ref, no artifact uploads, and no dependency caching. Dependency installation runs before the token-bearing step. Only that step receives the secrets. The helper consumes them from its process environment, creates a permission-protected temporary auth file, and removes its temporary files on success or failure. The job uses a fresh GitHub-hosted runner. Reports contain only fixed status fields, reviewed operation names, and capability gaps. Do not enable shell tracing, HTTP/debug logging, step debugging, core dumps, response uploads, or caches containing configuration.
30
+
31
+ Account-wide plan listing and user reads are excluded. This reduces application scope; it does not reduce token permissions. Read-only CI intentionally reports missing write and fixture-dependent operations, so success does not mean all-feature live coverage.
32
+
33
+ ## Keep mutation tests local
34
+
35
+ The full runner creates reusable accounts, a category group, a category, and a payee, and exercises transaction and budgeting changes. Some fixtures cannot be deleted through the API. It persists private mutation intents and stops on uncertain outcomes. Keep that journal outside the repository and reconcile uncertain writes before rerunning.
36
+
37
+ Write tests are deliberately absent from `live.yml`. Ephemeral CI journals disappear when a job ends; blindly rerunning a failed write job can duplicate an uncertain mutation. Adding CI writes requires a reviewed design for durable private journal storage, restoration before every run, concurrency, reconciliation, retention, and cleanup. Do not upload an auth file or mutation journal as a public workflow artifact.
38
+
39
+ ## Rotation and incident response
40
+
41
+ Revoke the CI token in YNAB Developer Settings and replace the environment secret to rotate it. Remove the secret and disable the live workflow when no longer needed. If a token may have appeared in output or an untrusted runner, revoke it immediately; deleting logs alone is insufficient. No local token is uploaded automatically by this project.
@@ -25,3 +25,11 @@ Before each mutation, the runner writes a private intent journal. An uncertain o
25
25
  Payee-location reads require location fixtures. Direct Import needs a linked bank account; manual test accounts cannot verify actual bank imports. The report lists these gaps and never claims full live coverage when they remain.
26
26
 
27
27
  No live account connection is provisioned by installing this project. Supply credentials and a designated plan explicitly to run these tests.
28
+
29
+ ## Verified live run, 2026-10-18
30
+
31
+ An explicitly designated disposable plan passed the real read/write lifecycle run: 43 of 44 operations completed. The run covered accounts, categories, payees, transaction creation and bulk updates, duplicate import IDs, splits, transfers, scheduled transactions, budgeting, and deletion checks. The missing operation was `getPayeeLocationById` because no suitable location fixture existed. The import endpoint completed, but actual bank import remains unverified for manual accounts.
32
+
33
+ Subsequent reads confirmed that both fixture account balances returned to zero, and the private journal had no unresolved mutation. Reusable accounts, category group, category, and payee remain in the disposable plan. No token, plan UUID, account data, or response bodies are included in this report.
34
+
35
+ The proposed CI-specific read-only probe also passed 15 selected-plan operations against that plan. See [secure live CI](live-ci.md) before putting credentials on a remote runner. These results do not claim full live coverage.
@@ -1,7 +1,8 @@
1
1
  [project]
2
2
  name = "ynab-api-cli"
3
- version = "0.1.0"
4
- description = "Complete contract-driven REST CLI for YNAB"
3
+ version = "0.1.1"
4
+ description = "DEPRECATED: use codapact-ynab. Contract-driven REST CLI for YNAB"
5
+ classifiers = ["Development Status :: 7 - Inactive"]
5
6
  readme = "README.md"
6
7
  requires-python = ">=3.12"
7
8
  dependencies = [
@@ -18,9 +19,9 @@ name = "Mike Hill"
18
19
  ynab-api-cli = "ynab_api_cli.__main__:Application.main"
19
20
 
20
21
  [project.urls]
21
- Homepage = "https://github.com/mikejhill/ynab-api-cli"
22
- Repository = "https://github.com/mikejhill/ynab-api-cli"
23
- Issues = "https://github.com/mikejhill/ynab-api-cli/issues"
22
+ Homepage = "https://github.com/mikejhill/codapact-ynab"
23
+ Repository = "https://github.com/mikejhill/codapact-ynab"
24
+ Issues = "https://github.com/mikejhill/codapact-ynab/issues"
24
25
 
25
26
  [build-system]
26
27
  requires = ["uv_build>=0.11.32,<0.12.0"]
@@ -1,7 +1,8 @@
1
1
  [project]
2
2
  name = "ynab-api-cli"
3
- version = "0.1.0"
4
- description = "Complete contract-driven REST CLI for YNAB"
3
+ version = "0.1.1"
4
+ description = "DEPRECATED: use codapact-ynab. Contract-driven REST CLI for YNAB"
5
+ classifiers = ["Development Status :: 7 - Inactive"]
5
6
  readme = "README.md"
6
7
  authors = [
7
8
  { name = "Mike Hill" }
@@ -18,9 +19,9 @@ dependencies = [
18
19
  ynab-api-cli = "ynab_api_cli.__main__:Application.main"
19
20
 
20
21
  [project.urls]
21
- Homepage = "https://github.com/mikejhill/ynab-api-cli"
22
- Repository = "https://github.com/mikejhill/ynab-api-cli"
23
- Issues = "https://github.com/mikejhill/ynab-api-cli/issues"
22
+ Homepage = "https://github.com/mikejhill/codapact-ynab"
23
+ Repository = "https://github.com/mikejhill/codapact-ynab"
24
+ Issues = "https://github.com/mikejhill/codapact-ynab/issues"
24
25
 
25
26
  [build-system]
26
27
  requires = ["uv_build>=0.11.32,<0.12.0"]
@@ -0,0 +1,16 @@
1
+ """Deprecated: use codapact-ynab (https://pypi.org/project/codapact-ynab/)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ import warnings
7
+ from pathlib import Path
8
+
9
+ # The console script prints its own notice; warn library importers only.
10
+ if Path(sys.argv[0] if sys.argv else "").stem != "ynab-api-cli":
11
+ warnings.warn(
12
+ "ynab-api-cli is deprecated; switch to codapact-ynab "
13
+ "(import codapact_ynab, command `ynab`): https://pypi.org/project/codapact-ynab/",
14
+ DeprecationWarning,
15
+ stacklevel=2,
16
+ )
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import getpass
6
+ import io
6
7
  import json
7
8
  import logging
8
9
  import shutil
@@ -24,6 +25,12 @@ from ynab_api_cli.types import JsonCodec, JsonValue, RequestValues
24
25
 
25
26
  logger = logging.getLogger(__name__)
26
27
 
28
+ DEPRECATION_NOTICE = (
29
+ "ynab-api-cli is deprecated and receives no further updates. "
30
+ "Switch to codapact-ynab: `uv tool install codapact-ynab`, then run `ynab`. "
31
+ "https://pypi.org/project/codapact-ynab/"
32
+ )
33
+
27
34
 
28
35
  class Application:
29
36
  """Coordinate connection setup and reviewed typed operation dispatch."""
@@ -38,6 +45,9 @@ class Application:
38
45
  @classmethod
39
46
  def main(cls, argv: Sequence[str] | None = None) -> None:
40
47
  """Run the console entry point and return its documented exit status."""
48
+ if isinstance(sys.stdout, io.TextIOWrapper):
49
+ sys.stdout.reconfigure(encoding="utf-8")
50
+ sys.stderr.write(f"warning: {DEPRECATION_NOTICE}\n")
41
51
  sys.exit(cls().run(argv))
42
52
 
43
53
  def run(self, argv: Sequence[str] | None = None) -> int:
@@ -0,0 +1,74 @@
1
+ """Run read-only CI probes with temporary credentials and sanitized output."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import logging
7
+ import os
8
+ import sys
9
+ import tempfile
10
+ from collections.abc import MutableMapping
11
+ from pathlib import Path
12
+ from uuid import UUID
13
+
14
+ import httpx
15
+
16
+ from ynab_api_cli.config import ConfigStore
17
+ from ynab_api_cli.live import LiveOptions, LiveRunner
18
+ from ynab_api_cli.transport import Connection, Transport
19
+ from ynab_api_cli.types import JsonValue
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ class Application:
25
+ """Consume CI secrets once and expose only operation-level test results."""
26
+
27
+ @classmethod
28
+ def main(cls, client: httpx.Client | None = None) -> None:
29
+ """Run the probe without emitting exception details or API payloads."""
30
+ logging.basicConfig(level=logging.WARNING)
31
+ try:
32
+ report = cls.run(os.environ, client)
33
+ except KeyboardInterrupt:
34
+ raise SystemExit(130) from None
35
+ except Exception: # noqa: BLE001 - redact unexpected failures at the process boundary.
36
+ logger.error( # noqa: TRY400 - tracebacks may expose credentials.
37
+ "Live CI probe failed; no response or credential details logged."
38
+ )
39
+ raise SystemExit(1) from None
40
+ sys.stdout.write(json.dumps(report, indent=2) + "\n")
41
+
42
+ @staticmethod
43
+ def run(
44
+ environment: MutableMapping[str, str], client: httpx.Client | None = None
45
+ ) -> dict[str, JsonValue]:
46
+ """Use explicit environment secrets and remove private files on all exits."""
47
+ token = environment.pop("YNAB_ACCESS_TOKEN", "")
48
+ plan = environment.pop("YNAB_TEST_PLAN_ID", "")
49
+ connection = Connection(token, str(UUID(plan)))
50
+ with tempfile.TemporaryDirectory(prefix="ynab-api-cli-live-") as temporary:
51
+ root = Path(temporary)
52
+ store = ConfigStore(root / "config")
53
+ store.configure(connection.token)
54
+ options = LiveOptions(
55
+ plan_id=connection.plan_id or "",
56
+ auth_file=store.root / "auth.json",
57
+ state_directory=root / "state",
58
+ enable_writes=False,
59
+ account_reads=False,
60
+ )
61
+ runner = LiveRunner(options, Transport(connection, client=client))
62
+ result = runner.run()
63
+ return {
64
+ "ok": True,
65
+ "mode": "read-only",
66
+ "completed_operations": result["completed_operations"],
67
+ "missing_operations": result["missing_operations"],
68
+ "gaps": result["gaps"],
69
+ "complete_live_coverage": False,
70
+ }
71
+
72
+
73
+ if __name__ == "__main__":
74
+ Application.main()
@@ -30,6 +30,7 @@ class LiveOptions:
30
30
  auth_file: Path
31
31
  state_directory: Path
32
32
  enable_writes: bool
33
+ account_reads: bool = True
33
34
 
34
35
 
35
36
  class LiveRunner:
@@ -405,6 +406,12 @@ class LiveRunner:
405
406
  for operation in self.contract.operations.values():
406
407
  if operation.write or operation.identifier in self.completed:
407
408
  continue
409
+ if not self.options.account_reads and operation.identifier in {
410
+ "getPlans",
411
+ "getUser",
412
+ }:
413
+ self.gaps.append(operation.identifier + ": account-wide read excluded")
414
+ continue
408
415
  required = {
409
416
  str(param["name"])
410
417
  for param in self.contract.parameters(operation)
@@ -0,0 +1,166 @@
1
+ """Network-boundary tests for CI confinement, redaction, and credential cleanup."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ import re
8
+ import tempfile
9
+ from enum import StrEnum
10
+ from pathlib import Path
11
+ from uuid import UUID
12
+
13
+ import httpx
14
+ import pytest
15
+ from tests.support import Server
16
+
17
+ from ynab_api_cli.ci_live import Application
18
+ from ynab_api_cli.contract import Contract
19
+ from ynab_api_cli.exceptions import AppError
20
+
21
+
22
+ class Failure(StrEnum):
23
+ """Adversarial API outcomes that must not disclose request secrets."""
24
+
25
+ NONE = "none"
26
+ STATUS = "status"
27
+ MALFORMED = "malformed"
28
+ NETWORK = "network"
29
+ UNEXPECTED = "unexpected"
30
+ PLAN_NAME = "plan-name"
31
+
32
+
33
+ class LiveServer:
34
+ """Serve reviewed response schemas at an observable HTTP boundary."""
35
+
36
+ def __init__(self, contract: Contract, *, failure: Failure = Failure.NONE) -> None:
37
+ self._contract = contract
38
+ self._failure = failure
39
+ self.requests: list[httpx.Request] = []
40
+
41
+ def handle(self, request: httpx.Request) -> httpx.Response:
42
+ """Return representative responses or deliberately secret-bearing failures."""
43
+ self.requests.append(request)
44
+ secret = request.headers["Authorization"]
45
+ if self._failure == Failure.STATUS:
46
+ return httpx.Response(502, json={"detail": secret})
47
+ if self._failure == Failure.MALFORMED:
48
+ return httpx.Response(200, text=secret)
49
+ if self._failure == Failure.NETWORK:
50
+ raise httpx.ReadTimeout(secret)
51
+ if self._failure == Failure.UNEXPECTED:
52
+ raise ValueError(secret)
53
+ path = request.url.path.removeprefix("/v1")
54
+ operation = next(
55
+ item
56
+ for item in self._contract.operations.values()
57
+ if item.method == request.method
58
+ and re.fullmatch(re.sub(r"\{[^}]+\}", "[^/]+", item.path), path)
59
+ )
60
+ response = Server(self._contract, operation.identifier).handle(request)
61
+ if operation.identifier != "getPlanById":
62
+ return response
63
+ data = response.json()
64
+ data["data"]["plan"]["name"] = (
65
+ "ordinary plan"
66
+ if self._failure == Failure.PLAN_NAME
67
+ else "YNAB API CLI TEST synthetic"
68
+ )
69
+ return httpx.Response(response.status_code, json=data)
70
+
71
+
72
+ class TestApplication:
73
+ """Exercise real orchestration while replacing only the HTTP boundary."""
74
+
75
+ CREDENTIAL = "synthetic-ci-secret-do-not-log"
76
+ PLAN = str(UUID(int=123))
77
+
78
+ def environment(self, monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
79
+ """Supply fake step secrets and confine temporary files to the test."""
80
+ monkeypatch.setenv("YNAB_ACCESS_TOKEN", self.CREDENTIAL)
81
+ monkeypatch.setenv("YNAB_TEST_PLAN_ID", self.PLAN)
82
+ monkeypatch.setattr(tempfile, "tempdir", str(tmp_path))
83
+
84
+ def test_read_only_scope_and_summary(
85
+ self,
86
+ contract: Contract,
87
+ monkeypatch: pytest.MonkeyPatch,
88
+ tmp_path: Path,
89
+ capsys: pytest.CaptureFixture[str],
90
+ ) -> None:
91
+ """CI submits only selected-plan GETs and exports no private record data."""
92
+ self.environment(monkeypatch, tmp_path)
93
+ server = LiveServer(contract)
94
+ with httpx.Client(transport=httpx.MockTransport(server.handle)) as client:
95
+ Application.main(client)
96
+
97
+ output = capsys.readouterr().out
98
+ report = json.loads(output)
99
+ assert report["ok"] is True
100
+ assert report["mode"] == "read-only"
101
+ assert report["complete_live_coverage"] is False
102
+ assert "getPlanById" in report["completed_operations"]
103
+ assert "getPlans" not in report["completed_operations"]
104
+ assert "getUser" not in report["completed_operations"]
105
+ assert all(request.method == "GET" for request in server.requests)
106
+ assert all(
107
+ request.url.path.startswith("/v1/plans/" + self.PLAN)
108
+ for request in server.requests
109
+ )
110
+ assert self.CREDENTIAL not in output
111
+ assert self.PLAN not in output
112
+ assert "YNAB_ACCESS_TOKEN" not in os.environ
113
+ assert "YNAB_TEST_PLAN_ID" not in os.environ
114
+ assert not list(tmp_path.rglob("auth.json"))
115
+ assert not list(tmp_path.iterdir())
116
+
117
+ @pytest.mark.parametrize(
118
+ "failure", [item for item in Failure if item != Failure.NONE]
119
+ )
120
+ def test_failure_redaction_and_cleanup(
121
+ self,
122
+ monkeypatch: pytest.MonkeyPatch,
123
+ tmp_path: Path,
124
+ capsys: pytest.CaptureFixture[str],
125
+ caplog: pytest.LogCaptureFixture,
126
+ failure: Failure,
127
+ ) -> None:
128
+ """API payloads, nested exception details, and credentials stay out of logs."""
129
+ self.environment(monkeypatch, tmp_path)
130
+ server = LiveServer(Contract(), failure=failure)
131
+ with (
132
+ httpx.Client(transport=httpx.MockTransport(server.handle)) as client,
133
+ pytest.raises(SystemExit) as caught,
134
+ ):
135
+ Application.main(client)
136
+
137
+ captured = capsys.readouterr()
138
+ assert caught.value.code == 1
139
+ assert self.CREDENTIAL not in captured.out + captured.err + caplog.text
140
+ assert self.PLAN not in captured.out + captured.err + caplog.text
141
+ assert captured.out == ""
142
+ assert "Live CI probe failed" in caplog.text
143
+ assert not list(tmp_path.iterdir())
144
+
145
+ @pytest.mark.parametrize(
146
+ ("token", "plan"),
147
+ [
148
+ ("", PLAN),
149
+ ("contains whitespace", PLAN),
150
+ (CREDENTIAL, ""),
151
+ (CREDENTIAL, "default"),
152
+ ],
153
+ )
154
+ def test_invalid_settings_prevent_network(
155
+ self, contract: Contract, token: str, plan: str
156
+ ) -> None:
157
+ """Missing secrets, invalid headers, and implicit plan aliases fail locally."""
158
+ environment = {"YNAB_ACCESS_TOKEN": token, "YNAB_TEST_PLAN_ID": plan}
159
+ server = LiveServer(contract)
160
+ with (
161
+ httpx.Client(transport=httpx.MockTransport(server.handle)) as client,
162
+ pytest.raises((ValueError, AppError)),
163
+ ):
164
+ Application.run(environment, client)
165
+ assert environment == {}
166
+ assert server.requests == []
@@ -4,6 +4,9 @@ from __future__ import annotations
4
4
 
5
5
  import io
6
6
  import json
7
+ import os
8
+ import subprocess
9
+ import sys
7
10
  from pathlib import Path
8
11
  from unittest.mock import Mock
9
12
 
@@ -22,6 +25,28 @@ from ynab_api_cli.transport import Transport
22
25
  class TestApplication:
23
26
  """Exercise the public process behavior with isolated settings and HTTP."""
24
27
 
28
+ def test_utf8_redirected_output(self, tmp_path: Path) -> None:
29
+ """A real CLI subprocess emits Unicode JSON through a legacy-encoded pipe."""
30
+ xdg = tmp_path / "home-\U0001f3e0"
31
+ store = ConfigStore(xdg / "ynab-api-cli")
32
+ store.configure("synthetic-subprocess-credential")
33
+ environment = dict(os.environ)
34
+ environment.update({"XDG_CONFIG_HOME": str(xdg), "PYTHONIOENCODING": "cp1252"})
35
+
36
+ result = subprocess.run(
37
+ [sys.executable, "-m", "ynab_api_cli", "doctor", "--offline"],
38
+ cwd=tmp_path,
39
+ env=environment,
40
+ capture_output=True,
41
+ check=False,
42
+ )
43
+
44
+ assert result.returncode == 0
45
+ report = json.loads(result.stdout.decode("utf-8"))
46
+ assert report["ok"] is True
47
+ assert report["api_checked"] is False
48
+ assert report["directory"] == str(store.root)
49
+
25
50
  @pytest.mark.parametrize(
26
51
  "argv",
27
52
  [