holder-python 0.2.5__py3-none-any.whl

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 (391) hide show
  1. holder/__init__.py +21 -0
  2. holder/client.py +182 -0
  3. holder/collections.py +70 -0
  4. holder/events.py +238 -0
  5. holder/exceptions.py +35 -0
  6. holder/generated/__init__.py +8 -0
  7. holder/generated/api/__init__.py +1 -0
  8. holder/generated/api/default/__init__.py +1 -0
  9. holder/generated/api/default/delete_ai_messages_message_id.py +166 -0
  10. holder/generated/api/default/delete_ai_messages_message_id_links.py +205 -0
  11. holder/generated/api/default/delete_ai_providers_credentials_provider.py +168 -0
  12. holder/generated/api/default/delete_ai_threads_thread_id.py +166 -0
  13. holder/generated/api/default/delete_cards_card_id.py +225 -0
  14. holder/generated/api/default/delete_cards_card_id_links.py +205 -0
  15. holder/generated/api/default/delete_cards_card_id_milestones_milestone_id.py +175 -0
  16. holder/generated/api/default/delete_cards_card_id_resources.py +197 -0
  17. holder/generated/api/default/delete_cards_card_id_tags.py +197 -0
  18. holder/generated/api/default/delete_locations_location_id.py +105 -0
  19. holder/generated/api/default/delete_locations_location_id_binding.py +102 -0
  20. holder/generated/api/default/delete_projects_project_id.py +187 -0
  21. holder/generated/api/default/delete_resources_resource_id.py +161 -0
  22. holder/generated/api/default/delete_trash.py +181 -0
  23. holder/generated/api/default/delete_trash_type_id.py +175 -0
  24. holder/generated/api/default/get_ai_capabilities.py +166 -0
  25. holder/generated/api/default/get_ai_catalog_json.py +134 -0
  26. holder/generated/api/default/get_ai_catalog_yaml.py +132 -0
  27. holder/generated/api/default/get_ai_local_models_config.py +134 -0
  28. holder/generated/api/default/get_ai_messages.py +186 -0
  29. holder/generated/api/default/get_ai_messages_message_id.py +166 -0
  30. holder/generated/api/default/get_ai_messages_message_id_backlinks.py +194 -0
  31. holder/generated/api/default/get_ai_messages_message_id_links.py +194 -0
  32. holder/generated/api/default/get_ai_providers_catalog.py +139 -0
  33. holder/generated/api/default/get_ai_providers_credentials.py +136 -0
  34. holder/generated/api/default/get_ai_runner_pull_job_id.py +171 -0
  35. holder/generated/api/default/get_ai_runner_pull_job_id_events.py +218 -0
  36. holder/generated/api/default/get_ai_runs.py +186 -0
  37. holder/generated/api/default/get_ai_runs_run_id.py +166 -0
  38. holder/generated/api/default/get_ai_runs_run_id_events.py +213 -0
  39. holder/generated/api/default/get_ai_status.py +134 -0
  40. holder/generated/api/default/get_ai_threads.py +171 -0
  41. holder/generated/api/default/get_ai_threads_thread_id.py +166 -0
  42. holder/generated/api/default/get_calendar.py +221 -0
  43. holder/generated/api/default/get_cards.py +286 -0
  44. holder/generated/api/default/get_cards_card_id.py +166 -0
  45. holder/generated/api/default/get_cards_card_id_backlinks.py +194 -0
  46. holder/generated/api/default/get_cards_card_id_links.py +194 -0
  47. holder/generated/api/default/get_cards_card_id_milestones.py +173 -0
  48. holder/generated/api/default/get_cards_context.py +226 -0
  49. holder/generated/api/default/get_event_cursor.py +198 -0
  50. holder/generated/api/default/get_git_providers_json.py +134 -0
  51. holder/generated/api/default/get_git_providers_yaml.py +132 -0
  52. holder/generated/api/default/get_health.py +134 -0
  53. holder/generated/api/default/get_imports_job_id.py +159 -0
  54. holder/generated/api/default/get_locations.py +160 -0
  55. holder/generated/api/default/get_locations_location_id.py +157 -0
  56. holder/generated/api/default/get_openapi_yaml.py +132 -0
  57. holder/generated/api/default/get_ping.py +128 -0
  58. holder/generated/api/default/get_projects.py +261 -0
  59. holder/generated/api/default/get_projects_project_id.py +166 -0
  60. holder/generated/api/default/get_projects_project_id_cards.py +321 -0
  61. holder/generated/api/default/get_projects_project_id_encryption_check.py +166 -0
  62. holder/generated/api/default/get_projects_project_id_git_sync_status.py +171 -0
  63. holder/generated/api/default/get_projects_project_id_history.py +246 -0
  64. holder/generated/api/default/get_projects_project_id_history_cards_card_id.py +243 -0
  65. holder/generated/api/default/get_projects_project_id_history_cards_card_id_compare.py +286 -0
  66. holder/generated/api/default/get_projects_project_id_history_cards_card_id_snapshot.py +225 -0
  67. holder/generated/api/default/get_projects_project_id_tags.py +171 -0
  68. holder/generated/api/default/get_resources.py +242 -0
  69. holder/generated/api/default/get_resources_resource_id.py +161 -0
  70. holder/generated/api/default/get_resources_resource_id_assets_asset_id_content.py +247 -0
  71. holder/generated/api/default/get_search_ai.py +216 -0
  72. holder/generated/api/default/get_search_cards.py +216 -0
  73. holder/generated/api/default/get_trash.py +181 -0
  74. holder/generated/api/default/patch_ai_messages_message_id.py +192 -0
  75. holder/generated/api/default/patch_ai_threads_thread_id.py +192 -0
  76. holder/generated/api/default/patch_cards_card_id.py +192 -0
  77. holder/generated/api/default/patch_cards_card_id_milestones_milestone_id.py +214 -0
  78. holder/generated/api/default/patch_locations_location_id.py +117 -0
  79. holder/generated/api/default/patch_projects_project_id.py +192 -0
  80. holder/generated/api/default/patch_resources_resource_id.py +192 -0
  81. holder/generated/api/default/post_ai_messages.py +176 -0
  82. holder/generated/api/default/post_ai_messages_capture.py +181 -0
  83. holder/generated/api/default/post_ai_messages_message_id_links.py +192 -0
  84. holder/generated/api/default/post_ai_messages_message_id_restore.py +166 -0
  85. holder/generated/api/default/post_ai_runner_pull.py +181 -0
  86. holder/generated/api/default/post_ai_runner_retry.py +139 -0
  87. holder/generated/api/default/post_ai_runs.py +194 -0
  88. holder/generated/api/default/post_ai_threads.py +176 -0
  89. holder/generated/api/default/post_bye.py +166 -0
  90. holder/generated/api/default/post_card_references_resolve.py +181 -0
  91. holder/generated/api/default/post_cards.py +176 -0
  92. holder/generated/api/default/post_cards_card_id_links.py +192 -0
  93. holder/generated/api/default/post_cards_card_id_milestones.py +199 -0
  94. holder/generated/api/default/post_cards_card_id_move.py +222 -0
  95. holder/generated/api/default/post_cards_card_id_resources.py +197 -0
  96. holder/generated/api/default/post_cards_card_id_restore.py +186 -0
  97. holder/generated/api/default/post_cards_card_id_tags.py +197 -0
  98. holder/generated/api/default/post_imports.py +160 -0
  99. holder/generated/api/default/post_locations.py +160 -0
  100. holder/generated/api/default/post_locations_location_id_test.py +105 -0
  101. holder/generated/api/default/post_projects.py +171 -0
  102. holder/generated/api/default/post_projects_project_id_git_pull.py +211 -0
  103. holder/generated/api/default/post_projects_project_id_git_push.py +193 -0
  104. holder/generated/api/default/post_projects_project_id_git_sync.py +207 -0
  105. holder/generated/api/default/post_projects_project_id_git_test_remote.py +205 -0
  106. holder/generated/api/default/post_projects_project_id_history_cards_card_id_restore.py +240 -0
  107. holder/generated/api/default/post_projects_project_id_recovery_token_export.py +196 -0
  108. holder/generated/api/default/post_projects_project_id_recovery_token_import.py +196 -0
  109. holder/generated/api/default/post_projects_project_id_repair_hierarchy.py +240 -0
  110. holder/generated/api/default/post_rebuild.py +176 -0
  111. holder/generated/api/default/post_recovery_token_import.py +185 -0
  112. holder/generated/api/default/post_reindex.py +139 -0
  113. holder/generated/api/default/post_resources.py +176 -0
  114. holder/generated/api/default/put_ai_local_models_config.py +173 -0
  115. holder/generated/api/default/put_ai_providers_credentials.py +175 -0
  116. holder/generated/api/default/put_locations_location_id_binding.py +117 -0
  117. holder/generated/api/default/put_locations_preferred.py +107 -0
  118. holder/generated/api/default/stream_changes.py +256 -0
  119. holder/generated/client.py +280 -0
  120. holder/generated/errors.py +16 -0
  121. holder/generated/models/__init__.py +569 -0
  122. holder/generated/models/ai_local_model_config_data.py +111 -0
  123. holder/generated/models/ai_local_model_config_response.py +78 -0
  124. holder/generated/models/ai_local_model_config_update_request.py +134 -0
  125. holder/generated/models/ai_message.py +204 -0
  126. holder/generated/models/ai_message_capture_request.py +246 -0
  127. holder/generated/models/ai_message_capture_request_context_type_0.py +46 -0
  128. holder/generated/models/ai_message_capture_response.py +78 -0
  129. holder/generated/models/ai_message_capture_response_data.py +78 -0
  130. holder/generated/models/ai_message_create_request.py +186 -0
  131. holder/generated/models/ai_message_create_response.py +78 -0
  132. holder/generated/models/ai_message_create_response_data.py +62 -0
  133. holder/generated/models/ai_message_get_response.py +76 -0
  134. holder/generated/models/ai_message_list_response.py +84 -0
  135. holder/generated/models/ai_message_update_request.py +203 -0
  136. holder/generated/models/ai_message_update_response.py +78 -0
  137. holder/generated/models/ai_message_update_response_data.py +62 -0
  138. holder/generated/models/ai_provider_catalog_provider.py +265 -0
  139. holder/generated/models/ai_provider_catalog_provider_api_type_0.py +46 -0
  140. holder/generated/models/ai_provider_catalog_provider_auth_type_0.py +46 -0
  141. holder/generated/models/ai_provider_catalog_provider_models_item.py +46 -0
  142. holder/generated/models/ai_provider_catalog_response.py +78 -0
  143. holder/generated/models/ai_provider_catalog_response_data.py +100 -0
  144. holder/generated/models/ai_provider_credential.py +94 -0
  145. holder/generated/models/ai_provider_credential_delete_response.py +80 -0
  146. holder/generated/models/ai_provider_credential_delete_response_data.py +62 -0
  147. holder/generated/models/ai_provider_credential_upsert_request.py +92 -0
  148. holder/generated/models/ai_provider_credential_upsert_response.py +78 -0
  149. holder/generated/models/ai_provider_credentials_list_response.py +80 -0
  150. holder/generated/models/ai_provider_credentials_list_response_data.py +78 -0
  151. holder/generated/models/ai_run_create_request.py +133 -0
  152. holder/generated/models/ai_run_create_request_context.py +46 -0
  153. holder/generated/models/ai_run_event_data.py +146 -0
  154. holder/generated/models/ai_run_get_response.py +76 -0
  155. holder/generated/models/ai_run_item.py +328 -0
  156. holder/generated/models/ai_run_item_policy_trace_type_0.py +46 -0
  157. holder/generated/models/ai_run_list_response.py +84 -0
  158. holder/generated/models/ai_status_data.py +186 -0
  159. holder/generated/models/ai_status_pull_job.py +130 -0
  160. holder/generated/models/ai_status_response.py +76 -0
  161. holder/generated/models/ai_thread.py +116 -0
  162. holder/generated/models/ai_thread_create_request.py +119 -0
  163. holder/generated/models/ai_thread_create_response.py +78 -0
  164. holder/generated/models/ai_thread_create_response_data.py +62 -0
  165. holder/generated/models/ai_thread_get_response.py +76 -0
  166. holder/generated/models/ai_thread_list_response.py +84 -0
  167. holder/generated/models/ai_thread_patch_response.py +78 -0
  168. holder/generated/models/ai_thread_patch_response_data.py +62 -0
  169. holder/generated/models/ai_thread_update_request.py +104 -0
  170. holder/generated/models/asset_import_job.py +141 -0
  171. holder/generated/models/asset_import_job_response.py +76 -0
  172. holder/generated/models/asset_import_job_status.py +13 -0
  173. holder/generated/models/asset_import_request.py +86 -0
  174. holder/generated/models/asset_placement.py +96 -0
  175. holder/generated/models/asset_placement_encoding.py +9 -0
  176. holder/generated/models/bye_response.py +62 -0
  177. holder/generated/models/calendar_card_activity.py +86 -0
  178. holder/generated/models/card_context_breadcrumb.py +113 -0
  179. holder/generated/models/card_context_breadcrumb_type.py +9 -0
  180. holder/generated/models/card_context_card.py +161 -0
  181. holder/generated/models/card_context_data.py +124 -0
  182. holder/generated/models/card_context_data_project.py +70 -0
  183. holder/generated/models/card_context_response.py +76 -0
  184. holder/generated/models/card_create_request.py +147 -0
  185. holder/generated/models/card_create_response.py +78 -0
  186. holder/generated/models/card_create_response_data.py +70 -0
  187. holder/generated/models/card_data.py +160 -0
  188. holder/generated/models/card_get_response.py +76 -0
  189. holder/generated/models/card_history_author.py +70 -0
  190. holder/generated/models/card_history_comparison_data.py +110 -0
  191. holder/generated/models/card_history_comparison_response.py +78 -0
  192. holder/generated/models/card_history_diff_line.py +100 -0
  193. holder/generated/models/card_history_diff_line_origin.py +10 -0
  194. holder/generated/models/card_history_entry.py +172 -0
  195. holder/generated/models/card_history_entry_kind.py +16 -0
  196. holder/generated/models/card_history_entry_saves_item.py +94 -0
  197. holder/generated/models/card_history_page_data.py +114 -0
  198. holder/generated/models/card_history_page_response.py +76 -0
  199. holder/generated/models/card_history_restore_response.py +80 -0
  200. holder/generated/models/card_history_restore_response_data.py +108 -0
  201. holder/generated/models/card_history_snapshot_response.py +80 -0
  202. holder/generated/models/card_history_snapshot_response_data.py +76 -0
  203. holder/generated/models/card_history_version.py +86 -0
  204. holder/generated/models/card_link.py +116 -0
  205. holder/generated/models/card_link_create_request.py +111 -0
  206. holder/generated/models/card_link_create_response.py +76 -0
  207. holder/generated/models/card_link_delete_request.py +80 -0
  208. holder/generated/models/card_link_list_response.py +84 -0
  209. holder/generated/models/card_list_item.py +161 -0
  210. holder/generated/models/card_list_response.py +84 -0
  211. holder/generated/models/card_move_intent.py +15 -0
  212. holder/generated/models/card_move_request.py +134 -0
  213. holder/generated/models/card_move_response.py +76 -0
  214. holder/generated/models/card_move_result.py +114 -0
  215. holder/generated/models/card_page.py +90 -0
  216. holder/generated/models/card_page_item.py +170 -0
  217. holder/generated/models/card_page_response.py +76 -0
  218. holder/generated/models/card_patch_response.py +78 -0
  219. holder/generated/models/card_patch_response_data.py +62 -0
  220. holder/generated/models/card_reference_candidate.py +84 -0
  221. holder/generated/models/card_reference_resolve_data.py +129 -0
  222. holder/generated/models/card_reference_resolve_data_match_kind.py +10 -0
  223. holder/generated/models/card_reference_resolve_data_status.py +10 -0
  224. holder/generated/models/card_reference_resolve_request.py +82 -0
  225. holder/generated/models/card_reference_resolve_request_scope.py +10 -0
  226. holder/generated/models/card_reference_resolve_response.py +78 -0
  227. holder/generated/models/card_tag_mutation_request.py +70 -0
  228. holder/generated/models/card_tag_mutation_response.py +78 -0
  229. holder/generated/models/card_tag_mutation_result.py +88 -0
  230. holder/generated/models/card_tag_mutation_result_outcome.py +12 -0
  231. holder/generated/models/card_update_request.py +122 -0
  232. holder/generated/models/caste_info.py +85 -0
  233. holder/generated/models/caste_info_name.py +12 -0
  234. holder/generated/models/caste_recommended_model.py +143 -0
  235. holder/generated/models/caste_recommended_model_required_caste.py +12 -0
  236. holder/generated/models/change_event_data.py +109 -0
  237. holder/generated/models/change_event_data_entity.py +14 -0
  238. holder/generated/models/delete_cards_card_id_children.py +9 -0
  239. holder/generated/models/delete_cards_card_id_resources_body.py +70 -0
  240. holder/generated/models/error.py +70 -0
  241. holder/generated/models/error_response.py +76 -0
  242. holder/generated/models/event_checkpoint.py +92 -0
  243. holder/generated/models/event_checkpoint_git_revisions.py +66 -0
  244. holder/generated/models/event_checkpoint_history_urls.py +48 -0
  245. holder/generated/models/event_checkpoint_response.py +76 -0
  246. holder/generated/models/event_resync_data.py +116 -0
  247. holder/generated/models/event_resync_data_reason.py +9 -0
  248. holder/generated/models/get_ai_catalog_json_response_200.py +46 -0
  249. holder/generated/models/get_ai_messages_message_id_backlinks_include_deleted.py +9 -0
  250. holder/generated/models/get_ai_messages_message_id_links_include_deleted.py +9 -0
  251. holder/generated/models/get_cards_card_id_backlinks_include_deleted.py +9 -0
  252. holder/generated/models/get_cards_card_id_links_include_deleted.py +9 -0
  253. holder/generated/models/get_cards_context_order.py +10 -0
  254. holder/generated/models/get_cards_order.py +10 -0
  255. holder/generated/models/get_cards_view.py +9 -0
  256. holder/generated/models/get_git_providers_json_response_200.py +46 -0
  257. holder/generated/models/get_locations_location_id_response_200.py +46 -0
  258. holder/generated/models/get_ping_response_200.py +8 -0
  259. holder/generated/models/get_projects_order.py +13 -0
  260. holder/generated/models/get_projects_project_id_cards_order.py +9 -0
  261. holder/generated/models/get_projects_project_id_cards_parent_type_0.py +8 -0
  262. holder/generated/models/get_projects_project_id_history_cards_card_id_compare_mode.py +9 -0
  263. holder/generated/models/get_projects_project_id_history_kind.py +13 -0
  264. holder/generated/models/health_data.py +110 -0
  265. holder/generated/models/health_data_crypt_filter.py +78 -0
  266. holder/generated/models/health_response.py +76 -0
  267. holder/generated/models/hierarchy_repair.py +100 -0
  268. holder/generated/models/hierarchy_repair_move.py +76 -0
  269. holder/generated/models/hierarchy_repair_request.py +62 -0
  270. holder/generated/models/hierarchy_repair_response.py +76 -0
  271. holder/generated/models/local_model.py +91 -0
  272. holder/generated/models/location_binding_request.py +81 -0
  273. holder/generated/models/location_binding_request_values.py +46 -0
  274. holder/generated/models/milestone.py +177 -0
  275. holder/generated/models/milestone_create_request.py +135 -0
  276. holder/generated/models/milestone_list_response.py +84 -0
  277. holder/generated/models/milestone_remove_response.py +78 -0
  278. holder/generated/models/milestone_remove_result.py +79 -0
  279. holder/generated/models/milestone_response.py +76 -0
  280. holder/generated/models/milestone_update_request.py +116 -0
  281. holder/generated/models/patch_locations_location_id_body.py +90 -0
  282. holder/generated/models/patch_locations_location_id_body_configuration.py +46 -0
  283. holder/generated/models/post_ai_runner_pull_body.py +62 -0
  284. holder/generated/models/post_cards_card_id_resources_body.py +70 -0
  285. holder/generated/models/post_locations_response_201.py +46 -0
  286. holder/generated/models/project.py +197 -0
  287. holder/generated/models/project_calendar_response.py +78 -0
  288. holder/generated/models/project_calendar_response_data.py +136 -0
  289. holder/generated/models/project_create_request.py +177 -0
  290. holder/generated/models/project_create_request_privacy_mode.py +9 -0
  291. holder/generated/models/project_create_response.py +78 -0
  292. holder/generated/models/project_create_response_data.py +62 -0
  293. holder/generated/models/project_delete_response.py +78 -0
  294. holder/generated/models/project_delete_response_data.py +62 -0
  295. holder/generated/models/project_encryption_check.py +94 -0
  296. holder/generated/models/project_encryption_check_response.py +80 -0
  297. holder/generated/models/project_encryption_check_response_data.py +92 -0
  298. holder/generated/models/project_encryption_check_response_data_privacy_mode.py +9 -0
  299. holder/generated/models/project_get_response.py +76 -0
  300. holder/generated/models/project_git_push_request.py +95 -0
  301. holder/generated/models/project_git_push_response.py +78 -0
  302. holder/generated/models/project_git_push_response_data.py +199 -0
  303. holder/generated/models/project_git_push_response_data_status.py +15 -0
  304. holder/generated/models/project_git_sync_operation_response.py +80 -0
  305. holder/generated/models/project_git_sync_operation_response_data.py +163 -0
  306. holder/generated/models/project_git_sync_operation_response_data_pull.py +96 -0
  307. holder/generated/models/project_git_sync_operation_response_data_pull_status.py +11 -0
  308. holder/generated/models/project_git_sync_operation_response_data_push.py +118 -0
  309. holder/generated/models/project_git_sync_operation_response_data_push_status.py +15 -0
  310. holder/generated/models/project_git_sync_operation_response_data_status.py +9 -0
  311. holder/generated/models/project_git_sync_request.py +94 -0
  312. holder/generated/models/project_git_sync_status_response.py +80 -0
  313. holder/generated/models/project_git_sync_status_response_data.py +76 -0
  314. holder/generated/models/project_git_test_remote_request.py +96 -0
  315. holder/generated/models/project_git_test_remote_response.py +80 -0
  316. holder/generated/models/project_git_test_remote_response_data.py +151 -0
  317. holder/generated/models/project_git_test_remote_response_data_status.py +14 -0
  318. holder/generated/models/project_history_activity.py +138 -0
  319. holder/generated/models/project_history_affected_object.py +74 -0
  320. holder/generated/models/project_history_affected_object_kind.py +13 -0
  321. holder/generated/models/project_history_page_data.py +115 -0
  322. holder/generated/models/project_history_page_response.py +78 -0
  323. holder/generated/models/project_list_response.py +84 -0
  324. holder/generated/models/project_privacy_mode.py +9 -0
  325. holder/generated/models/project_recovery_token_export_request.py +62 -0
  326. holder/generated/models/project_recovery_token_export_response.py +80 -0
  327. holder/generated/models/project_recovery_token_export_response_data.py +78 -0
  328. holder/generated/models/project_recovery_token_import_request.py +70 -0
  329. holder/generated/models/project_recovery_token_import_response.py +80 -0
  330. holder/generated/models/project_recovery_token_import_response_data.py +62 -0
  331. holder/generated/models/project_sync.py +227 -0
  332. holder/generated/models/project_tag.py +70 -0
  333. holder/generated/models/project_tag_list_response.py +84 -0
  334. holder/generated/models/project_update_request.py +181 -0
  335. holder/generated/models/project_update_request_privacy_mode.py +9 -0
  336. holder/generated/models/project_update_response.py +78 -0
  337. holder/generated/models/project_update_response_data.py +75 -0
  338. holder/generated/models/put_locations_preferred_body.py +70 -0
  339. holder/generated/models/rebuild_data.py +94 -0
  340. holder/generated/models/rebuild_request.py +62 -0
  341. holder/generated/models/rebuild_response.py +76 -0
  342. holder/generated/models/recovery_token_import_auto_response.py +80 -0
  343. holder/generated/models/recovery_token_import_auto_response_data.py +141 -0
  344. holder/generated/models/recovery_token_import_auto_response_data_pull_status.py +10 -0
  345. holder/generated/models/reindex_data.py +62 -0
  346. holder/generated/models/reindex_response.py +76 -0
  347. holder/generated/models/resource.py +134 -0
  348. holder/generated/models/resource_asset.py +140 -0
  349. holder/generated/models/resource_attachment_response.py +80 -0
  350. holder/generated/models/resource_attachment_response_data.py +90 -0
  351. holder/generated/models/resource_attachment_response_data_outcome.py +11 -0
  352. holder/generated/models/resource_create_request.py +129 -0
  353. holder/generated/models/resource_delete_response.py +78 -0
  354. holder/generated/models/resource_delete_response_data.py +62 -0
  355. holder/generated/models/resource_list_response.py +134 -0
  356. holder/generated/models/resource_metadata.py +55 -0
  357. holder/generated/models/resource_response.py +76 -0
  358. holder/generated/models/resource_update_request.py +102 -0
  359. holder/generated/models/runner_capabilities_data.py +220 -0
  360. holder/generated/models/runner_capabilities_response.py +78 -0
  361. holder/generated/models/runner_pull_job.py +122 -0
  362. holder/generated/models/runner_pull_progress.py +86 -0
  363. holder/generated/models/runner_pull_start_response.py +78 -0
  364. holder/generated/models/runner_pull_start_response_data.py +78 -0
  365. holder/generated/models/runner_pull_status_response.py +76 -0
  366. holder/generated/models/search_card_item.py +102 -0
  367. holder/generated/models/search_cards_response.py +84 -0
  368. holder/generated/models/search_message_item.py +86 -0
  369. holder/generated/models/search_messages_response.py +84 -0
  370. holder/generated/models/storage_location.py +149 -0
  371. holder/generated/models/storage_location_configuration.py +48 -0
  372. holder/generated/models/storage_location_list_response.py +100 -0
  373. holder/generated/models/storage_location_provider.py +9 -0
  374. holder/generated/models/storage_location_request.py +118 -0
  375. holder/generated/models/storage_location_request_configuration.py +46 -0
  376. holder/generated/models/storage_location_request_provider.py +9 -0
  377. holder/generated/models/trash_delete_response.py +78 -0
  378. holder/generated/models/trash_delete_response_data.py +46 -0
  379. holder/generated/models/trash_item.py +211 -0
  380. holder/generated/models/trash_list_response.py +84 -0
  381. holder/generated/types.py +54 -0
  382. holder/library.py +15 -0
  383. holder/models/__init__.py +15 -0
  384. holder/models/card.py +272 -0
  385. holder/models/project.py +207 -0
  386. holder/models/search.py +66 -0
  387. holder/models/tag.py +117 -0
  388. holder/py.typed +0 -0
  389. holder_python-0.2.5.dist-info/METADATA +120 -0
  390. holder_python-0.2.5.dist-info/RECORD +391 -0
  391. holder_python-0.2.5.dist-info/WHEEL +4 -0
