mostlyright-data 0.9.0__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 (314) hide show
  1. mostlyright/data_harness/__init__.py +158 -0
  2. mostlyright/data_harness/acquisition/__init__.py +55 -0
  3. mostlyright/data_harness/acquisition/http.py +2773 -0
  4. mostlyright/data_harness/acquisition/parsing.py +809 -0
  5. mostlyright/data_harness/acquisition/ranges.py +495 -0
  6. mostlyright/data_harness/acquisition/result_download.py +360 -0
  7. mostlyright/data_harness/acquisition/retention_admission.py +248 -0
  8. mostlyright/data_harness/acquisition/sandbox.py +4888 -0
  9. mostlyright/data_harness/acquisition/url_policy.py +530 -0
  10. mostlyright/data_harness/agent_runtime.py +2743 -0
  11. mostlyright/data_harness/assets/logo-ink.svg +31 -0
  12. mostlyright/data_harness/backends/__init__.py +28 -0
  13. mostlyright/data_harness/backends/pandas_backend.py +350 -0
  14. mostlyright/data_harness/backends/polars_backend.py +366 -0
  15. mostlyright/data_harness/backends/protocol.py +124 -0
  16. mostlyright/data_harness/backends/reference.py +83 -0
  17. mostlyright/data_harness/backends/registry.py +55 -0
  18. mostlyright/data_harness/backends/restrictions.py +126 -0
  19. mostlyright/data_harness/canonical.py +333 -0
  20. mostlyright/data_harness/catalog_job.py +625 -0
  21. mostlyright/data_harness/cli.py +5398 -0
  22. mostlyright/data_harness/contracts.py +53 -0
  23. mostlyright/data_harness/coordinator.py +1307 -0
  24. mostlyright/data_harness/deploy.py +924 -0
  25. mostlyright/data_harness/deploy_target.py +312 -0
  26. mostlyright/data_harness/deployment_evidence.py +1067 -0
  27. mostlyright/data_harness/event_presentation.py +576 -0
  28. mostlyright/data_harness/events.py +2152 -0
  29. mostlyright/data_harness/fast_delimited.py +239 -0
  30. mostlyright/data_harness/fleet.py +237 -0
  31. mostlyright/data_harness/formats.py +236 -0
  32. mostlyright/data_harness/governors.py +1163 -0
  33. mostlyright/data_harness/hosted_bootstrap.py +972 -0
  34. mostlyright/data_harness/hosted_crawler.py +1115 -0
  35. mostlyright/data_harness/hosted_crawler_container_smoke.py +351 -0
  36. mostlyright/data_harness/hosted_crawler_fetch.py +423 -0
  37. mostlyright/data_harness/hosted_crawler_job.py +1277 -0
  38. mostlyright/data_harness/hosted_crawler_protocol.py +676 -0
  39. mostlyright/data_harness/hosted_dataset.py +1500 -0
  40. mostlyright/data_harness/hosted_deploy.py +3037 -0
  41. mostlyright/data_harness/hosted_handoff.py +62 -0
  42. mostlyright/data_harness/hosted_ingestion_contract.py +504 -0
  43. mostlyright/data_harness/hosted_ingestion_job.py +356 -0
  44. mostlyright/data_harness/hosted_ingestion_job_smoke.py +40 -0
  45. mostlyright/data_harness/hosted_session_container_smoke.py +194 -0
  46. mostlyright/data_harness/hosted_session_worker.py +3554 -0
  47. mostlyright/data_harness/hosted_session_worker_job_smoke.py +46 -0
  48. mostlyright/data_harness/hosted_worker.py +6784 -0
  49. mostlyright/data_harness/ingestion/__init__.py +56 -0
  50. mostlyright/data_harness/ingestion/contracts.py +461 -0
  51. mostlyright/data_harness/ingestion/faults.py +42 -0
  52. mostlyright/data_harness/ingestion/gcs_store.py +1162 -0
  53. mostlyright/data_harness/ingestion/spool.py +130 -0
  54. mostlyright/data_harness/ingestion/store.py +885 -0
  55. mostlyright/data_harness/key_seam.py +434 -0
  56. mostlyright/data_harness/linux_process_boundary.py +262 -0
  57. mostlyright/data_harness/local_contracts.py +2880 -0
  58. mostlyright/data_harness/local_search/__init__.py +5 -0
  59. mostlyright/data_harness/local_search/build_index.py +1087 -0
  60. mostlyright/data_harness/local_search/contracts.py +920 -0
  61. mostlyright/data_harness/local_search/query_trace.py +266 -0
  62. mostlyright/data_harness/local_search/retrieval.py +700 -0
  63. mostlyright/data_harness/local_search/sealed.py +474 -0
  64. mostlyright/data_harness/local_search/service.py +784 -0
  65. mostlyright/data_harness/nbrender/CONTRACT.md +212 -0
  66. mostlyright/data_harness/nbrender/__init__.py +12 -0
  67. mostlyright/data_harness/nbrender/chrome.py +359 -0
  68. mostlyright/data_harness/nbrender/code_body.py +266 -0
  69. mostlyright/data_harness/nbrender/document.py +407 -0
  70. mostlyright/data_harness/nbrender/frame.py +275 -0
  71. mostlyright/data_harness/nbrender/interactive.py +337 -0
  72. mostlyright/data_harness/nbrender/markdown_body.py +477 -0
  73. mostlyright/data_harness/nbrender/mr_components.py +134 -0
  74. mostlyright/data_harness/nbrender/outputs_data.py +595 -0
  75. mostlyright/data_harness/nbrender/outputs_rich.py +906 -0
  76. mostlyright/data_harness/nbrender/outputs_source.py +260 -0
  77. mostlyright/data_harness/nbrender/outputs_stage.py +176 -0
  78. mostlyright/data_harness/nbrender/outputs_text.py +400 -0
  79. mostlyright/data_harness/nbrender/parse.py +394 -0
  80. mostlyright/data_harness/nbrender/status.py +40 -0
  81. mostlyright/data_harness/nbrender/tokens.py +1295 -0
  82. mostlyright/data_harness/notebook.py +1710 -0
  83. mostlyright/data_harness/offline.py +2049 -0
  84. mostlyright/data_harness/operation_registry.py +1007 -0
  85. mostlyright/data_harness/operator_setup.py +239 -0
  86. mostlyright/data_harness/pipeline.py +6428 -0
  87. mostlyright/data_harness/plan_graph.py +2026 -0
  88. mostlyright/data_harness/preparation/__init__.py +104 -0
  89. mostlyright/data_harness/preparation/contracts.py +1017 -0
  90. mostlyright/data_harness/preparation/engine.py +221 -0
  91. mostlyright/data_harness/preparation/errors.py +14 -0
  92. mostlyright/data_harness/preparation/gates.py +751 -0
  93. mostlyright/data_harness/preparation/joins.py +574 -0
  94. mostlyright/data_harness/preparation/profile.py +384 -0
  95. mostlyright/data_harness/preparation/table.py +217 -0
  96. mostlyright/data_harness/preparation/transforms.py +568 -0
  97. mostlyright/data_harness/progress_events.py +534 -0
  98. mostlyright/data_harness/readers/__init__.py +46 -0
  99. mostlyright/data_harness/readers/containers.py +963 -0
  100. mostlyright/data_harness/readers/contracts.py +542 -0
  101. mostlyright/data_harness/readers/delimited.py +257 -0
  102. mostlyright/data_harness/readers/grib2/__init__.py +33 -0
  103. mostlyright/data_harness/readers/grib2/admission.py +722 -0
  104. mostlyright/data_harness/readers/grib2/decode.py +1009 -0
  105. mostlyright/data_harness/readers/grib2/geometry.py +1133 -0
  106. mostlyright/data_harness/readers/grib2/portable_math.py +501 -0
  107. mostlyright/data_harness/readers/json_tabular.py +485 -0
  108. mostlyright/data_harness/readers/registry.py +514 -0
  109. mostlyright/data_harness/readers/samples/README.md +110 -0
  110. mostlyright/data_harness/readers/samples/archive.gzip/1.0.0/cities_one_stream/cities.csv.gz +0 -0
  111. mostlyright/data_harness/readers/samples/archive.gzip/1.0.0/cities_one_stream/expected.json +24 -0
  112. mostlyright/data_harness/readers/samples/archive.gzip/1.1.0/cities_one_stream/cities.csv.gz +0 -0
  113. mostlyright/data_harness/readers/samples/archive.gzip/1.1.0/cities_one_stream/expected.json +24 -0
  114. mostlyright/data_harness/readers/samples/archive.tar/1.0.0/cities_beside_a_directory_entry/cities.tar +0 -0
  115. mostlyright/data_harness/readers/samples/archive.tar/1.0.0/cities_beside_a_directory_entry/expected.json +24 -0
  116. mostlyright/data_harness/readers/samples/archive.tar/1.1.0/cities_beside_a_directory_entry/cities.tar +0 -0
  117. mostlyright/data_harness/readers/samples/archive.tar/1.1.0/cities_beside_a_directory_entry/expected.json +24 -0
  118. mostlyright/data_harness/readers/samples/archive.zip/1.0.0/cities_beside_a_second_member/cities.zip +0 -0
  119. mostlyright/data_harness/readers/samples/archive.zip/1.0.0/cities_beside_a_second_member/expected.json +25 -0
  120. mostlyright/data_harness/readers/samples/archive.zip/1.1.0/dwd_semicolon_station_member/dwd-station.zip +0 -0
  121. mostlyright/data_harness/readers/samples/archive.zip/1.1.0/dwd_semicolon_station_member/expected.json +25 -0
  122. mostlyright/data_harness/readers/samples/archive.zip/1.2.0/dwd_semicolon_station_member/dwd-station.zip +0 -0
  123. mostlyright/data_harness/readers/samples/archive.zip/1.2.0/dwd_semicolon_station_member/expected.json +25 -0
  124. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/an_ordinary_comma_separated_table/cities.csv +3 -0
  125. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/an_ordinary_comma_separated_table/expected.json +23 -0
  126. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/quoted_fields_holding_the_delimiter/cities.tsv +5 -0
  127. mostlyright/data_harness/readers/samples/delimited_text/1.0.0/quoted_fields_holding_the_delimiter/expected.json +25 -0
  128. mostlyright/data_harness/readers/samples/delimited_text/1.1.0/an_hourly_observation_table_served_as_plain_text/expected.json +30 -0
  129. mostlyright/data_harness/readers/samples/delimited_text/1.1.0/an_hourly_observation_table_served_as_plain_text/observations.csv +5 -0
  130. mostlyright/data_harness/readers/samples/json.tabular/1.0.0/nested_hourly_observations/expected.json +44 -0
  131. mostlyright/data_harness/readers/samples/json.tabular/1.0.0/nested_hourly_observations/stations.json +1 -0
  132. mostlyright/data_harness/readers/samples/json.tabular/1.1.0/an_observation_stream_served_as_plain_text/expected.json +48 -0
  133. mostlyright/data_harness/readers/samples/json.tabular/1.1.0/an_observation_stream_served_as_plain_text/observations.ndjson +4 -0
  134. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/an_ordinary_table_beside_a_second_sheet/cities.xlsx +0 -0
  135. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/an_ordinary_table_beside_a_second_sheet/expected.json +24 -0
  136. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/shares_the_workbook_had_already_computed/expected.json +27 -0
  137. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.0.0/shares_the_workbook_had_already_computed/shares.xlsx +0 -0
  138. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.1.0/shares_the_workbook_had_already_computed/expected.json +27 -0
  139. mostlyright/data_harness/readers/samples/spreadsheet.xlsx/1.1.0/shares_the_workbook_had_already_computed/shares.xlsx +0 -0
  140. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/README.md +20 -0
  141. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/gfs_2m_temperature/expected.json +55 -0
  142. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/gfs_2m_temperature/gfs-2m-temperature.grib2 +0 -0
  143. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_2m_temperature/expected.json +54 -0
  144. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_2m_temperature/hrrr-2m-temperature.grib2 +0 -0
  145. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_categorical_rain/expected.json +54 -0
  146. mostlyright/data_harness/readers/samples/weather.grib2/1.0.0/hrrr_categorical_rain/hrrr-categorical-rain.grib2 +0 -0
  147. mostlyright/data_harness/readers/samples/weather.grib2/2.0.0/hrrr_2m_temperature/expected.json +54 -0
  148. mostlyright/data_harness/readers/samples/weather.grib2/2.0.0/hrrr_2m_temperature/hrrr-2m-temperature.grib2 +0 -0
  149. mostlyright/data_harness/readers/samples.py +582 -0
  150. mostlyright/data_harness/readers/spreadsheet.py +803 -0
  151. mostlyright/data_harness/readers/tabular.py +510 -0
  152. mostlyright/data_harness/recipe.py +5321 -0
  153. mostlyright/data_harness/repair/__init__.py +78 -0
  154. mostlyright/data_harness/repair/adapters.py +274 -0
  155. mostlyright/data_harness/repair/contracts.py +872 -0
  156. mostlyright/data_harness/repair/coordinator.py +1099 -0
  157. mostlyright/data_harness/repair/errors.py +16 -0
  158. mostlyright/data_harness/review.py +2533 -0
  159. mostlyright/data_harness/rowset.py +283 -0
  160. mostlyright/data_harness/serving.py +1975 -0
  161. mostlyright/data_harness/serving_edge.py +590 -0
  162. mostlyright/data_harness/serving_http.py +1031 -0
  163. mostlyright/data_harness/session_probes.py +759 -0
  164. mostlyright/data_harness/signing.py +101 -0
  165. mostlyright/data_harness/source_discovery.py +898 -0
  166. mostlyright/data_harness/sources/__init__.py +209 -0
  167. mostlyright/data_harness/sources/_adapter_steps.py +213 -0
  168. mostlyright/data_harness/sources/adapters.py +1214 -0
  169. mostlyright/data_harness/sources/cadence.py +1428 -0
  170. mostlyright/data_harness/sources/cadence_emission.py +453 -0
  171. mostlyright/data_harness/sources/cadence_history.py +546 -0
  172. mostlyright/data_harness/sources/catalog/__init__.py +17 -0
  173. mostlyright/data_harness/sources/catalog/admission.py +477 -0
  174. mostlyright/data_harness/sources/catalog/authoring.py +1701 -0
  175. mostlyright/data_harness/sources/catalog/authoring_policy.py +701 -0
  176. mostlyright/data_harness/sources/catalog/authoring_shards.py +1217 -0
  177. mostlyright/data_harness/sources/catalog/bounded_io.py +231 -0
  178. mostlyright/data_harness/sources/catalog/channel.py +523 -0
  179. mostlyright/data_harness/sources/catalog/channel_client.py +296 -0
  180. mostlyright/data_harness/sources/catalog/contracts.py +825 -0
  181. mostlyright/data_harness/sources/catalog/coverage.py +137 -0
  182. mostlyright/data_harness/sources/catalog/delta.py +1340 -0
  183. mostlyright/data_harness/sources/catalog/embedding.py +532 -0
  184. mostlyright/data_harness/sources/catalog/entry_v2.py +1182 -0
  185. mostlyright/data_harness/sources/catalog/fill.py +3889 -0
  186. mostlyright/data_harness/sources/catalog/fill_partitions.py +459 -0
  187. mostlyright/data_harness/sources/catalog/fill_staging.py +1105 -0
  188. mostlyright/data_harness/sources/catalog/gating.py +374 -0
  189. mostlyright/data_harness/sources/catalog/generation_receipt.py +1607 -0
  190. mostlyright/data_harness/sources/catalog/harvest/__init__.py +7 -0
  191. mostlyright/data_harness/sources/catalog/harvest/ckan.py +384 -0
  192. mostlyright/data_harness/sources/catalog/harvest/datagov_v4.py +798 -0
  193. mostlyright/data_harness/sources/catalog/harvest/protocol.py +964 -0
  194. mostlyright/data_harness/sources/catalog/harvest/sdmx.py +445 -0
  195. mostlyright/data_harness/sources/catalog/harvest/stac.py +384 -0
  196. mostlyright/data_harness/sources/catalog/health.py +447 -0
  197. mostlyright/data_harness/sources/catalog/hosted_catalog.py +105 -0
  198. mostlyright/data_harness/sources/catalog/identity_history.py +1549 -0
  199. mostlyright/data_harness/sources/catalog/neural.py +1618 -0
  200. mostlyright/data_harness/sources/catalog/packed_catalog.py +2345 -0
  201. mostlyright/data_harness/sources/catalog/packed_retrieval.py +1517 -0
  202. mostlyright/data_harness/sources/catalog/packed_writer.py +2802 -0
  203. mostlyright/data_harness/sources/catalog/query_trace.py +1037 -0
  204. mostlyright/data_harness/sources/catalog/recommend.py +171 -0
  205. mostlyright/data_harness/sources/catalog/retrieval.py +230 -0
  206. mostlyright/data_harness/sources/catalog/retrieval_manifest.py +995 -0
  207. mostlyright/data_harness/sources/catalog/rights_decisions.py +254 -0
  208. mostlyright/data_harness/sources/catalog/sealed.py +560 -0
  209. mostlyright/data_harness/sources/catalog/search.py +230 -0
  210. mostlyright/data_harness/sources/catalog/streaming_delta.py +1097 -0
  211. mostlyright/data_harness/sources/catalog/update.py +891 -0
  212. mostlyright/data_harness/sources/collections.py +815 -0
  213. mostlyright/data_harness/sources/contracts.py +2223 -0
  214. mostlyright/data_harness/sources/deletion.py +761 -0
  215. mostlyright/data_harness/sources/fitness.py +162 -0
  216. mostlyright/data_harness/sources/governance.py +163 -0
  217. mostlyright/data_harness/sources/hosted.py +173 -0
  218. mostlyright/data_harness/sources/integration.py +218 -0
  219. mostlyright/data_harness/sources/range_reader.py +418 -0
  220. mostlyright/data_harness/sources/registry.py +514 -0
  221. mostlyright/data_harness/sources/rights_rule.py +59 -0
  222. mostlyright/data_harness/sources/source_cadence_vectors.v1.json +1 -0
  223. mostlyright/data_harness/sources/sports.py +521 -0
  224. mostlyright/data_harness/sources/stream.py +524 -0
  225. mostlyright/data_harness/sources/stream_connector.py +418 -0
  226. mostlyright/data_harness/sources/stream_recorder.py +1404 -0
  227. mostlyright/data_harness/studio_boundary.py +2019 -0
  228. mostlyright/data_harness/thin/__init__.py +37 -0
  229. mostlyright/data_harness/thin/acquire.py +1137 -0
  230. mostlyright/data_harness/thin/acquire_cancel.py +579 -0
  231. mostlyright/data_harness/thin/approvals.py +617 -0
  232. mostlyright/data_harness/thin/commands.py +406 -0
  233. mostlyright/data_harness/thin/download.py +194 -0
  234. mostlyright/data_harness/thin/narrative.py +589 -0
  235. mostlyright/data_harness/thin/parity.py +1070 -0
  236. mostlyright/data_harness/thin/propose.py +2759 -0
  237. mostlyright/data_harness/thin/research.py +1663 -0
  238. mostlyright/data_harness/thin/router.py +924 -0
  239. mostlyright/data_harness/thin/runs.py +519 -0
  240. mostlyright/data_harness/thin/session.py +281 -0
  241. mostlyright/data_harness/thin/stream.py +501 -0
  242. mostlyright/data_harness/thin/transport.py +187 -0
  243. mostlyright/data_harness/thin/vocabulary.py +368 -0
  244. mostlyright/data_harness/thin/workers.py +164 -0
  245. mostlyright/data_harness/ucum/TABLE-PIN.json +40 -0
  246. mostlyright/data_harness/ucum/ucum-subset.v1.json +632 -0
  247. mostlyright/data_harness/unit_flow.py +927 -0
  248. mostlyright/data_harness/units.py +572 -0
  249. mostlyright/data_harness/ux/__init__.py +9 -0
  250. mostlyright/data_harness/ux/approve.py +485 -0
  251. mostlyright/data_harness/ux/author_yaml.py +597 -0
  252. mostlyright/data_harness/ux/cloud_auth.py +447 -0
  253. mostlyright/data_harness/ux/commands/__init__.py +260 -0
  254. mostlyright/data_harness/ux/commands/approve.py +136 -0
  255. mostlyright/data_harness/ux/commands/auth.py +744 -0
  256. mostlyright/data_harness/ux/commands/author.py +79 -0
  257. mostlyright/data_harness/ux/commands/catalog_author.py +403 -0
  258. mostlyright/data_harness/ux/commands/catalog_fill.py +523 -0
  259. mostlyright/data_harness/ux/commands/catalog_harvest.py +545 -0
  260. mostlyright/data_harness/ux/commands/catalog_publish.py +1838 -0
  261. mostlyright/data_harness/ux/commands/catalog_search.py +71 -0
  262. mostlyright/data_harness/ux/commands/catalog_update.py +437 -0
  263. mostlyright/data_harness/ux/commands/deploy.py +134 -0
  264. mostlyright/data_harness/ux/commands/deploy_dataset.py +98 -0
  265. mostlyright/data_harness/ux/commands/deploy_plan.py +105 -0
  266. mostlyright/data_harness/ux/commands/deploy_status.py +104 -0
  267. mostlyright/data_harness/ux/commands/diff.py +74 -0
  268. mostlyright/data_harness/ux/commands/index.py +84 -0
  269. mostlyright/data_harness/ux/commands/inventory.py +47 -0
  270. mostlyright/data_harness/ux/commands/list_builds.py +143 -0
  271. mostlyright/data_harness/ux/commands/login.py +63 -0
  272. mostlyright/data_harness/ux/commands/peek.py +236 -0
  273. mostlyright/data_harness/ux/commands/plan_check.py +90 -0
  274. mostlyright/data_harness/ux/commands/preflight.py +97 -0
  275. mostlyright/data_harness/ux/commands/record.py +107 -0
  276. mostlyright/data_harness/ux/commands/review_setup.py +47 -0
  277. mostlyright/data_harness/ux/commands/search.py +440 -0
  278. mostlyright/data_harness/ux/commands/show.py +61 -0
  279. mostlyright/data_harness/ux/commands/whoami.py +37 -0
  280. mostlyright/data_harness/ux/credential_native.py +551 -0
  281. mostlyright/data_harness/ux/credential_store.py +1055 -0
  282. mostlyright/data_harness/ux/credentials.py +631 -0
  283. mostlyright/data_harness/ux/diffing.py +444 -0
  284. mostlyright/data_harness/ux/headline.py +671 -0
  285. mostlyright/data_harness/ux/hosted_acquisition.py +974 -0
  286. mostlyright/data_harness/ux/hosted_run_status.py +619 -0
  287. mostlyright/data_harness/ux/inventory.py +427 -0
  288. mostlyright/data_harness/ux/local_review.py +375 -0
  289. mostlyright/data_harness/ux/login.py +691 -0
  290. mostlyright/data_harness/ux/path_kind.py +147 -0
  291. mostlyright/data_harness/ux/peek.py +1000 -0
  292. mostlyright/data_harness/ux/plain_file.py +178 -0
  293. mostlyright/data_harness/ux/plan_check.py +311 -0
  294. mostlyright/data_harness/ux/preflight.py +918 -0
  295. mostlyright/data_harness/ux/readers.py +1124 -0
  296. mostlyright/data_harness/ux/remediation.py +2195 -0
  297. mostlyright/data_harness/ux/render.py +657 -0
  298. mostlyright/data_harness/ux/workload.py +1077 -0
  299. mostlyright/data_harness/viewer.py +3713 -0
  300. mostlyright/data_harness/visual_run/__init__.py +83 -0
  301. mostlyright/data_harness/visual_run/authoring.py +235 -0
  302. mostlyright/data_harness/visual_run/contracts.py +673 -0
  303. mostlyright/data_harness/visual_run/materialize.py +486 -0
  304. mostlyright/data_harness/visual_run/observations.py +874 -0
  305. mostlyright/data_harness/visual_run/query.py +259 -0
  306. mostlyright/data_harness/visual_run/reducer.py +280 -0
  307. mostlyright/data_harness/visual_run/sdk.py +892 -0
  308. mostlyright/data_harness/visual_run/store.py +584 -0
  309. mostlyright/data_harness/visual_run/transport.py +239 -0
  310. mostlyright/data_harness/watch.py +2999 -0
  311. mostlyright_data-0.9.0.dist-info/METADATA +607 -0
  312. mostlyright_data-0.9.0.dist-info/RECORD +314 -0
  313. mostlyright_data-0.9.0.dist-info/WHEEL +4 -0
  314. mostlyright_data-0.9.0.dist-info/entry_points.txt +12 -0