holder/__init__.py ADDED
@@ -0,0 +1,21 @@
1
+ """Holder Python Library - control your knowledge using Python."""
2
+
3
+ from .client import Holder
4
+ from .models import (
5
+ Card,
6
+ CardSearchResult,
7
+ HierarchyRepair,
8
+ Project,
9
+ RepairedCard,
10
+ Tag,
11
+ )
12
+
13
+ __all__ = [
14
+ "Card",
15
+ "CardSearchResult",
16
+ "HierarchyRepair",
17
+ "Holder",
18
+ "Project",
19
+ "RepairedCard",
20
+ "Tag",
21
+ ]
holder/client.py ADDED
@@ -0,0 +1,182 @@
1
+ """Connection and request handling for the synchronous Holder client."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import subprocess
7
+ from collections.abc import Iterator
8
+ from typing import Any, Self
9
+
10
+ import httpx
11
+
12
+ from .exceptions import (
13
+ APIError,
14
+ AuthenticationError,
15
+ ClosedError,
16
+ ConnectionError,
17
+ NotFoundError,
18
+ ProtocolError,
19
+ )
20
+ from .generated import AuthenticatedClient
21
+ from .generated.models import ErrorResponse
22
+ from .models.project import Project, ProjectCollection
23
+
24
+
25
+ class Holder:
26
+ """Control your local knowledge, or connect with an explicit URL/token pair.
27
+
28
+ Use as a context manager. Collection queries run only when consumed.
29
+ Local discovery requires holderctl on PATH (or the holderctl argument).
30
+ """
31
+
32
+ def __init__(
33
+ self,
34
+ *,
35
+ url: str | None = None,
36
+ token: str | None = None,
37
+ holderctl: str = "holderctl",
38
+ timeout: float | httpx.Timeout = 10.0,
39
+ transport: httpx.BaseTransport | None = None,
40
+ ) -> None:
41
+ if (url is None) != (token is None):
42
+ raise ValueError("Supply url and token together, or omit both")
43
+ self._url = url
44
+ self._token = token
45
+ self._local = url is None
46
+ self._holderctl = holderctl
47
+ self._timeout = timeout
48
+ self._transport = transport
49
+ self._client: AuthenticatedClient | None = None
50
+ self._closed = False
51
+ self.projects = ProjectCollection(self)
52
+
53
+ def _command(self, *args: str) -> str:
54
+ try:
55
+ result = subprocess.run(
56
+ [self._holderctl, *args],
57
+ capture_output=True,
58
+ text=True,
59
+ timeout=65,
60
+ check=False,
61
+ )
62
+ except (OSError, subprocess.TimeoutExpired):
63
+ raise ConnectionError(
64
+ "Could not run holderctl; install Holder or supply url and token"
65
+ ) from None
66
+ if result.returncode:
67
+ # CLI output and stderr may contain local credentials; never echo them.
68
+ raise ConnectionError(
69
+ f"holderctl {args[0]} failed (exit {result.returncode})"
70
+ )
71
+ return result.stdout
72
+
73
+ def _discover(self) -> None:
74
+ try:
75
+ result = json.loads(
76
+ self._command(
77
+ "ensure",
78
+ "--json",
79
+ "--api-min",
80
+ "0.1",
81
+ "--api-max-exclusive",
82
+ "1.0",
83
+ )
84
+ )
85
+ url = result["daemon"]["url"]
86
+ if result.get("ok") is not True or not isinstance(url, str) or not url:
87
+ raise ValueError
88
+ token = self._command("token").strip()
89
+ if not token:
90
+ raise ValueError
91
+ except (ValueError, KeyError, TypeError):
92
+ raise ConnectionError(
93
+ "holderctl did not return valid daemon connection details"
94
+ ) from None
95
+ self._url, self._token = url, token
96
+
97
+ def _connect(self) -> None:
98
+ if self._local:
99
+ self._discover()
100
+ self._client = AuthenticatedClient(
101
+ base_url=self._url,
102
+ token=self._token,
103
+ timeout=httpx.Timeout(self._timeout),
104
+ httpx_args={"transport": self._transport} if self._transport else {},
105
+ )
106
+ self._client.__enter__()
107
+
108
+ def __enter__(self) -> Self:
109
+ if self._closed or self._client is not None:
110
+ raise ClosedError("Holder contexts cannot be reused")
111
+ self._connect()
112
+ return self
113
+
114
+ def __exit__(self, *args: object) -> None:
115
+ self._closed = True
116
+ if self._client is not None:
117
+ self._client.__exit__(*args)
118
+
119
+ def _active(self) -> AuthenticatedClient:
120
+ if self._closed or self._client is None:
121
+ raise ClosedError("Use Holder inside with Holder(...) as h")
122
+ return self._client
123
+
124
+ def __iter__(self) -> Iterator[Project]:
125
+ return iter(self.projects)
126
+
127
+ def _call(
128
+ self, endpoint: Any, *args: Any, write: bool = False, **kwargs: Any
129
+ ) -> Any:
130
+ client = self._active()
131
+ for attempt in range(2):
132
+ try:
133
+ response = endpoint.sync_detailed(*args, client=client, **kwargs)
134
+ except httpx.TransportError:
135
+ raise ConnectionError(
136
+ "Could not communicate with the Holder daemon"
137
+ ) from None
138
+ except (ValueError, KeyError, TypeError):
139
+ raise ProtocolError("Invalid daemon response") from None
140
+ if (
141
+ response.status_code == 401
142
+ and self._local
143
+ and not write
144
+ and attempt == 0
145
+ ):
146
+ self._discover()
147
+ # Preserve the pool and custom transport while updating a restarted daemon.
148
+ http_client = client.get_httpx_client()
149
+ http_client.base_url = self._url
150
+ http_client.headers["Authorization"] = f"Bearer {self._token}"
151
+ continue
152
+ if isinstance(response.parsed, ErrorResponse):
153
+ error = response.parsed.error
154
+ error_type = {401: AuthenticationError, 404: NotFoundError}.get(
155
+ response.status_code, APIError
156
+ )
157
+ raise error_type(int(response.status_code), error.code, error.message)
158
+ if response.status_code >= 400:
159
+ error_type = {401: AuthenticationError, 404: NotFoundError}.get(
160
+ response.status_code, APIError
161
+ )
162
+ # Undocumented statuses are not parsed by generated endpoints.
163
+ try:
164
+ error = ErrorResponse.from_dict(json.loads(response.content)).error
165
+ except (ValueError, KeyError, TypeError, AttributeError):
166
+ error = None
167
+ if error is not None:
168
+ raise error_type(
169
+ int(response.status_code), error.code, error.message
170
+ )
171
+ raise error_type(
172
+ int(response.status_code), "http_error", "Daemon request failed"
173
+ )
174
+ if (
175
+ response.parsed is None
176
+ or getattr(response.parsed, "ok", None) is not True
177
+ ):
178
+ raise ProtocolError("Invalid daemon success envelope")
179
+ return response.parsed.data
180
+ raise AuthenticationError(
181
+ 401, "unauthorized", "Daemon rejected refreshed credentials"
182
+ )
holder/collections.py ADDED
@@ -0,0 +1,70 @@
1
+ """Shared lazy iteration and caching for Holder collections."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Iterator
6
+ from typing import TYPE_CHECKING, Any, Generic, Self, TypeVar
7
+
8
+ if TYPE_CHECKING:
9
+ from .client import Holder
10
+
11
+ T = TypeVar("T")
12
+
13
+
14
+ class Collection(Generic[T]):
15
+ """Lazy query with a progressively populated cache and independent iterators."""
16
+
17
+ def __init__(self, holder: Holder, query: dict[str, Any] | None = None) -> None:
18
+ self._holder = holder
19
+ self._query = dict(query or {})
20
+ self._cache: list[T] = []
21
+ self._next: int | str | None = None
22
+ self._done = False
23
+ self._generation = 0
24
+
25
+ def __iter__(self) -> Iterator[T]:
26
+ self._holder._active()
27
+ generation = self._generation
28
+ index = 0
29
+ while True:
30
+ self._holder._active()
31
+ if generation != self._generation:
32
+ raise RuntimeError("Collection was refreshed during iteration")
33
+ if index == len(self._cache):
34
+ if self._done:
35
+ return
36
+ rows, next_cursor = self._fetch_page()
37
+ self._cache.extend(rows)
38
+ self._next = next_cursor
39
+ self._done = next_cursor is None
40
+ if not rows:
41
+ return
42
+ yield self._cache[index]
43
+ index += 1
44
+
45
+ def __getitem__(self, index: int) -> T:
46
+ if not isinstance(index, int):
47
+ raise TypeError("Collection indices must be integers")
48
+ if index < 0:
49
+ raise IndexError("Collection indices must be nonnegative")
50
+ for position, item in enumerate(self):
51
+ if position == index:
52
+ return item
53
+ raise IndexError("Collection index out of range")
54
+
55
+ def all(self) -> Self:
56
+ return self._clone(self._query)
57
+
58
+ def refresh(self) -> Self:
59
+ self._holder._active()
60
+ self._cache.clear()
61
+ self._next = None
62
+ self._done = False
63
+ self._generation += 1
64
+ return self
65
+
66
+ def _clone(self, query: dict[str, Any]) -> Self:
67
+ raise NotImplementedError
68
+
69
+ def _fetch_page(self) -> tuple[list[T], int | str | None]:
70
+ raise NotImplementedError
holder/events.py ADDED
@@ -0,0 +1,238 @@
1
+ """Incremental asyncio transport for GET /events; no entity or recovery policy."""
2
+
3
+ import asyncio
4
+ import json
5
+ import math
6
+ from collections.abc import AsyncIterator
7
+ from contextlib import asynccontextmanager
8
+ from dataclasses import dataclass
9
+ from typing import Any, Self
10
+
11
+ import httpx
12
+
13
+ from .generated import AuthenticatedClient, Client
14
+
15
+
16
+ @dataclass(frozen=True)
17
+ class Event:
18
+ """A complete SSE frame. IDs are opaque; data is retained as UTF-8 text."""
19
+
20
+ event: str
21
+ data: str
22
+ id: str | None = None
23
+
24
+ def json(self) -> Any:
25
+ return json.loads(self.data)
26
+
27
+
28
+ async def _lines(response: httpx.Response) -> AsyncIterator[str]:
29
+ # SSE recognizes only CR and LF, not Unicode separators inside JSON strings.
30
+ response.encoding = "utf-8"
31
+ line: list[str] = []
32
+ after_cr = False
33
+ async for chunk in response.aiter_text():
34
+ for character in chunk:
35
+ if after_cr and character == "\n":
36
+ after_cr = False
37
+ continue
38
+ after_cr = character == "\r"
39
+ if character in "\r\n":
40
+ yield "".join(line)
41
+ line.clear()
42
+ else:
43
+ line.append(character)
44
+ # No synthetic newline at EOF: partial frames must replay after reconnect.
45
+
46
+
47
+ async def _frames(response: httpx.Response) -> AsyncIterator[Event]:
48
+ data: list[str] = []
49
+ name = "message"
50
+ event_id = None
51
+ first = True
52
+ async for line in _lines(response):
53
+ if first:
54
+ line = line.removeprefix("\ufeff")
55
+ first = False
56
+ if not line:
57
+ if data:
58
+ yield Event(name, "\n".join(data), event_id)
59
+ data, name, event_id = [], "message", None
60
+ continue
61
+ if line.startswith(":"):
62
+ continue
63
+ field, _, value = line.partition(":")
64
+ value = value.removeprefix(" ")
65
+ if field == "data":
66
+ data.append(value)
67
+ elif field == "event":
68
+ name = value or "message"
69
+ elif field == "id" and "\x00" not in value:
70
+ event_id = value
71
+ # An unterminated frame is not delivered or acknowledged on disconnect.
72
+
73
+
74
+ class _ClosingStream(httpx.AsyncByteStream):
75
+ """Protect cleanup both at HTTPX's automatic EOF close and context exit."""
76
+
77
+ def __init__(self, stream: httpx.AsyncByteStream) -> None:
78
+ self._stream = stream
79
+ self._cleanup: asyncio.Task[None] | None = None
80
+
81
+ async def __aiter__(self) -> AsyncIterator[bytes]:
82
+ async for chunk in self._stream:
83
+ yield chunk
84
+
85
+ async def aclose(self) -> None:
86
+ if self._cleanup is None:
87
+ self._cleanup = asyncio.create_task(self._stream.aclose())
88
+ cleanup = self._cleanup
89
+ # A second task cancellation must not interrupt socket/pool cleanup.
90
+ cancelled = False
91
+ while not cleanup.done():
92
+ try:
93
+ await asyncio.shield(cleanup)
94
+ except asyncio.CancelledError:
95
+ cancelled = True
96
+ cleanup.result()
97
+ if cancelled:
98
+ raise asyncio.CancelledError
99
+
100
+
101
+ @asynccontextmanager
102
+ async def _connection(
103
+ transport: httpx.AsyncClient, **kwargs: Any
104
+ ) -> AsyncIterator[httpx.Response]:
105
+ request = transport.build_request("GET", "/events", **kwargs)
106
+ response = await transport.send(request, stream=True)
107
+ response.stream = _ClosingStream(response.stream)
108
+ try:
109
+ yield response
110
+ finally:
111
+ await response.aclose()
112
+
113
+
114
+ class ChangeStream:
115
+ """Context-managed asyncio iterator over daemon invalidations.
116
+
117
+ Pass the same project filter used for /events/cursor. cursor advances when
118
+ the consumer requests the next event (after processing the previous one).
119
+ Persist cursor for a later subscription. A ready frame uses its ID, never
120
+ the newer checkpoint in its data. resync_required is yielded once and stops
121
+ iteration without advancing cursor: refresh state before subscribing again.
122
+
123
+ EOF and HTTPX transport errors reconnect after reconnect_delay seconds.
124
+ HTTP errors and invalid content types propagate without retry. Read timeout
125
+ defaults to disabled; connection/write/pool timeouts remain bounded.
126
+ Cancellation, aclose(), and context exit close the active response. The
127
+ supplied generated client remains caller-owned. Use asyncio task cancellation
128
+ or aclose() to interrupt a pending read; only one reader is supported.
129
+ """
130
+
131
+ def __init__(
132
+ self,
133
+ client: Client | AuthenticatedClient,
134
+ *,
135
+ project_id: str | None = None,
136
+ last_revision: str | None = None,
137
+ last_event_id: str | None = None,
138
+ reconnect_delay: float = 1.0,
139
+ timeout: httpx.Timeout | None = None,
140
+ ) -> None:
141
+ if not math.isfinite(reconnect_delay) or reconnect_delay < 0:
142
+ raise ValueError("reconnect_delay must be finite and nonnegative")
143
+ self.cursor = last_event_id
144
+ self._client = client
145
+ self._params = {
146
+ key: value
147
+ for key, value in {
148
+ "project_id": project_id,
149
+ "last_revision": last_revision,
150
+ }.items()
151
+ if value is not None
152
+ }
153
+ self._delay = reconnect_delay
154
+ self._timeout = timeout if timeout is not None else httpx.Timeout(10, read=None)
155
+ self._iterator = self._events()
156
+ self._pending: asyncio.Task[Event] | None = None
157
+ self._entered = False
158
+ self._closed = False
159
+
160
+ async def __aenter__(self) -> Self:
161
+ if self._entered or self._closed:
162
+ raise RuntimeError("ChangeStream contexts cannot be reused")
163
+ self._entered = True
164
+ return self
165
+
166
+ async def __aexit__(self, *args: object) -> None:
167
+ await self.aclose()
168
+
169
+ def __aiter__(self) -> Self:
170
+ return self
171
+
172
+ async def __anext__(self) -> Event:
173
+ if not self._entered:
174
+ raise RuntimeError("Use ChangeStream inside async with")
175
+ if self._closed:
176
+ raise StopAsyncIteration
177
+ if self._pending is not None:
178
+ raise RuntimeError("ChangeStream supports only one reader")
179
+ pending = asyncio.create_task(anext(self._iterator))
180
+ self._pending = pending
181
+ try:
182
+ return await pending
183
+ except BaseException:
184
+ self._closed = True
185
+ await self._iterator.aclose()
186
+ raise
187
+ finally:
188
+ self._pending = None
189
+
190
+ async def aclose(self) -> None:
191
+ """Stop iteration, including a blocked read or reconnect delay."""
192
+ self._closed = True
193
+ pending = self._pending
194
+ if pending is not None:
195
+ pending.cancel()
196
+ try:
197
+ await pending
198
+ except (asyncio.CancelledError, StopAsyncIteration):
199
+ pass
200
+ await self._iterator.aclose()
201
+
202
+ async def _events(self) -> AsyncIterator[Event]:
203
+ transport = self._client.get_async_httpx_client()
204
+ while not self._closed:
205
+ headers = {
206
+ "Accept": "text/event-stream",
207
+ "Last-Event-ID": self.cursor or "",
208
+ }
209
+ terminal = None
210
+ try:
211
+ async with _connection(
212
+ transport,
213
+ params=self._params,
214
+ headers=headers,
215
+ timeout=self._timeout,
216
+ ) as response:
217
+ response.raise_for_status()
218
+ if (
219
+ response.headers.get("content-type", "")
220
+ .split(";")[0]
221
+ .strip()
222
+ .lower()
223
+ != "text/event-stream"
224
+ ):
225
+ raise ValueError("Expected text/event-stream response")
226
+ async for event in _frames(response):
227
+ if event.event == "resync_required":
228
+ terminal = event
229
+ break
230
+ yield event
231
+ if event.id is not None:
232
+ self.cursor = event.id or None
233
+ except httpx.TransportError:
234
+ pass
235
+ if terminal is not None:
236
+ yield terminal
237
+ return
238
+ await asyncio.sleep(self._delay)
holder/exceptions.py ADDED
@@ -0,0 +1,35 @@
1
+ """Errors raised by the high-level Holder API."""
2
+
3
+
4
+ class HolderError(Exception):
5
+ """Base error for the high-level client."""
6
+
7
+
8
+ class ConnectionError(HolderError):
9
+ """Daemon discovery, startup or transport failed."""
10
+
11
+
12
+ class ClosedError(HolderError):
13
+ """The client is outside its active context."""
14
+
15
+
16
+ class ProtocolError(HolderError):
17
+ """The daemon returned an invalid or incompatible response."""
18
+
19
+
20
+ class APIError(HolderError):
21
+ """A failed daemon request, with its status and error envelope."""
22
+
23
+ def __init__(self, status: int, code: str, message: str) -> None:
24
+ self.status = status
25
+ self.code = code
26
+ self.message = message
27
+ super().__init__(f"{status} {code}: {message}")
28
+
29
+
30
+ class AuthenticationError(APIError):
31
+ """The daemon rejected the credentials."""
32
+
33
+
34
+ class NotFoundError(APIError):
35
+ """The requested object was not found within its scope."""
@@ -0,0 +1,8 @@
1
+ """A client library for accessing Holder API"""
2
+
3
+ from .client import AuthenticatedClient, Client
4
+
5
+ __all__ = (
6
+ "AuthenticatedClient",
7
+ "Client",
8
+ )
@@ -0,0 +1 @@
1
+ """Contains methods for accessing the API"""
@@ -0,0 +1 @@
1
+ """Contains endpoint functions for accessing the API"""