@@ -0,0 +1,631 @@
1
+ """Effective credential resolution and one-way protected-store migration.
2
+
3
+ ``~/.mostlyright/credentials`` is either the strict seven-field legacy object (only until verified
4
+ migration) or a two-field non-secret backend manifest. Raw keys otherwise exist only in the
5
+ selected protected backend and in the explicitly redacted values returned to cloud transports.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import contextlib
11
+ import os
12
+ import secrets
13
+ import stat
14
+ from collections.abc import Mapping
15
+ from dataclasses import dataclass
16
+ from pathlib import Path
17
+ from typing import Any
18
+
19
+ from mostlyright.data_harness.canonical import (
20
+ CanonicalJSONError,
21
+ canonical_json_bytes,
22
+ parse_canonical_json,
23
+ )
24
+ from mostlyright.data_harness.key_seam import LEGACY_API_KEY_ENV
25
+ from mostlyright.data_harness.ux.credential_native import native_backend
26
+ from mostlyright.data_harness.ux.credential_store import (
27
+ CredentialBackend,
28
+ CredentialEnvelope,
29
+ CredentialStore,
30
+ CredentialStoreError,
31
+ ProtectedCredential,
32
+ SecureFileBackend,
33
+ _windows_acl_is_private,
34
+ credential_lock,
35
+ )
36
+
37
+ CREDENTIALS_DIR_NAME = ".mostlyright"
38
+ CREDENTIALS_FILENAME = "credentials"
39
+ MAX_CREDENTIALS_FILE_BYTES = 64 * 1024
40
+ CREDENTIAL_MANIFEST_SCHEMA = "mr-data-credential-manifest.v1"
41
+
42
+ _REQUIRED_FIELDS = frozenset(
43
+ {"cloud_url", "key_id", "prefix", "last_four", "device_name", "created_at", "raw_key"}
44
+ )
45
+ _MANIFEST_FIELDS = frozenset({"schema_version", "backend"})
46
+ _BACKENDS = frozenset({"keychain", "dpapi", "secret-service", "secure-file"})
47
+
48
+ CLOUD_URL_ENV = "MOSTLYRIGHT_CLOUD_URL"
49
+ DEFAULT_CLOUD_URL = "https://mostlyright.md"
50
+
51
+
52
+ class LoginError(RuntimeError):
53
+ """A fail-closed login/storage failure whose detail is always safe to print."""
54
+
55
+ def __init__(self, code: str, detail: str) -> None:
56
+ super().__init__(f"{code}: {detail}")
57
+ self.code = code
58
+ self.detail = detail
59
+
60
+
61
+ @dataclass(frozen=True)
62
+ class StoredCredentials:
63
+ """One stored device credential with an always-redacted representation."""
64
+
65
+ cloud_url: str
66
+ key_id: str
67
+ prefix: str
68
+ last_four: str
69
+ device_name: str
70
+ created_at: str
71
+ raw_key: str
72
+
73
+ def __repr__(self) -> str:
74
+ return (
75
+ "StoredCredentials("
76
+ f"cloud_url={self.cloud_url!r}, key_id={self.key_id!r}, prefix={self.prefix!r}, "
77
+ f"last_four={self.last_four!r}, device_name={self.device_name!r}, "
78
+ f"created_at={self.created_at!r}, raw_key='<redacted>')"
79
+ )
80
+
81
+ def to_receipt(self) -> dict[str, Any]:
82
+ return {
83
+ "cloud_url": self.cloud_url,
84
+ "key_id": self.key_id,
85
+ "prefix": self.prefix,
86
+ "last_four": self.last_four,
87
+ "device_name": self.device_name,
88
+ "created_at": self.created_at,
89
+ }
90
+
91
+ def _protected(self) -> ProtectedCredential:
92
+ try:
93
+ return ProtectedCredential(
94
+ cloud_url=self.cloud_url,
95
+ key_id=self.key_id,
96
+ prefix=self.prefix,
97
+ last_four=self.last_four,
98
+ device_name=self.device_name,
99
+ created_at=self.created_at,
100
+ raw_key=self.raw_key,
101
+ )
102
+ except CredentialStoreError as error:
103
+ raise LoginError(
104
+ "LOGIN_CREDENTIALS_UNREADABLE", "the credential contains an invalid field"
105
+ ) from error
106
+
107
+ @classmethod
108
+ def _from_protected(cls, value: ProtectedCredential) -> StoredCredentials:
109
+ return cls(
110
+ cloud_url=value.cloud_url,
111
+ key_id=value.key_id,
112
+ prefix=value.prefix,
113
+ last_four=value.last_four,
114
+ device_name=value.device_name,
115
+ created_at=value.created_at,
116
+ raw_key=value.raw_key,
117
+ )
118
+
119
+
120
+ @dataclass(frozen=True)
121
+ class ResolvedCloudCredentials:
122
+ """One raw transport credential with safe receipt and representation projections."""
123
+
124
+ cloud_url: str
125
+ raw_key: str
126
+ source: str
127
+
128
+ def __repr__(self) -> str:
129
+ return (
130
+ "ResolvedCloudCredentials("
131
+ f"cloud_url={self.cloud_url!r}, raw_key='<redacted>', source={self.source!r})"
132
+ )
133
+
134
+ def to_receipt(self) -> dict[str, str]:
135
+ return {"cloud_url": self.cloud_url, "source": self.source}
136
+
137
+
138
+ def default_credentials_dir() -> Path:
139
+ return Path.home() / CREDENTIALS_DIR_NAME
140
+
141
+
142
+ def credentials_path(config_dir: Path | None = None) -> Path:
143
+ return (config_dir or default_credentials_dir()) / CREDENTIALS_FILENAME
144
+
145
+
146
+ def refuse_existing_credentials(*, config_dir: Path | None = None) -> None:
147
+ path = credentials_path(config_dir)
148
+ if os.path.lexists(path):
149
+ raise LoginError(
150
+ "LOGIN_CREDENTIALS_EXIST",
151
+ f"a stored credential already exists at {path}; pass --force to overwrite it",
152
+ )
153
+
154
+
155
+ def store_credentials(
156
+ credentials: StoredCredentials,
157
+ *,
158
+ config_dir: Path | None = None,
159
+ force: bool = False,
160
+ credential_store: str | None = None,
161
+ ) -> Path:
162
+ """Install one active credential in the selected protected backend."""
163
+
164
+ directory = config_dir or default_credentials_dir()
165
+ path = directory / CREDENTIALS_FILENAME
166
+ selector = _selector(credential_store)
167
+ try:
168
+ with credential_lock(directory):
169
+ if os.path.lexists(path) and not force:
170
+ # This remains a second direct raise site because the preflight above is only a
171
+ # courtesy check; a competing writer may win between it and this locked gate.
172
+ raise LoginError(
173
+ "LOGIN_CREDENTIALS_EXIST",
174
+ f"a stored credential already exists at {path}; pass --force to overwrite it",
175
+ )
176
+ control = _read_control(path)
177
+ if control is None:
178
+ backend = _backend_for(directory, selector or _native_backend_name(directory))
179
+ current = None
180
+ else:
181
+ kind, value = _parse_control(control, path)
182
+ if kind == "legacy":
183
+ backend = _backend_for(directory, selector or _native_backend_name(directory))
184
+ current = None
185
+ else:
186
+ backend_name = value["backend"]
187
+ _require_selector_agrees(selector, backend_name)
188
+ backend = _backend_for(directory, backend_name)
189
+ current = _load_envelope(directory, backend)
190
+ generation = 1 if current is None else current.generation + 1
191
+ envelope = CredentialEnvelope.active_envelope(
192
+ generation=generation,
193
+ active=credentials._protected(),
194
+ )
195
+ _write_envelope(directory, backend, envelope)
196
+ _require_readback(directory, backend, envelope)
197
+ _install_manifest(path, backend.name)
198
+ return path
199
+ except LoginError:
200
+ raise
201
+ except CredentialStoreError as error:
202
+ raise _login_store_error(error) from None
203
+ except OSError:
204
+ raise LoginError(
205
+ "LOGIN_CREDENTIAL_MIGRATION_FAILED",
206
+ f"the credential manifest at {path} could not be installed",
207
+ ) from None
208
+
209
+
210
+ def read_credentials(
211
+ *,
212
+ config_dir: Path | None = None,
213
+ credential_store: str | None = None,
214
+ ) -> StoredCredentials | None:
215
+ """Read active protected credentials, migrating an exact safe legacy file once."""
216
+
217
+ directory = config_dir or default_credentials_dir()
218
+ path = directory / CREDENTIALS_FILENAME
219
+ selector = _selector(credential_store)
220
+ if not os.path.lexists(path):
221
+ return None
222
+ try:
223
+ with credential_lock(directory):
224
+ raw = _read_control(path)
225
+ if raw is None:
226
+ return None
227
+ kind, value = _parse_control(raw, path)
228
+ if kind == "manifest":
229
+ backend_name = value["backend"]
230
+ _require_selector_agrees(selector, backend_name)
231
+ envelope = _load_envelope(directory, _backend_for(directory, backend_name))
232
+ return _active_credentials(envelope)
233
+
234
+ legacy = _legacy_credentials(value, path)
235
+ backend = _backend_for(directory, selector or _native_backend_name(directory))
236
+ envelope = CredentialEnvelope.active_envelope(
237
+ generation=1,
238
+ active=legacy._protected(),
239
+ )
240
+ _write_envelope(directory, backend, envelope)
241
+ _require_readback(directory, backend, envelope)
242
+ try:
243
+ _install_manifest(path, backend.name)
244
+ except OSError:
245
+ raise LoginError(
246
+ "LOGIN_CREDENTIAL_MIGRATION_FAILED",
247
+ f"the legacy credential at {path} remains recoverable because migration "
248
+ "could not finish",
249
+ ) from None
250
+ return legacy
251
+ except LoginError:
252
+ raise
253
+ except CredentialStoreError as error:
254
+ raise _login_store_error(error) from None
255
+ except OSError:
256
+ raise LoginError(
257
+ "LOGIN_CREDENTIALS_UNREADABLE",
258
+ f"the stored credential at {path} could not be read",
259
+ ) from None
260
+
261
+
262
+ def resolve_cloud_credentials(
263
+ *,
264
+ environment: Mapping[str, str] | None = None,
265
+ config_dir: Path | None = None,
266
+ credential_store: str | None = None,
267
+ ) -> ResolvedCloudCredentials:
268
+ values = os.environ if environment is None else environment
269
+ if LEGACY_API_KEY_ENV in values:
270
+ raw_key = values[LEGACY_API_KEY_ENV]
271
+ if not isinstance(raw_key, str) or not raw_key.strip():
272
+ raise LoginError(
273
+ "LOGIN_API_KEY_INVALID",
274
+ f"{LEGACY_API_KEY_ENV} is set but does not contain a credential",
275
+ )
276
+ return ResolvedCloudCredentials(
277
+ cloud_url=values.get(CLOUD_URL_ENV) or DEFAULT_CLOUD_URL,
278
+ raw_key=raw_key,
279
+ source="environment",
280
+ )
281
+ stored = read_credentials(config_dir=config_dir, credential_store=credential_store)
282
+ if stored is None:
283
+ raise LoginError(
284
+ "LOGIN_REQUIRED",
285
+ "this cloud command needs a device credential; run mr-data login first",
286
+ )
287
+ return ResolvedCloudCredentials(
288
+ cloud_url=stored.cloud_url,
289
+ raw_key=stored.raw_key,
290
+ source="stored_device",
291
+ )
292
+
293
+
294
+ def credential_transaction_store(
295
+ *,
296
+ config_dir: Path | None = None,
297
+ credential_store: str | None = None,
298
+ ) -> tuple[CredentialStore, Path] | None:
299
+ """Return the selected protected store and its non-secret manifest path.
300
+
301
+ Mutating lifecycle commands use this narrow helper rather than reimplementing backend
302
+ selection. A legacy control file is migrated through the existing verified migration path
303
+ before its backend is returned; a pending envelope is deliberately returned rather than
304
+ flattened through :func:`read_credentials`.
305
+ """
306
+
307
+ directory = config_dir or default_credentials_dir()
308
+ path = directory / CREDENTIALS_FILENAME
309
+ selector = _selector(credential_store)
310
+ if not os.path.lexists(path):
311
+ backend = _backend_for(directory, selector or _native_backend_name(directory))
312
+ return CredentialStore(directory, backend), path
313
+ raw = _read_control(path)
314
+ if raw is None:
315
+ return None
316
+ kind, value = _parse_control(raw, path)
317
+ if kind == "legacy":
318
+ # This call owns the migration lock and preserves the legacy bytes on every failure.
319
+ read_credentials(config_dir=directory, credential_store=selector)
320
+ raw = _read_control(path)
321
+ if raw is None:
322
+ return None
323
+ kind, value = _parse_control(raw, path)
324
+ if kind != "manifest":
325
+ raise LoginError("LOGIN_CREDENTIALS_UNREADABLE", "the stored credential is unreadable")
326
+ backend_name = value["backend"]
327
+ _require_selector_agrees(selector, backend_name)
328
+ return CredentialStore(directory, _backend_for(directory, backend_name)), path
329
+
330
+
331
+ def remove_credential_manifest_under_held_lock(path: Path) -> None:
332
+ """Delete the non-secret control record after protected cleanup succeeds.
333
+
334
+ Callers hold the matching per-config lock. Validate the exact regular manifest immediately
335
+ before unlinking so a lifecycle cleanup cannot remove a substituted filesystem object.
336
+ """
337
+
338
+ raw = _read_control(path)
339
+ if raw is None:
340
+ return
341
+ kind, _value = _parse_control(raw, path)
342
+ if kind != "manifest":
343
+ raise LoginError("LOGIN_CREDENTIALS_UNREADABLE", "the stored credential is unreadable")
344
+ try:
345
+ path.unlink()
346
+ except OSError:
347
+ raise LoginError(
348
+ "LOGIN_CREDENTIAL_MIGRATION_FAILED",
349
+ "the local credential manifest could not be removed",
350
+ ) from None
351
+
352
+
353
+ def install_credential_manifest(path: Path, backend_name: str) -> None:
354
+ """Durably publish the non-secret backend selector after a protected write."""
355
+
356
+ _install_manifest(path, backend_name)
357
+
358
+
359
+ def whoami_payload(
360
+ *,
361
+ config_dir: Path | None = None,
362
+ credential_store: str | None = None,
363
+ ) -> dict[str, Any]:
364
+ path = credentials_path(config_dir)
365
+ credentials = read_credentials(config_dir=config_dir, credential_store=credential_store)
366
+ if credentials is None:
367
+ return {
368
+ "schema_version": "mr-data-whoami.v1",
369
+ "status": "not_authenticated",
370
+ "credentials_path": str(path),
371
+ }
372
+ return {
373
+ "schema_version": "mr-data-whoami.v1",
374
+ "status": "authenticated",
375
+ **credentials.to_receipt(),
376
+ "credentials_path": str(path),
377
+ }
378
+
379
+
380
+ def _selector(value: str | None) -> str | None:
381
+ if value is None:
382
+ return None
383
+ if value != "secure-file":
384
+ raise LoginError(
385
+ "LOGIN_CREDENTIAL_STORE_INVALID",
386
+ "credential store must be the exact value secure-file when explicitly selected",
387
+ )
388
+ return value
389
+
390
+
391
+ def _native_backend_name(directory: Path) -> str:
392
+ try:
393
+ return native_backend(directory).name
394
+ except CredentialStoreError as error:
395
+ raise _login_store_error(error) from None
396
+
397
+
398
+ def _backend_for(directory: Path, backend_name: str) -> CredentialBackend:
399
+ if backend_name == "secure-file":
400
+ return SecureFileBackend(directory, explicit=True)
401
+ try:
402
+ backend = native_backend(directory)
403
+ except CredentialStoreError as error:
404
+ raise _login_store_error(error) from None
405
+ if backend.name != backend_name:
406
+ raise LoginError(
407
+ "LOGIN_CREDENTIAL_STORE_UNAVAILABLE",
408
+ f"the credential manifest names {backend_name}, which is unavailable on this system",
409
+ )
410
+ return backend
411
+
412
+
413
+ def _require_selector_agrees(selector: str | None, backend_name: str) -> None:
414
+ if selector is not None and selector != backend_name:
415
+ raise LoginError(
416
+ "LOGIN_CREDENTIAL_STORE_CONFLICT",
417
+ f"the stored credential uses {backend_name}; the explicit selector does not agree",
418
+ )
419
+
420
+
421
+ def _load_envelope(directory: Path, backend: CredentialBackend) -> CredentialEnvelope | None:
422
+ return CredentialStore(directory, backend).load_under_held_lock()
423
+
424
+
425
+ def _write_envelope(
426
+ directory: Path,
427
+ backend: CredentialBackend,
428
+ envelope: CredentialEnvelope,
429
+ ) -> None:
430
+ CredentialStore(directory, backend).write_under_held_lock(envelope)
431
+
432
+
433
+ def _active_credentials(envelope: CredentialEnvelope | None) -> StoredCredentials | None:
434
+ if envelope is None:
435
+ return None
436
+ if envelope.state != "active" or envelope.active is None:
437
+ detail = (
438
+ "a device login is pending and its remote mint outcome is unproved"
439
+ if envelope.state == "login_pending"
440
+ else "the stored credential has a pending recovery operation"
441
+ )
442
+ raise LoginError("LOGIN_CREDENTIAL_RECOVERY_REQUIRED", detail)
443
+ return StoredCredentials._from_protected(envelope.active)
444
+
445
+
446
+ def _require_readback(
447
+ directory: Path,
448
+ backend: CredentialBackend,
449
+ expected: CredentialEnvelope,
450
+ ) -> None:
451
+ actual = _load_envelope(directory, backend)
452
+ if actual != expected or actual is None or actual.to_bytes() != expected.to_bytes():
453
+ raise CredentialStoreError(
454
+ "CREDENTIAL_STORE_READBACK_FAILED",
455
+ "the protected credential did not verify after write",
456
+ )
457
+
458
+
459
+ def _read_control(path: Path) -> bytes | None:
460
+ try:
461
+ descriptor = os.open(
462
+ path,
463
+ os.O_RDONLY
464
+ | getattr(os, "O_BINARY", 0)
465
+ | getattr(os, "O_NONBLOCK", 0)
466
+ | getattr(os, "O_NOFOLLOW", 0),
467
+ )
468
+ except FileNotFoundError:
469
+ if os.path.lexists(path):
470
+ raise LoginError(
471
+ "LOGIN_CREDENTIALS_UNREADABLE",
472
+ f"the stored credential at {path} could not be read",
473
+ ) from None
474
+ return None
475
+ except OSError:
476
+ raise LoginError(
477
+ "LOGIN_CREDENTIALS_UNREADABLE", f"the stored credential at {path} could not be read"
478
+ ) from None
479
+ try:
480
+ info = os.fstat(descriptor)
481
+ valid = stat.S_ISREG(info.st_mode) and info.st_nlink == 1
482
+ if os.name == "posix":
483
+ valid = valid and info.st_uid == os.geteuid() and stat.S_IMODE(info.st_mode) == 0o600
484
+ elif os.name == "nt":
485
+ valid = valid and _windows_acl_is_private(path)
486
+ if not valid or info.st_size > MAX_CREDENTIALS_FILE_BYTES:
487
+ raise LoginError(
488
+ "LOGIN_CREDENTIALS_UNREADABLE",
489
+ f"the stored credential at {path} could not be read",
490
+ )
491
+ raw = bytearray()
492
+ while len(raw) <= MAX_CREDENTIALS_FILE_BYTES:
493
+ chunk = os.read(descriptor, min(16 * 1024, MAX_CREDENTIALS_FILE_BYTES + 1 - len(raw)))
494
+ if not chunk:
495
+ break
496
+ raw.extend(chunk)
497
+ if len(raw) > MAX_CREDENTIALS_FILE_BYTES:
498
+ raise LoginError(
499
+ "LOGIN_CREDENTIALS_UNREADABLE",
500
+ f"the stored credential at {path} could not be read",
501
+ )
502
+ return bytes(raw)
503
+ finally:
504
+ os.close(descriptor)
505
+
506
+
507
+ def _parse_control(raw: bytes, path: Path) -> tuple[str, dict[str, Any]]:
508
+ try:
509
+ value = parse_canonical_json(raw)
510
+ except CanonicalJSONError:
511
+ raise LoginError(
512
+ "LOGIN_CREDENTIALS_UNREADABLE",
513
+ f"the stored credential at {path} is not the file this command writes",
514
+ ) from None
515
+ if not isinstance(value, dict):
516
+ raise LoginError(
517
+ "LOGIN_CREDENTIALS_UNREADABLE",
518
+ f"the stored credential at {path} is not the file this command writes",
519
+ )
520
+ if set(value) == _MANIFEST_FIELDS:
521
+ if (
522
+ value.get("schema_version") != CREDENTIAL_MANIFEST_SCHEMA
523
+ or value.get("backend") not in _BACKENDS
524
+ ):
525
+ raise LoginError(
526
+ "LOGIN_CREDENTIALS_UNREADABLE",
527
+ f"the stored credential at {path} is not the file this command writes",
528
+ )
529
+ return "manifest", value
530
+ if set(value) == _REQUIRED_FIELDS:
531
+ return "legacy", value
532
+ raise LoginError(
533
+ "LOGIN_CREDENTIALS_UNREADABLE",
534
+ f"the stored credential at {path} is not the file this command writes",
535
+ )
536
+
537
+
538
+ def _legacy_credentials(value: dict[str, Any], path: Path) -> StoredCredentials:
539
+ if any(not isinstance(value[field], str) or not value[field] for field in _REQUIRED_FIELDS):
540
+ raise LoginError(
541
+ "LOGIN_CREDENTIALS_UNREADABLE",
542
+ f"the stored credential at {path} is not the file this command writes",
543
+ )
544
+ return StoredCredentials(**{field: value[field] for field in _REQUIRED_FIELDS})
545
+
546
+
547
+ def _install_manifest(path: Path, backend_name: str) -> None:
548
+ payload = canonical_json_bytes(
549
+ {"backend": backend_name, "schema_version": CREDENTIAL_MANIFEST_SCHEMA}
550
+ )
551
+ stage = path.parent / f".{path.name}.{secrets.token_hex(16)}.stage"
552
+ descriptor: int | None = None
553
+ directory_fd: int | None = None
554
+ try:
555
+ descriptor = os.open(
556
+ stage,
557
+ os.O_WRONLY
558
+ | os.O_CREAT
559
+ | os.O_EXCL
560
+ | getattr(os, "O_BINARY", 0)
561
+ | getattr(os, "O_NOFOLLOW", 0),
562
+ 0o600,
563
+ )
564
+ if os.name == "posix":
565
+ os.fchmod(descriptor, 0o600)
566
+ offset = 0
567
+ while offset < len(payload):
568
+ written = os.write(descriptor, payload[offset:])
569
+ if written <= 0:
570
+ raise OSError("short manifest write")
571
+ offset += written
572
+ os.fsync(descriptor)
573
+ os.close(descriptor)
574
+ descriptor = None
575
+ os.replace(stage, path)
576
+ if os.name == "posix":
577
+ directory_fd = os.open(
578
+ path.parent,
579
+ os.O_RDONLY | getattr(os, "O_DIRECTORY", 0) | getattr(os, "O_NOFOLLOW", 0),
580
+ )
581
+ os.fsync(directory_fd)
582
+ finally:
583
+ if descriptor is not None:
584
+ os.close(descriptor)
585
+ if directory_fd is not None:
586
+ os.close(directory_fd)
587
+ with contextlib.suppress(OSError):
588
+ stage.unlink()
589
+
590
+
591
+ def _login_store_error(error: CredentialStoreError) -> LoginError:
592
+ suffix = error.code.removeprefix("CREDENTIAL_")
593
+ details = {
594
+ "CREDENTIAL_STORE_UNAVAILABLE": (
595
+ "native protected storage is unavailable; explicitly select "
596
+ "--credential-store secure-file to use an owner-controlled file"
597
+ ),
598
+ "CREDENTIAL_STORE_WRITE_FAILED": "the protected credential could not be written",
599
+ "CREDENTIAL_STORE_READBACK_FAILED": (
600
+ "the protected credential could not be verified after writing"
601
+ ),
602
+ "CREDENTIAL_STORE_PERMISSIONS": ("the selected credential store is not owner controlled"),
603
+ "CREDENTIAL_STORE_CORRUPT": "the protected credential is unreadable",
604
+ "CREDENTIAL_LOCKED": "another credential operation is already running",
605
+ "CREDENTIAL_LOCK_UNAVAILABLE": "the credential operation lock is unavailable",
606
+ }
607
+ return LoginError(
608
+ f"LOGIN_CREDENTIAL_{suffix}",
609
+ details.get(error.code, "the protected credential operation failed"),
610
+ )
611
+
612
+
613
+ __all__ = [
614
+ "CLOUD_URL_ENV",
615
+ "CREDENTIALS_DIR_NAME",
616
+ "CREDENTIALS_FILENAME",
617
+ "DEFAULT_CLOUD_URL",
618
+ "LoginError",
619
+ "ResolvedCloudCredentials",
620
+ "StoredCredentials",
621
+ "credential_transaction_store",
622
+ "credentials_path",
623
+ "default_credentials_dir",
624
+ "install_credential_manifest",
625
+ "read_credentials",
626
+ "refuse_existing_credentials",
627
+ "remove_credential_manifest_under_held_lock",
628
+ "resolve_cloud_credentials",
629
+ "store_credentials",
630
+ "whoami_payload",
631
+ ]