machineconfig 1.95__py3-none-any.whl → 1.97__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.

Potentially problematic release.


This version of machineconfig might be problematic. Click here for more details.

Files changed (504) hide show
  1. machineconfig/cluster/cloud_manager.py +445 -343
  2. machineconfig/cluster/data_transfer.py +63 -57
  3. machineconfig/cluster/distribute.py +284 -280
  4. machineconfig/cluster/file_manager.py +234 -237
  5. machineconfig/cluster/job_params.py +133 -133
  6. machineconfig/cluster/loader_runner.py +183 -149
  7. machineconfig/cluster/remote_machine.py +269 -252
  8. machineconfig/cluster/script_execution.py +215 -209
  9. machineconfig/cluster/script_notify_upon_completion.py +50 -43
  10. machineconfig/cluster/self_ssh.py +52 -54
  11. machineconfig/cluster/sessions_managers/__init__.py +0 -0
  12. machineconfig/cluster/sessions_managers/archive/__init__.py +0 -0
  13. machineconfig/{jobs/python → cluster/sessions_managers/archive}/create_zellij_template.py +5 -3
  14. machineconfig/cluster/sessions_managers/archive/session_managers.py +184 -0
  15. machineconfig/cluster/sessions_managers/demo_rich_zellij.py +0 -0
  16. machineconfig/cluster/sessions_managers/enhanced_command_runner.py +160 -0
  17. machineconfig/cluster/sessions_managers/wt_local.py +494 -0
  18. machineconfig/cluster/sessions_managers/wt_local_manager.py +577 -0
  19. machineconfig/cluster/sessions_managers/wt_remote.py +288 -0
  20. machineconfig/cluster/sessions_managers/wt_remote_manager.py +483 -0
  21. machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +196 -0
  22. machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +418 -0
  23. machineconfig/cluster/sessions_managers/wt_utils/remote_executor.py +175 -0
  24. machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +300 -0
  25. machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +228 -0
  26. machineconfig/cluster/sessions_managers/zellij_local.py +418 -0
  27. machineconfig/cluster/sessions_managers/zellij_local_manager.py +533 -0
  28. machineconfig/cluster/sessions_managers/zellij_remote.py +229 -0
  29. machineconfig/cluster/sessions_managers/zellij_remote_manager.py +188 -0
  30. machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +64 -0
  31. machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +126 -0
  32. machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +334 -0
  33. machineconfig/cluster/sessions_managers/zellij_utils/remote_executor.py +68 -0
  34. machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +119 -0
  35. machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +85 -0
  36. machineconfig/cluster/templates/cli_click.py +0 -1
  37. machineconfig/cluster/templates/cli_gooey.py +102 -104
  38. machineconfig/cluster/templates/run_cloud.py +51 -51
  39. machineconfig/cluster/templates/run_cluster.py +103 -59
  40. machineconfig/cluster/templates/run_remote.py +57 -58
  41. machineconfig/cluster/templates/utils.py +69 -36
  42. machineconfig/jobs/__pycache__/__init__.cpython-311.pyc +0 -0
  43. machineconfig/jobs/python/__pycache__/__init__.cpython-311.pyc +0 -0
  44. machineconfig/jobs/python/check_installations.py +258 -190
  45. machineconfig/jobs/python/create_bootable_media.py +7 -3
  46. machineconfig/jobs/python/python_cargo_build_share.py +50 -50
  47. machineconfig/jobs/python/python_ve_symlink.py +6 -6
  48. machineconfig/jobs/python/vscode/__pycache__/select_interpreter.cpython-311.pyc +0 -0
  49. machineconfig/jobs/python/vscode/api.py +1 -1
  50. machineconfig/jobs/python/vscode/link_ve.py +2 -2
  51. machineconfig/jobs/python/vscode/select_interpreter.py +9 -5
  52. machineconfig/jobs/python/vscode/sync_code.py +8 -5
  53. machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  54. machineconfig/jobs/python_custom_installers/archive/ngrok.py +1 -1
  55. machineconfig/jobs/python_custom_installers/dev/alacritty.py +3 -2
  56. machineconfig/jobs/python_custom_installers/dev/brave.py +7 -3
  57. machineconfig/jobs/python_custom_installers/dev/bypass_paywall.py +3 -4
  58. machineconfig/jobs/python_custom_installers/dev/code.py +3 -1
  59. machineconfig/jobs/python_custom_installers/dev/cursor.py +66 -5
  60. machineconfig/jobs/python_custom_installers/dev/docker_desktop.py +0 -1
  61. machineconfig/jobs/python_custom_installers/dev/espanso.py +13 -9
  62. machineconfig/jobs/python_custom_installers/dev/goes.py +2 -8
  63. machineconfig/jobs/python_custom_installers/dev/lvim.py +3 -2
  64. machineconfig/jobs/python_custom_installers/dev/nerdfont.py +1 -1
  65. machineconfig/jobs/python_custom_installers/dev/redis.py +7 -3
  66. machineconfig/jobs/python_custom_installers/dev/wezterm.py +8 -4
  67. machineconfig/jobs/python_custom_installers/dev/winget.py +194 -0
  68. machineconfig/jobs/python_custom_installers/{dev/docker.py → docker.py} +8 -3
  69. machineconfig/jobs/python_custom_installers/gh.py +4 -3
  70. machineconfig/jobs/python_custom_installers/hx.py +9 -8
  71. machineconfig/jobs/python_custom_installers/scripts/linux/vscode.sh +97 -30
  72. machineconfig/jobs/python_custom_installers/{dev/warp-cli.py → warp-cli.py} +1 -1
  73. machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  74. machineconfig/jobs/python_generic_installers/config.json +133 -9
  75. machineconfig/jobs/python_generic_installers/dev/config.json +208 -37
  76. machineconfig/jobs/python_generic_installers/update.py +3 -0
  77. machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  78. machineconfig/jobs/python_linux_installers/config.json +42 -6
  79. machineconfig/jobs/python_linux_installers/dev/config.json +79 -11
  80. machineconfig/jobs/python_windows_installers/config.json +6 -0
  81. machineconfig/profile/__pycache__/__init__.cpython-311.pyc +0 -0
  82. machineconfig/profile/__pycache__/create.cpython-311.pyc +0 -0
  83. machineconfig/profile/__pycache__/shell.cpython-311.pyc +0 -0
  84. machineconfig/profile/create.py +5 -5
  85. machineconfig/profile/create_hardlinks.py +5 -5
  86. machineconfig/profile/shell.py +44 -17
  87. machineconfig/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  88. machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
  89. machineconfig/scripts/linux/fire_agents +27 -0
  90. machineconfig/scripts/linux/mcinit +27 -0
  91. machineconfig/scripts/linux/wifi_conn +24 -0
  92. machineconfig/scripts/python/__pycache__/__init__.cpython-311.pyc +0 -0
  93. machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
  94. machineconfig/scripts/python/__pycache__/cloud_copy.cpython-311.pyc +0 -0
  95. machineconfig/scripts/python/__pycache__/cloud_mount.cpython-311.pyc +0 -0
  96. machineconfig/scripts/python/__pycache__/cloud_sync.cpython-311.pyc +0 -0
  97. machineconfig/scripts/python/__pycache__/croshell.cpython-311.pyc +0 -0
  98. machineconfig/scripts/python/__pycache__/devops.cpython-311.pyc +0 -0
  99. machineconfig/scripts/python/__pycache__/devops_backup_retrieve.cpython-311.pyc +0 -0
  100. machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-311.pyc +0 -0
  101. machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-311.pyc +0 -0
  102. machineconfig/scripts/python/__pycache__/fire_agents.cpython-311.pyc +0 -0
  103. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-311.pyc +0 -0
  104. machineconfig/scripts/python/__pycache__/fire_jobs.cpython-313.pyc +0 -0
  105. machineconfig/scripts/python/__pycache__/get_zellij_cmd.cpython-311.pyc +0 -0
  106. machineconfig/scripts/python/__pycache__/repos.cpython-311.pyc +0 -0
  107. machineconfig/scripts/python/ai/__pycache__/init.cpython-311.pyc +0 -0
  108. machineconfig/scripts/python/ai/init.py +56 -0
  109. machineconfig/scripts/python/ai/rules/python/dev.md +31 -0
  110. machineconfig/scripts/python/archive/im2text.py +1 -3
  111. machineconfig/scripts/python/choose_wezterm_theme.py +3 -3
  112. machineconfig/scripts/python/cloud_copy.py +10 -10
  113. machineconfig/scripts/python/cloud_manager.py +77 -99
  114. machineconfig/scripts/python/cloud_mount.py +13 -12
  115. machineconfig/scripts/python/cloud_repo_sync.py +14 -11
  116. machineconfig/scripts/python/croshell.py +24 -21
  117. machineconfig/scripts/python/devops.py +12 -17
  118. machineconfig/scripts/python/devops_add_identity.py +32 -10
  119. machineconfig/scripts/python/devops_add_ssh_key.py +10 -10
  120. machineconfig/scripts/python/devops_backup_retrieve.py +9 -8
  121. machineconfig/scripts/python/devops_devapps_install.py +6 -6
  122. machineconfig/scripts/python/devops_update_repos.py +4 -3
  123. machineconfig/scripts/python/dotfile.py +10 -7
  124. machineconfig/scripts/python/fire_agents.py +69 -0
  125. machineconfig/scripts/python/fire_jobs.py +62 -65
  126. machineconfig/scripts/python/ftpx.py +8 -8
  127. machineconfig/scripts/python/get_zellij_cmd.py +3 -3
  128. machineconfig/scripts/python/gh_models.py +6 -4
  129. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-311.pyc +0 -0
  130. machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-313.pyc +0 -0
  131. machineconfig/scripts/python/helpers/__pycache__/cloud_helpers.cpython-311.pyc +0 -0
  132. machineconfig/scripts/python/helpers/__pycache__/helpers2.cpython-311.pyc +0 -0
  133. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-311.pyc +0 -0
  134. machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-313.pyc +0 -0
  135. machineconfig/scripts/python/helpers/__pycache__/repo_sync_helpers.cpython-311.pyc +0 -0
  136. machineconfig/scripts/python/helpers/cloud_helpers.py +12 -12
  137. machineconfig/scripts/python/helpers/helpers2.py +9 -8
  138. machineconfig/scripts/python/helpers/helpers4.py +23 -35
  139. machineconfig/scripts/python/helpers/repo_sync_helpers.py +17 -16
  140. machineconfig/scripts/python/mount_nfs.py +8 -11
  141. machineconfig/scripts/python/mount_nw_drive.py +4 -4
  142. machineconfig/scripts/python/mount_ssh.py +2 -2
  143. machineconfig/scripts/python/onetimeshare.py +56 -57
  144. machineconfig/scripts/python/pomodoro.py +55 -55
  145. machineconfig/scripts/python/repos.py +26 -18
  146. machineconfig/scripts/python/scheduler.py +70 -53
  147. machineconfig/scripts/python/snapshot.py +21 -24
  148. machineconfig/scripts/python/start_slidev.py +6 -5
  149. machineconfig/scripts/python/start_terminals.py +3 -1
  150. machineconfig/scripts/python/viewer.py +5 -4
  151. machineconfig/scripts/python/viewer_template.py +138 -140
  152. machineconfig/scripts/python/wifi_conn.py +412 -60
  153. machineconfig/scripts/python/wsl_windows_transfer.py +18 -3
  154. machineconfig/scripts/windows/mcinit.ps1 +4 -0
  155. machineconfig/settings/linters/.pylintrc +6 -7
  156. machineconfig/settings/lvim/windows/config.lua +0 -0
  157. machineconfig/settings/shells/bash/init.sh +6 -0
  158. machineconfig/settings/shells/ipy/profiles/default/startup/playext.py +7 -6
  159. machineconfig/settings/shells/pwsh/init.ps1 +6 -6
  160. machineconfig/settings/shells/wt/settings.json +51 -266
  161. machineconfig/setup_linux/web_shortcuts/interactive.sh +5 -2
  162. machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +3 -6
  163. machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +11 -9
  164. machineconfig/utils/ai/url2md.py +2 -2
  165. machineconfig/utils/cloud/onedrive/setup_oauth.py +59 -0
  166. machineconfig/utils/cloud/onedrive/transaction.py +796 -0
  167. machineconfig/utils/code.py +22 -13
  168. machineconfig/utils/installer.py +78 -35
  169. machineconfig/utils/installer_utils/installer_abc.py +7 -6
  170. machineconfig/utils/installer_utils/installer_class.py +44 -25
  171. machineconfig/utils/io_save.py +107 -0
  172. machineconfig/utils/links.py +19 -15
  173. machineconfig/utils/options.py +4 -8
  174. machineconfig/utils/path.py +91 -78
  175. machineconfig/utils/path_reduced.py +608 -0
  176. machineconfig/utils/procs.py +110 -45
  177. machineconfig/utils/scheduling.py +312 -222
  178. machineconfig/utils/utils.py +7 -7
  179. machineconfig/utils/utils2.py +42 -0
  180. machineconfig/utils/utils5.py +84 -0
  181. machineconfig/utils/ve.py +49 -87
  182. {machineconfig-1.95.dist-info → machineconfig-1.97.dist-info}/METADATA +2 -2
  183. machineconfig-1.97.dist-info/RECORD +442 -0
  184. machineconfig/cluster/session_managers.py +0 -183
  185. machineconfig/cluster/templates/f.py +0 -4
  186. machineconfig/jobs/python/__pycache__/check_installations.cpython-311.pyc +0 -0
  187. machineconfig/jobs/python/__pycache__/checkout_version.cpython-311.pyc +0 -0
  188. machineconfig/jobs/python/__pycache__/python_ve_symlink.cpython-311.pyc +0 -0
  189. machineconfig/jobs/python/checkout_version.py +0 -123
  190. machineconfig/jobs/python/vscode/__pycache__/api.cpython-311.pyc +0 -0
  191. machineconfig/jobs/python/vscode/__pycache__/link_ve.cpython-311.pyc +0 -0
  192. machineconfig/jobs/python_custom_installers/__pycache__/hx.cpython-311.pyc +0 -0
  193. machineconfig/jobs/python_windows_installers/__pycache__/__init__.cpython-311.pyc +0 -0
  194. machineconfig/scripts/python/.mypy_cache/.gitignore +0 -2
  195. machineconfig/scripts/python/.mypy_cache/3.11/@plugins_snapshot.json +0 -1
  196. machineconfig/scripts/python/.mypy_cache/3.11/__future__.data.json +0 -1
  197. machineconfig/scripts/python/.mypy_cache/3.11/__future__.meta.json +0 -1
  198. machineconfig/scripts/python/.mypy_cache/3.11/_ast.data.json +0 -1
  199. machineconfig/scripts/python/.mypy_cache/3.11/_ast.meta.json +0 -1
  200. machineconfig/scripts/python/.mypy_cache/3.11/_bz2.data.json +0 -1
  201. machineconfig/scripts/python/.mypy_cache/3.11/_bz2.meta.json +0 -1
  202. machineconfig/scripts/python/.mypy_cache/3.11/_codecs.data.json +0 -1
  203. machineconfig/scripts/python/.mypy_cache/3.11/_codecs.meta.json +0 -1
  204. machineconfig/scripts/python/.mypy_cache/3.11/_collections_abc.data.json +0 -1
  205. machineconfig/scripts/python/.mypy_cache/3.11/_collections_abc.meta.json +0 -1
  206. machineconfig/scripts/python/.mypy_cache/3.11/_compression.data.json +0 -1
  207. machineconfig/scripts/python/.mypy_cache/3.11/_compression.meta.json +0 -1
  208. machineconfig/scripts/python/.mypy_cache/3.11/_decimal.data.json +0 -1
  209. machineconfig/scripts/python/.mypy_cache/3.11/_decimal.meta.json +0 -1
  210. machineconfig/scripts/python/.mypy_cache/3.11/_frozen_importlib.data.json +0 -1
  211. machineconfig/scripts/python/.mypy_cache/3.11/_frozen_importlib.meta.json +0 -1
  212. machineconfig/scripts/python/.mypy_cache/3.11/_frozen_importlib_external.data.json +0 -1
  213. machineconfig/scripts/python/.mypy_cache/3.11/_frozen_importlib_external.meta.json +0 -1
  214. machineconfig/scripts/python/.mypy_cache/3.11/_io.data.json +0 -1
  215. machineconfig/scripts/python/.mypy_cache/3.11/_io.meta.json +0 -1
  216. machineconfig/scripts/python/.mypy_cache/3.11/_locale.data.json +0 -1
  217. machineconfig/scripts/python/.mypy_cache/3.11/_locale.meta.json +0 -1
  218. machineconfig/scripts/python/.mypy_cache/3.11/_stat.data.json +0 -1
  219. machineconfig/scripts/python/.mypy_cache/3.11/_stat.meta.json +0 -1
  220. machineconfig/scripts/python/.mypy_cache/3.11/_struct.data.json +0 -1
  221. machineconfig/scripts/python/.mypy_cache/3.11/_struct.meta.json +0 -1
  222. machineconfig/scripts/python/.mypy_cache/3.11/_thread.data.json +0 -1
  223. machineconfig/scripts/python/.mypy_cache/3.11/_thread.meta.json +0 -1
  224. machineconfig/scripts/python/.mypy_cache/3.11/_typeshed/__init__.data.json +0 -1
  225. machineconfig/scripts/python/.mypy_cache/3.11/_typeshed/__init__.meta.json +0 -1
  226. machineconfig/scripts/python/.mypy_cache/3.11/_typeshed/importlib.data.json +0 -1
  227. machineconfig/scripts/python/.mypy_cache/3.11/_typeshed/importlib.meta.json +0 -1
  228. machineconfig/scripts/python/.mypy_cache/3.11/_warnings.data.json +0 -1
  229. machineconfig/scripts/python/.mypy_cache/3.11/_warnings.meta.json +0 -1
  230. machineconfig/scripts/python/.mypy_cache/3.11/_weakref.data.json +0 -1
  231. machineconfig/scripts/python/.mypy_cache/3.11/_weakref.meta.json +0 -1
  232. machineconfig/scripts/python/.mypy_cache/3.11/_weakrefset.data.json +0 -1
  233. machineconfig/scripts/python/.mypy_cache/3.11/_weakrefset.meta.json +0 -1
  234. machineconfig/scripts/python/.mypy_cache/3.11/abc.data.json +0 -1
  235. machineconfig/scripts/python/.mypy_cache/3.11/abc.meta.json +0 -1
  236. machineconfig/scripts/python/.mypy_cache/3.11/argparse.data.json +0 -1
  237. machineconfig/scripts/python/.mypy_cache/3.11/argparse.meta.json +0 -1
  238. machineconfig/scripts/python/.mypy_cache/3.11/ast.data.json +0 -1
  239. machineconfig/scripts/python/.mypy_cache/3.11/ast.meta.json +0 -1
  240. machineconfig/scripts/python/.mypy_cache/3.11/binascii.data.json +0 -1
  241. machineconfig/scripts/python/.mypy_cache/3.11/binascii.meta.json +0 -1
  242. machineconfig/scripts/python/.mypy_cache/3.11/builtins.data.json +0 -1
  243. machineconfig/scripts/python/.mypy_cache/3.11/builtins.meta.json +0 -1
  244. machineconfig/scripts/python/.mypy_cache/3.11/bz2.data.json +0 -1
  245. machineconfig/scripts/python/.mypy_cache/3.11/bz2.meta.json +0 -1
  246. machineconfig/scripts/python/.mypy_cache/3.11/calendar.data.json +0 -1
  247. machineconfig/scripts/python/.mypy_cache/3.11/calendar.meta.json +0 -1
  248. machineconfig/scripts/python/.mypy_cache/3.11/codecs.data.json +0 -1
  249. machineconfig/scripts/python/.mypy_cache/3.11/codecs.meta.json +0 -1
  250. machineconfig/scripts/python/.mypy_cache/3.11/collections/__init__.data.json +0 -1
  251. machineconfig/scripts/python/.mypy_cache/3.11/collections/__init__.meta.json +0 -1
  252. machineconfig/scripts/python/.mypy_cache/3.11/collections/abc.data.json +0 -1
  253. machineconfig/scripts/python/.mypy_cache/3.11/collections/abc.meta.json +0 -1
  254. machineconfig/scripts/python/.mypy_cache/3.11/configparser.data.json +0 -1
  255. machineconfig/scripts/python/.mypy_cache/3.11/configparser.meta.json +0 -1
  256. machineconfig/scripts/python/.mypy_cache/3.11/contextlib.data.json +0 -1
  257. machineconfig/scripts/python/.mypy_cache/3.11/contextlib.meta.json +0 -1
  258. machineconfig/scripts/python/.mypy_cache/3.11/dataclasses.data.json +0 -1
  259. machineconfig/scripts/python/.mypy_cache/3.11/dataclasses.meta.json +0 -1
  260. machineconfig/scripts/python/.mypy_cache/3.11/datetime.data.json +0 -1
  261. machineconfig/scripts/python/.mypy_cache/3.11/datetime.meta.json +0 -1
  262. machineconfig/scripts/python/.mypy_cache/3.11/decimal.data.json +0 -1
  263. machineconfig/scripts/python/.mypy_cache/3.11/decimal.meta.json +0 -1
  264. machineconfig/scripts/python/.mypy_cache/3.11/dis.data.json +0 -1
  265. machineconfig/scripts/python/.mypy_cache/3.11/dis.meta.json +0 -1
  266. machineconfig/scripts/python/.mypy_cache/3.11/email/__init__.data.json +0 -1
  267. machineconfig/scripts/python/.mypy_cache/3.11/email/__init__.meta.json +0 -1
  268. machineconfig/scripts/python/.mypy_cache/3.11/email/_policybase.data.json +0 -1
  269. machineconfig/scripts/python/.mypy_cache/3.11/email/_policybase.meta.json +0 -1
  270. machineconfig/scripts/python/.mypy_cache/3.11/email/charset.data.json +0 -1
  271. machineconfig/scripts/python/.mypy_cache/3.11/email/charset.meta.json +0 -1
  272. machineconfig/scripts/python/.mypy_cache/3.11/email/contentmanager.data.json +0 -1
  273. machineconfig/scripts/python/.mypy_cache/3.11/email/contentmanager.meta.json +0 -1
  274. machineconfig/scripts/python/.mypy_cache/3.11/email/errors.data.json +0 -1
  275. machineconfig/scripts/python/.mypy_cache/3.11/email/errors.meta.json +0 -1
  276. machineconfig/scripts/python/.mypy_cache/3.11/email/header.data.json +0 -1
  277. machineconfig/scripts/python/.mypy_cache/3.11/email/header.meta.json +0 -1
  278. machineconfig/scripts/python/.mypy_cache/3.11/email/message.data.json +0 -1
  279. machineconfig/scripts/python/.mypy_cache/3.11/email/message.meta.json +0 -1
  280. machineconfig/scripts/python/.mypy_cache/3.11/email/policy.data.json +0 -1
  281. machineconfig/scripts/python/.mypy_cache/3.11/email/policy.meta.json +0 -1
  282. machineconfig/scripts/python/.mypy_cache/3.11/enum.data.json +0 -1
  283. machineconfig/scripts/python/.mypy_cache/3.11/enum.meta.json +0 -1
  284. machineconfig/scripts/python/.mypy_cache/3.11/fnmatch.data.json +0 -1
  285. machineconfig/scripts/python/.mypy_cache/3.11/fnmatch.meta.json +0 -1
  286. machineconfig/scripts/python/.mypy_cache/3.11/functools.data.json +0 -1
  287. machineconfig/scripts/python/.mypy_cache/3.11/functools.meta.json +0 -1
  288. machineconfig/scripts/python/.mypy_cache/3.11/gc.data.json +0 -1
  289. machineconfig/scripts/python/.mypy_cache/3.11/gc.meta.json +0 -1
  290. machineconfig/scripts/python/.mypy_cache/3.11/genericpath.data.json +0 -1
  291. machineconfig/scripts/python/.mypy_cache/3.11/genericpath.meta.json +0 -1
  292. machineconfig/scripts/python/.mypy_cache/3.11/getpass.data.json +0 -1
  293. machineconfig/scripts/python/.mypy_cache/3.11/getpass.meta.json +0 -1
  294. machineconfig/scripts/python/.mypy_cache/3.11/git/__init__.data.json +0 -1
  295. machineconfig/scripts/python/.mypy_cache/3.11/git/__init__.meta.json +0 -1
  296. machineconfig/scripts/python/.mypy_cache/3.11/git/cmd.data.json +0 -1
  297. machineconfig/scripts/python/.mypy_cache/3.11/git/cmd.meta.json +0 -1
  298. machineconfig/scripts/python/.mypy_cache/3.11/git/compat.data.json +0 -1
  299. machineconfig/scripts/python/.mypy_cache/3.11/git/compat.meta.json +0 -1
  300. machineconfig/scripts/python/.mypy_cache/3.11/git/config.data.json +0 -1
  301. machineconfig/scripts/python/.mypy_cache/3.11/git/config.meta.json +0 -1
  302. machineconfig/scripts/python/.mypy_cache/3.11/git/db.data.json +0 -1
  303. machineconfig/scripts/python/.mypy_cache/3.11/git/db.meta.json +0 -1
  304. machineconfig/scripts/python/.mypy_cache/3.11/git/diff.data.json +0 -1
  305. machineconfig/scripts/python/.mypy_cache/3.11/git/diff.meta.json +0 -1
  306. machineconfig/scripts/python/.mypy_cache/3.11/git/exc.data.json +0 -1
  307. machineconfig/scripts/python/.mypy_cache/3.11/git/exc.meta.json +0 -1
  308. machineconfig/scripts/python/.mypy_cache/3.11/git/index/__init__.data.json +0 -1
  309. machineconfig/scripts/python/.mypy_cache/3.11/git/index/__init__.meta.json +0 -1
  310. machineconfig/scripts/python/.mypy_cache/3.11/git/index/base.data.json +0 -1
  311. machineconfig/scripts/python/.mypy_cache/3.11/git/index/base.meta.json +0 -1
  312. machineconfig/scripts/python/.mypy_cache/3.11/git/index/fun.data.json +0 -1
  313. machineconfig/scripts/python/.mypy_cache/3.11/git/index/fun.meta.json +0 -1
  314. machineconfig/scripts/python/.mypy_cache/3.11/git/index/typ.data.json +0 -1
  315. machineconfig/scripts/python/.mypy_cache/3.11/git/index/typ.meta.json +0 -1
  316. machineconfig/scripts/python/.mypy_cache/3.11/git/index/util.data.json +0 -1
  317. machineconfig/scripts/python/.mypy_cache/3.11/git/index/util.meta.json +0 -1
  318. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/__init__.data.json +0 -1
  319. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/__init__.meta.json +0 -1
  320. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/base.data.json +0 -1
  321. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/base.meta.json +0 -1
  322. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/blob.data.json +0 -1
  323. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/blob.meta.json +0 -1
  324. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/commit.data.json +0 -1
  325. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/commit.meta.json +0 -1
  326. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/fun.data.json +0 -1
  327. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/fun.meta.json +0 -1
  328. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/__init__.data.json +0 -1
  329. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/__init__.meta.json +0 -1
  330. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/base.data.json +0 -1
  331. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/base.meta.json +0 -1
  332. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/root.data.json +0 -1
  333. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/root.meta.json +0 -1
  334. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/util.data.json +0 -1
  335. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/util.meta.json +0 -1
  336. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/tag.data.json +0 -1
  337. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/tag.meta.json +0 -1
  338. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/tree.data.json +0 -1
  339. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/tree.meta.json +0 -1
  340. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/util.data.json +0 -1
  341. machineconfig/scripts/python/.mypy_cache/3.11/git/objects/util.meta.json +0 -1
  342. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/__init__.data.json +0 -1
  343. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/__init__.meta.json +0 -1
  344. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/head.data.json +0 -1
  345. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/head.meta.json +0 -1
  346. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/log.data.json +0 -1
  347. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/log.meta.json +0 -1
  348. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/reference.data.json +0 -1
  349. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/reference.meta.json +0 -1
  350. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/remote.data.json +0 -1
  351. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/remote.meta.json +0 -1
  352. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/symbolic.data.json +0 -1
  353. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/symbolic.meta.json +0 -1
  354. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/tag.data.json +0 -1
  355. machineconfig/scripts/python/.mypy_cache/3.11/git/refs/tag.meta.json +0 -1
  356. machineconfig/scripts/python/.mypy_cache/3.11/git/remote.data.json +0 -1
  357. machineconfig/scripts/python/.mypy_cache/3.11/git/remote.meta.json +0 -1
  358. machineconfig/scripts/python/.mypy_cache/3.11/git/repo/__init__.data.json +0 -1
  359. machineconfig/scripts/python/.mypy_cache/3.11/git/repo/__init__.meta.json +0 -1
  360. machineconfig/scripts/python/.mypy_cache/3.11/git/repo/base.data.json +0 -1
  361. machineconfig/scripts/python/.mypy_cache/3.11/git/repo/base.meta.json +0 -1
  362. machineconfig/scripts/python/.mypy_cache/3.11/git/repo/fun.data.json +0 -1
  363. machineconfig/scripts/python/.mypy_cache/3.11/git/repo/fun.meta.json +0 -1
  364. machineconfig/scripts/python/.mypy_cache/3.11/git/types.data.json +0 -1
  365. machineconfig/scripts/python/.mypy_cache/3.11/git/types.meta.json +0 -1
  366. machineconfig/scripts/python/.mypy_cache/3.11/git/util.data.json +0 -1
  367. machineconfig/scripts/python/.mypy_cache/3.11/git/util.meta.json +0 -1
  368. machineconfig/scripts/python/.mypy_cache/3.11/glob.data.json +0 -1
  369. machineconfig/scripts/python/.mypy_cache/3.11/glob.meta.json +0 -1
  370. machineconfig/scripts/python/.mypy_cache/3.11/gzip.data.json +0 -1
  371. machineconfig/scripts/python/.mypy_cache/3.11/gzip.meta.json +0 -1
  372. machineconfig/scripts/python/.mypy_cache/3.11/importlib/__init__.data.json +0 -1
  373. machineconfig/scripts/python/.mypy_cache/3.11/importlib/__init__.meta.json +0 -1
  374. machineconfig/scripts/python/.mypy_cache/3.11/importlib/_abc.data.json +0 -1
  375. machineconfig/scripts/python/.mypy_cache/3.11/importlib/_abc.meta.json +0 -1
  376. machineconfig/scripts/python/.mypy_cache/3.11/importlib/_bootstrap.data.json +0 -1
  377. machineconfig/scripts/python/.mypy_cache/3.11/importlib/_bootstrap.meta.json +0 -1
  378. machineconfig/scripts/python/.mypy_cache/3.11/importlib/_bootstrap_external.data.json +0 -1
  379. machineconfig/scripts/python/.mypy_cache/3.11/importlib/_bootstrap_external.meta.json +0 -1
  380. machineconfig/scripts/python/.mypy_cache/3.11/importlib/abc.data.json +0 -1
  381. machineconfig/scripts/python/.mypy_cache/3.11/importlib/abc.meta.json +0 -1
  382. machineconfig/scripts/python/.mypy_cache/3.11/importlib/machinery.data.json +0 -1
  383. machineconfig/scripts/python/.mypy_cache/3.11/importlib/machinery.meta.json +0 -1
  384. machineconfig/scripts/python/.mypy_cache/3.11/importlib/metadata/__init__.data.json +0 -1
  385. machineconfig/scripts/python/.mypy_cache/3.11/importlib/metadata/__init__.meta.json +0 -1
  386. machineconfig/scripts/python/.mypy_cache/3.11/importlib/metadata/_meta.data.json +0 -1
  387. machineconfig/scripts/python/.mypy_cache/3.11/importlib/metadata/_meta.meta.json +0 -1
  388. machineconfig/scripts/python/.mypy_cache/3.11/importlib/readers.data.json +0 -1
  389. machineconfig/scripts/python/.mypy_cache/3.11/importlib/readers.meta.json +0 -1
  390. machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/__init__.data.json +0 -1
  391. machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/__init__.meta.json +0 -1
  392. machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/_common.data.json +0 -1
  393. machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/_common.meta.json +0 -1
  394. machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/abc.data.json +0 -1
  395. machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/abc.meta.json +0 -1
  396. machineconfig/scripts/python/.mypy_cache/3.11/inspect.data.json +0 -1
  397. machineconfig/scripts/python/.mypy_cache/3.11/inspect.meta.json +0 -1
  398. machineconfig/scripts/python/.mypy_cache/3.11/io.data.json +0 -1
  399. machineconfig/scripts/python/.mypy_cache/3.11/io.meta.json +0 -1
  400. machineconfig/scripts/python/.mypy_cache/3.11/itertools.data.json +0 -1
  401. machineconfig/scripts/python/.mypy_cache/3.11/itertools.meta.json +0 -1
  402. machineconfig/scripts/python/.mypy_cache/3.11/locale.data.json +0 -1
  403. machineconfig/scripts/python/.mypy_cache/3.11/locale.meta.json +0 -1
  404. machineconfig/scripts/python/.mypy_cache/3.11/logging/__init__.data.json +0 -1
  405. machineconfig/scripts/python/.mypy_cache/3.11/logging/__init__.meta.json +0 -1
  406. machineconfig/scripts/python/.mypy_cache/3.11/mimetypes.data.json +0 -1
  407. machineconfig/scripts/python/.mypy_cache/3.11/mimetypes.meta.json +0 -1
  408. machineconfig/scripts/python/.mypy_cache/3.11/mmap.data.json +0 -1
  409. machineconfig/scripts/python/.mypy_cache/3.11/mmap.meta.json +0 -1
  410. machineconfig/scripts/python/.mypy_cache/3.11/numbers.data.json +0 -1
  411. machineconfig/scripts/python/.mypy_cache/3.11/numbers.meta.json +0 -1
  412. machineconfig/scripts/python/.mypy_cache/3.11/opcode.data.json +0 -1
  413. machineconfig/scripts/python/.mypy_cache/3.11/opcode.meta.json +0 -1
  414. machineconfig/scripts/python/.mypy_cache/3.11/os/__init__.data.json +0 -1
  415. machineconfig/scripts/python/.mypy_cache/3.11/os/__init__.meta.json +0 -1
  416. machineconfig/scripts/python/.mypy_cache/3.11/os/path.data.json +0 -1
  417. machineconfig/scripts/python/.mypy_cache/3.11/os/path.meta.json +0 -1
  418. machineconfig/scripts/python/.mypy_cache/3.11/pathlib.data.json +0 -1
  419. machineconfig/scripts/python/.mypy_cache/3.11/pathlib.meta.json +0 -1
  420. machineconfig/scripts/python/.mypy_cache/3.11/platform.data.json +0 -1
  421. machineconfig/scripts/python/.mypy_cache/3.11/platform.meta.json +0 -1
  422. machineconfig/scripts/python/.mypy_cache/3.11/posixpath.data.json +0 -1
  423. machineconfig/scripts/python/.mypy_cache/3.11/posixpath.meta.json +0 -1
  424. machineconfig/scripts/python/.mypy_cache/3.11/re.data.json +0 -1
  425. machineconfig/scripts/python/.mypy_cache/3.11/re.meta.json +0 -1
  426. machineconfig/scripts/python/.mypy_cache/3.11/resource.data.json +0 -1
  427. machineconfig/scripts/python/.mypy_cache/3.11/resource.meta.json +0 -1
  428. machineconfig/scripts/python/.mypy_cache/3.11/shlex.data.json +0 -1
  429. machineconfig/scripts/python/.mypy_cache/3.11/shlex.meta.json +0 -1
  430. machineconfig/scripts/python/.mypy_cache/3.11/shutil.data.json +0 -1
  431. machineconfig/scripts/python/.mypy_cache/3.11/shutil.meta.json +0 -1
  432. machineconfig/scripts/python/.mypy_cache/3.11/signal.data.json +0 -1
  433. machineconfig/scripts/python/.mypy_cache/3.11/signal.meta.json +0 -1
  434. machineconfig/scripts/python/.mypy_cache/3.11/src/__init__.data.json +0 -1
  435. machineconfig/scripts/python/.mypy_cache/3.11/src/__init__.meta.json +0 -1
  436. machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/__init__.data.json +0 -1
  437. machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/__init__.meta.json +0 -1
  438. machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/scripts/__init__.data.json +0 -1
  439. machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/scripts/__init__.meta.json +0 -1
  440. machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/scripts/python/__init__.data.json +0 -1
  441. machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/scripts/python/__init__.meta.json +0 -1
  442. machineconfig/scripts/python/.mypy_cache/3.11/sre_compile.data.json +0 -1
  443. machineconfig/scripts/python/.mypy_cache/3.11/sre_compile.meta.json +0 -1
  444. machineconfig/scripts/python/.mypy_cache/3.11/sre_constants.data.json +0 -1
  445. machineconfig/scripts/python/.mypy_cache/3.11/sre_constants.meta.json +0 -1
  446. machineconfig/scripts/python/.mypy_cache/3.11/sre_parse.data.json +0 -1
  447. machineconfig/scripts/python/.mypy_cache/3.11/sre_parse.meta.json +0 -1
  448. machineconfig/scripts/python/.mypy_cache/3.11/stat.data.json +0 -1
  449. machineconfig/scripts/python/.mypy_cache/3.11/stat.meta.json +0 -1
  450. machineconfig/scripts/python/.mypy_cache/3.11/string.data.json +0 -1
  451. machineconfig/scripts/python/.mypy_cache/3.11/string.meta.json +0 -1
  452. machineconfig/scripts/python/.mypy_cache/3.11/struct.data.json +0 -1
  453. machineconfig/scripts/python/.mypy_cache/3.11/struct.meta.json +0 -1
  454. machineconfig/scripts/python/.mypy_cache/3.11/subprocess.data.json +0 -1
  455. machineconfig/scripts/python/.mypy_cache/3.11/subprocess.meta.json +0 -1
  456. machineconfig/scripts/python/.mypy_cache/3.11/sys/__init__.data.json +0 -1
  457. machineconfig/scripts/python/.mypy_cache/3.11/sys/__init__.meta.json +0 -1
  458. machineconfig/scripts/python/.mypy_cache/3.11/tarfile.data.json +0 -1
  459. machineconfig/scripts/python/.mypy_cache/3.11/tarfile.meta.json +0 -1
  460. machineconfig/scripts/python/.mypy_cache/3.11/tempfile.data.json +0 -1
  461. machineconfig/scripts/python/.mypy_cache/3.11/tempfile.meta.json +0 -1
  462. machineconfig/scripts/python/.mypy_cache/3.11/textwrap.data.json +0 -1
  463. machineconfig/scripts/python/.mypy_cache/3.11/textwrap.meta.json +0 -1
  464. machineconfig/scripts/python/.mypy_cache/3.11/threading.data.json +0 -1
  465. machineconfig/scripts/python/.mypy_cache/3.11/threading.meta.json +0 -1
  466. machineconfig/scripts/python/.mypy_cache/3.11/time.data.json +0 -1
  467. machineconfig/scripts/python/.mypy_cache/3.11/time.meta.json +0 -1
  468. machineconfig/scripts/python/.mypy_cache/3.11/types.data.json +0 -1
  469. machineconfig/scripts/python/.mypy_cache/3.11/types.meta.json +0 -1
  470. machineconfig/scripts/python/.mypy_cache/3.11/typing.data.json +0 -1
  471. machineconfig/scripts/python/.mypy_cache/3.11/typing.meta.json +0 -1
  472. machineconfig/scripts/python/.mypy_cache/3.11/typing_extensions.data.json +0 -1
  473. machineconfig/scripts/python/.mypy_cache/3.11/typing_extensions.meta.json +0 -1
  474. machineconfig/scripts/python/.mypy_cache/3.11/urllib/__init__.data.json +0 -1
  475. machineconfig/scripts/python/.mypy_cache/3.11/urllib/__init__.meta.json +0 -1
  476. machineconfig/scripts/python/.mypy_cache/3.11/urllib/parse.data.json +0 -1
  477. machineconfig/scripts/python/.mypy_cache/3.11/urllib/parse.meta.json +0 -1
  478. machineconfig/scripts/python/.mypy_cache/3.11/uuid.data.json +0 -1
  479. machineconfig/scripts/python/.mypy_cache/3.11/uuid.meta.json +0 -1
  480. machineconfig/scripts/python/.mypy_cache/3.11/warnings.data.json +0 -1
  481. machineconfig/scripts/python/.mypy_cache/3.11/warnings.meta.json +0 -1
  482. machineconfig/scripts/python/.mypy_cache/3.11/weakref.data.json +0 -1
  483. machineconfig/scripts/python/.mypy_cache/3.11/weakref.meta.json +0 -1
  484. machineconfig/scripts/python/.mypy_cache/3.11/zipfile/__init__.data.json +0 -1
  485. machineconfig/scripts/python/.mypy_cache/3.11/zipfile/__init__.meta.json +0 -1
  486. machineconfig/scripts/python/.mypy_cache/3.11/zlib.data.json +0 -1
  487. machineconfig/scripts/python/.mypy_cache/3.11/zlib.meta.json +0 -1
  488. machineconfig/scripts/python/.mypy_cache/CACHEDIR.TAG +0 -3
  489. machineconfig/scripts/python/__pycache__/cloud_repo_sync.cpython-311.pyc +0 -0
  490. machineconfig/scripts/python/__pycache__/gh_models.cpython-311.pyc +0 -0
  491. machineconfig/scripts/python/__pycache__/url2md.cpython-311.pyc +0 -0
  492. machineconfig/scripts/python/__pycache__/viewer.cpython-311.pyc +0 -0
  493. machineconfig/scripts/python/__pycache__/vscode_api.cpython-311.pyc +0 -0
  494. machineconfig/settings/__pycache__/__init__.cpython-311.pyc +0 -0
  495. machineconfig/settings/linters/.ruff_cache/.gitignore +0 -2
  496. machineconfig/settings/linters/.ruff_cache/CACHEDIR.TAG +0 -1
  497. machineconfig/settings/shells/ipy/profiles/default/__pycache__/__init__.cpython-311.pyc +0 -0
  498. machineconfig/settings/shells/ipy/profiles/default/startup/__pycache__/__init__.cpython-311.pyc +0 -0
  499. machineconfig/settings/shells/ipy/profiles/default/startup/__pycache__/playext.cpython-311.pyc +0 -0
  500. machineconfig/utils/ve_utils/ve1.py +0 -111
  501. machineconfig/utils/ve_utils/ve2.py +0 -155
  502. machineconfig-1.95.dist-info/RECORD +0 -712
  503. {machineconfig-1.95.dist-info → machineconfig-1.97.dist-info}/WHEEL +0 -0
  504. {machineconfig-1.95.dist-info → machineconfig-1.97.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,796 @@
1
+ """
2
+ OneDrive transaction functions for uploading and downloading files.
3
+
4
+ This module provides standalone functions to interact with Microsoft OneDrive
5
+ using the Microsoft Graph API. It supports both rclone tokens and direct OAuth2 authentication.
6
+
7
+ Key Features:
8
+ - Automatic token refresh when expired
9
+ - Persistent token storage
10
+ - Direct OAuth2 setup without rclone dependency
11
+ - Upload/download files with progress tracking
12
+ - Support for both small and large file uploads
13
+
14
+ Requirements:
15
+ pip install requests
16
+
17
+ Setup Options:
18
+
19
+ Option 1: Direct OAuth2 Setup (Recommended)
20
+ 1. Run setup_oauth_authentication() for first-time setup
21
+ 2. Follow the interactive prompts to authorize
22
+ 3. Tokens will be automatically saved and refreshed
23
+
24
+ Option 2: Using existing rclone token
25
+ 1. Update the RCLONE_TOKEN with your rclone token
26
+ 2. Set DRIVE_ID from your rclone config
27
+ 3. The system will automatically attempt to refresh when expired
28
+
29
+ Environment Variables (for OAuth2):
30
+ ONEDRIVE_CLIENT_ID: Your Azure App Registration Client ID
31
+ ONEDRIVE_CLIENT_SECRET: Your Client Secret (optional for public clients)
32
+ ONEDRIVE_REDIRECT_URI: Redirect URI (default: http://localhost:8080/callback)
33
+ """
34
+
35
+ import os
36
+ from datetime import datetime, timedelta
37
+ from pathlib import Path
38
+ from typing import Optional, Any
39
+ import requests
40
+ from urllib.parse import quote
41
+ import json
42
+
43
+
44
+ def get_rclone_token(section: str):
45
+ import platform
46
+ if platform.system() == "Windows":
47
+ rclone_file_path = Path(os.getenv("APPDATA", "")) / "rclone" / "rclone.conf"
48
+ else:
49
+ rclone_file_path = Path.home() / ".config" / "rclone" / "rclone.conf"
50
+ if rclone_file_path.exists():
51
+ import configparser
52
+ config = configparser.ConfigParser()
53
+ config.read(rclone_file_path)
54
+ if section in config:
55
+ results = config[section]
56
+ # something like {"token": {"access_token": "...", "expiry": "..."}, "drive_id": "...", "drive_type": "..."}
57
+ return dict(results)
58
+ return None
59
+
60
+ # Configuration - Will be loaded from rclone config
61
+ _cached_config = None
62
+
63
+ def get_config(section: str = "odp") -> dict[str, Any]:
64
+ """
65
+ Get OneDrive configuration from rclone config.
66
+
67
+ Args:
68
+ section: The rclone config section name (default: "odp")
69
+
70
+ Returns:
71
+ Dictionary containing token, drive_id, and drive_type
72
+ """
73
+ global _cached_config
74
+ if _cached_config is None:
75
+ rclone_config = get_rclone_token(section)
76
+ if not rclone_config:
77
+ raise Exception(f"Could not find rclone config section '{section}'. Please set up rclone first.")
78
+
79
+ # Parse the token from rclone config
80
+ token_str = rclone_config.get("token", "{}")
81
+ try:
82
+ token_data = json.loads(token_str)
83
+ except json.JSONDecodeError:
84
+ raise Exception(f"Invalid token format in rclone config section '{section}'")
85
+
86
+ _cached_config = {
87
+ "token": token_data,
88
+ "drive_id": rclone_config.get("drive_id"),
89
+ "drive_type": rclone_config.get("drive_type", "personal")
90
+ }
91
+
92
+ return _cached_config
93
+
94
+ def get_token() -> dict[str, Any]:
95
+ """Get the current token from rclone config."""
96
+ return get_config()["token"]
97
+
98
+ def get_drive_id():
99
+ """Get the drive ID from rclone config."""
100
+ return get_config()["drive_id"]
101
+
102
+ def get_drive_type():
103
+ """Get the drive type from rclone config."""
104
+ return get_config()["drive_type"]
105
+
106
+ def clear_config_cache():
107
+ """Clear the cached config to force reload from rclone."""
108
+ global _cached_config
109
+ _cached_config = None
110
+
111
+ # OAuth2 Configuration - You'll need to set these up in Azure App Registration
112
+ CLIENT_ID = os.getenv("ONEDRIVE_CLIENT_ID", "your_client_id_here")
113
+ CLIENT_SECRET = os.getenv("ONEDRIVE_CLIENT_SECRET", "your_client_secret_here") # Optional for public clients
114
+ REDIRECT_URI = os.getenv("ONEDRIVE_REDIRECT_URI", "http://localhost:8080/callback")
115
+
116
+ # Microsoft Graph API endpoints
117
+ GRAPH_API_BASE = "https://graph.microsoft.com/v1.0"
118
+ OAUTH_TOKEN_ENDPOINT = "https://login.microsoftonline.com/common/oauth2/v2.0/token"
119
+
120
+
121
+ def is_token_valid() -> bool:
122
+ """
123
+ Check if the current rclone token is still valid.
124
+
125
+ Returns:
126
+ True if token is valid, False otherwise
127
+ """
128
+ try:
129
+ token = get_token()
130
+ # Parse the expiry time from rclone format
131
+ expiry_str = token.get("expiry")
132
+ if not expiry_str:
133
+ return False
134
+
135
+ # Remove timezone info for parsing (rclone format includes timezone)
136
+ if '+' in expiry_str:
137
+ expiry_str = expiry_str.split('+')[0]
138
+ elif 'Z' in expiry_str:
139
+ expiry_str = expiry_str.replace('Z', '')
140
+
141
+ expiry_time = datetime.fromisoformat(expiry_str)
142
+ current_time = datetime.now()
143
+
144
+ # Add some buffer time (5 minutes)
145
+ return expiry_time > current_time + timedelta(minutes=5)
146
+ except Exception as e:
147
+ print(f"Error checking token validity: {e}")
148
+ return False
149
+
150
+
151
+ def get_access_token() -> Optional[str]:
152
+ """
153
+ Get access token, automatically refreshing if expired.
154
+
155
+ Returns:
156
+ Access token string or None if token cannot be obtained/refreshed
157
+ """
158
+ # First try to load token from file if it exists
159
+ load_token_from_file()
160
+
161
+ if not is_token_valid():
162
+ print("🔄 Access token has expired, attempting to refresh...")
163
+
164
+ # Try to refresh the token
165
+ refreshed_token = refresh_access_token()
166
+ if refreshed_token:
167
+ return refreshed_token["access_token"]
168
+ else:
169
+ print("❌ Failed to refresh token automatically!")
170
+ print("\n🔧 You have two options:")
171
+ print("1. Run setup_oauth_authentication() to set up OAuth")
172
+ print("2. Update your rclone token by running: rclone config reconnect odp")
173
+ return None
174
+
175
+ token = get_token()
176
+ return token.get("access_token")
177
+
178
+
179
+ def make_graph_request(method: str, endpoint: str, **kwargs: Any) -> requests.Response:
180
+ """
181
+ Make authenticated request to Microsoft Graph API.
182
+
183
+ Args:
184
+ method: HTTP method (GET, POST, PUT, etc.)
185
+ endpoint: API endpoint (without base URL)
186
+ **kwargs: Additional arguments for requests
187
+
188
+ Returns:
189
+ Response object
190
+
191
+ Raises:
192
+ Exception: If authentication fails or request fails
193
+ """
194
+ token = get_access_token()
195
+ if not token:
196
+ raise Exception("Failed to get valid access token")
197
+
198
+ headers = kwargs.get('headers', {})
199
+ headers['Authorization'] = f'Bearer {token}'
200
+ kwargs['headers'] = headers
201
+
202
+ url = f"{GRAPH_API_BASE}/{endpoint.lstrip('/')}"
203
+ response = requests.request(method, url, **kwargs)
204
+
205
+ return response
206
+
207
+
208
+ def push_to_onedrive(local_path: str, remote_path: str) -> bool:
209
+ """
210
+ Push a file from local system to OneDrive.
211
+
212
+ Args:
213
+ local_path: Path to the local file
214
+ remote_path: Path where the file should be stored in OneDrive
215
+ (e.g., "/Documents/myfile.txt")
216
+
217
+ Returns:
218
+ True if successful, False otherwise
219
+ """
220
+ local_file = Path(local_path)
221
+
222
+ if not local_file.exists():
223
+ print(f"Local file does not exist: {local_path}")
224
+ return False
225
+
226
+ if not local_file.is_file():
227
+ print(f"Path is not a file: {local_path}")
228
+ return False
229
+
230
+ # Ensure remote path starts with /
231
+ if not remote_path.startswith('/'):
232
+ remote_path = '/' + remote_path
233
+
234
+ # Create parent directories if they don't exist
235
+ remote_dir = os.path.dirname(remote_path)
236
+ if remote_dir and remote_dir != '/':
237
+ create_remote_directory(remote_dir)
238
+
239
+ try:
240
+ file_size = local_file.stat().st_size
241
+
242
+ # For small files (< 4MB), use simple upload
243
+ if file_size < 4 * 1024 * 1024:
244
+ return simple_upload(local_file, remote_path)
245
+ else:
246
+ return resumable_upload(local_file, remote_path)
247
+
248
+ except Exception as e:
249
+ print(f"Error uploading file: {e}")
250
+ return False
251
+
252
+
253
+ def simple_upload(local_file: Path, remote_path: str) -> bool:
254
+ """Upload small files using simple upload."""
255
+ try:
256
+ with open(local_file, 'rb') as f:
257
+ file_content = f.read()
258
+
259
+ # URL encode the remote path and use specific drive
260
+ encoded_path = quote(remote_path, safe='/')
261
+ drive_id = get_drive_id()
262
+ endpoint = f"drives/{drive_id}/root:{encoded_path}:/content"
263
+
264
+ response = make_graph_request('PUT', endpoint, data=file_content)
265
+
266
+ if response.status_code in [200, 201]:
267
+ print(f"Successfully uploaded: {local_file} -> {remote_path}")
268
+ return True
269
+ else:
270
+ print(f"Upload failed: {response.status_code} - {response.text}")
271
+ return False
272
+
273
+ except Exception as e:
274
+ print(f"Simple upload error: {e}")
275
+ return False
276
+
277
+
278
+ def resumable_upload(local_file: Path, remote_path: str) -> bool:
279
+ """Upload large files using resumable upload."""
280
+ try:
281
+ # Create upload session using specific drive
282
+ encoded_path = quote(remote_path, safe='/')
283
+ drive_id = get_drive_id()
284
+ endpoint = f"drives/{drive_id}/root:{encoded_path}:/createUploadSession"
285
+
286
+ item_data = {
287
+ "item": {
288
+ "@microsoft.graph.conflictBehavior": "replace",
289
+ "name": local_file.name
290
+ }
291
+ }
292
+
293
+ response = make_graph_request('POST', endpoint, json=item_data)
294
+
295
+ if response.status_code != 200:
296
+ print(f"Failed to create upload session: {response.status_code} - {response.text}")
297
+ return False
298
+
299
+ upload_url = response.json()['uploadUrl']
300
+ file_size = local_file.stat().st_size
301
+ chunk_size = 320 * 1024 # 320KB chunks
302
+
303
+ with open(local_file, 'rb') as f:
304
+ bytes_uploaded = 0
305
+
306
+ while bytes_uploaded < file_size:
307
+ chunk_data = f.read(chunk_size)
308
+ if not chunk_data:
309
+ break
310
+
311
+ chunk_end = min(bytes_uploaded + len(chunk_data) - 1, file_size - 1)
312
+
313
+ headers = {
314
+ 'Content-Range': f'bytes {bytes_uploaded}-{chunk_end}/{file_size}',
315
+ 'Content-Length': str(len(chunk_data))
316
+ }
317
+
318
+ chunk_response = requests.put(upload_url, data=chunk_data, headers=headers)
319
+
320
+ if chunk_response.status_code in [202, 200, 201]:
321
+ bytes_uploaded += len(chunk_data)
322
+ progress = (bytes_uploaded / file_size) * 100
323
+ print(f"Upload progress: {progress:.1f}%")
324
+ else:
325
+ print(f"Chunk upload failed: {chunk_response.status_code} - {chunk_response.text}")
326
+ return False
327
+
328
+ print(f"Successfully uploaded: {local_file} -> {remote_path}")
329
+ return True
330
+
331
+ except Exception as e:
332
+ print(f"Resumable upload error: {e}")
333
+ return False
334
+
335
+
336
+ def pull_from_onedrive(remote_path: str, local_path: str) -> bool:
337
+ """
338
+ Pull a file from OneDrive to local system.
339
+
340
+ Args:
341
+ remote_path: Path to the file in OneDrive (e.g., "/Documents/myfile.txt")
342
+ local_path: Path where the file should be saved locally
343
+
344
+ Returns:
345
+ True if successful, False otherwise
346
+ """
347
+ # Ensure remote path starts with /
348
+ if not remote_path.startswith('/'):
349
+ remote_path = '/' + remote_path
350
+
351
+ try:
352
+ # Get file metadata and download URL using specific drive
353
+ encoded_path = quote(remote_path, safe='/')
354
+ drive_id = get_drive_id()
355
+ endpoint = f"drives/{drive_id}/root:{encoded_path}"
356
+
357
+ response = make_graph_request('GET', endpoint)
358
+
359
+ if response.status_code == 404:
360
+ print(f"File not found in OneDrive: {remote_path}")
361
+ return False
362
+ elif response.status_code != 200:
363
+ print(f"Failed to get file info: {response.status_code} - {response.text}")
364
+ return False
365
+
366
+ file_info = response.json()
367
+
368
+ # Check if it's a file (not a folder)
369
+ if 'folder' in file_info:
370
+ print(f"Path is a folder, not a file: {remote_path}")
371
+ return False
372
+
373
+ # Get download URL
374
+ download_url = file_info.get('@microsoft.graph.downloadUrl')
375
+ if not download_url:
376
+ print("No download URL available")
377
+ return False
378
+
379
+ # Create local directory if it doesn't exist
380
+ local_file = Path(local_path)
381
+ local_file.parent.mkdir(parents=True, exist_ok=True)
382
+
383
+ # Download the file
384
+ download_response = requests.get(download_url, stream=True)
385
+ download_response.raise_for_status()
386
+
387
+ file_size = int(file_info.get('size', 0))
388
+ bytes_downloaded = 0
389
+
390
+ with open(local_file, 'wb') as f:
391
+ for chunk in download_response.iter_content(chunk_size=8192):
392
+ if chunk:
393
+ f.write(chunk)
394
+ bytes_downloaded += len(chunk)
395
+
396
+ if file_size > 0:
397
+ progress = (bytes_downloaded / file_size) * 100
398
+ print(f"Download progress: {progress:.1f}%")
399
+
400
+ print(f"Successfully downloaded: {remote_path} -> {local_path}")
401
+ return True
402
+
403
+ except Exception as e:
404
+ print(f"Error downloading file: {e}")
405
+ return False
406
+
407
+
408
+ def create_remote_directory(remote_path: str) -> bool:
409
+ """
410
+ Create a directory in OneDrive if it doesn't exist.
411
+
412
+ Args:
413
+ remote_path: Path to the directory in OneDrive
414
+
415
+ Returns:
416
+ True if successful or already exists, False otherwise
417
+ """
418
+ if not remote_path or remote_path == '/':
419
+ return True
420
+
421
+ # Ensure remote path starts with /
422
+ if not remote_path.startswith('/'):
423
+ remote_path = '/' + remote_path
424
+
425
+ try:
426
+ # Check if directory already exists using specific drive
427
+ encoded_path = quote(remote_path, safe='/')
428
+ drive_id = get_drive_id()
429
+ endpoint = f"drives/{drive_id}/root:{encoded_path}"
430
+
431
+ response = make_graph_request('GET', endpoint)
432
+
433
+ if response.status_code == 200:
434
+ # Directory already exists
435
+ return True
436
+ elif response.status_code != 404:
437
+ print(f"Error checking directory: {response.status_code} - {response.text}")
438
+ return False
439
+
440
+ # Create parent directory first
441
+ parent_dir = os.path.dirname(remote_path)
442
+ if parent_dir and parent_dir != '/':
443
+ if not create_remote_directory(parent_dir):
444
+ return False
445
+
446
+ # Create the directory
447
+ dir_name = os.path.basename(remote_path)
448
+ parent_encoded = quote(parent_dir if parent_dir else '/', safe='/')
449
+
450
+ if parent_dir and parent_dir != '/':
451
+ endpoint = f"drives/{drive_id}/root:{parent_encoded}:/children"
452
+ else:
453
+ endpoint = f"drives/{drive_id}/root/children"
454
+
455
+ folder_data = {
456
+ "name": dir_name,
457
+ "folder": {},
458
+ "@microsoft.graph.conflictBehavior": "replace"
459
+ }
460
+
461
+ response = make_graph_request('POST', endpoint, json=folder_data)
462
+
463
+ if response.status_code in [200, 201]:
464
+ return True
465
+ else:
466
+ print(f"Failed to create directory: {response.status_code} - {response.text}")
467
+ return False
468
+
469
+ except Exception as e:
470
+ print(f"Error creating directory: {e}")
471
+ return False
472
+
473
+
474
+ def refresh_access_token() -> Optional[dict[str, Any]]:
475
+ """
476
+ Refresh the access token using the refresh token.
477
+
478
+ Returns:
479
+ New token dictionary with access_token, refresh_token, and expiry, or None if failed
480
+ """
481
+ token = get_token()
482
+ refresh_token = token.get("refresh_token")
483
+ if not refresh_token:
484
+ print("ERROR: No refresh token available!")
485
+ return None
486
+
487
+ print("🔄 Refreshing access token...")
488
+
489
+ # Prepare the token refresh request
490
+ data = {
491
+ 'client_id': CLIENT_ID,
492
+ 'grant_type': 'refresh_token',
493
+ 'refresh_token': refresh_token,
494
+ 'scope': 'https://graph.microsoft.com/Files.ReadWrite.All offline_access'
495
+ }
496
+
497
+ # Add client secret if available (for confidential clients)
498
+ if CLIENT_SECRET and CLIENT_SECRET != "your_client_secret_here":
499
+ data['client_secret'] = CLIENT_SECRET
500
+
501
+ headers = {
502
+ 'Content-Type': 'application/x-www-form-urlencoded'
503
+ }
504
+
505
+ try:
506
+ response = requests.post(OAUTH_TOKEN_ENDPOINT, data=data, headers=headers)
507
+
508
+ if response.status_code == 200:
509
+ token_data = response.json()
510
+
511
+ # Calculate expiry time (tokens typically last 1 hour)
512
+ expires_in = token_data.get('expires_in', 3600) # Default to 1 hour
513
+ expiry_time = datetime.now() + timedelta(seconds=expires_in)
514
+
515
+ # Update the cached token configuration
516
+ new_token = {
517
+ 'access_token': token_data['access_token'],
518
+ 'token_type': token_data.get('token_type', 'Bearer'),
519
+ 'refresh_token': token_data.get('refresh_token', refresh_token), # Use new or keep old
520
+ 'expiry': expiry_time.isoformat()
521
+ }
522
+
523
+ # Update the cached config
524
+ global _cached_config
525
+ if _cached_config is not None:
526
+ _cached_config["token"] = new_token
527
+ else:
528
+ clear_config_cache() # Force reload on next access
529
+
530
+ print("✅ Access token refreshed successfully!")
531
+ print(f"🕒 New token expires at: {expiry_time}")
532
+
533
+ # Optionally save the new token to a file for persistence
534
+ save_token_to_file(new_token)
535
+
536
+ return new_token
537
+
538
+ else:
539
+ print(f"❌ Token refresh failed: {response.status_code}")
540
+ print(f"Response: {response.text}")
541
+ return None
542
+
543
+ except Exception as e:
544
+ print(f"❌ Error refreshing token: {e}")
545
+ return None
546
+
547
+
548
+ def save_token_to_file(token_data: dict[str, Any], file_path: Optional[str] = None) -> bool:
549
+ """
550
+ Save token data to a file for persistence.
551
+
552
+ Args:
553
+ token_data: Token dictionary to save
554
+ file_path: Optional path to save the token file
555
+
556
+ Returns:
557
+ True if successful, False otherwise
558
+ """
559
+ if not file_path:
560
+ # Default to a hidden file in user's home directory
561
+ file_path = os.path.expanduser("~/.onedrive_token.json")
562
+
563
+ try:
564
+ # Create directory if it doesn't exist
565
+ os.makedirs(os.path.dirname(file_path), exist_ok=True)
566
+
567
+ with open(file_path, 'w') as f:
568
+ json.dump(token_data, f, indent=2)
569
+
570
+ # Set restrictive permissions (readable only by owner)
571
+ os.chmod(file_path, 0o600)
572
+
573
+ print(f"💾 Token saved to: {file_path}")
574
+ return True
575
+
576
+ except Exception as e:
577
+ print(f"❌ Error saving token: {e}")
578
+ return False
579
+
580
+
581
+ def load_token_from_file(file_path: Optional[str] = None) -> Optional[dict[str, Any]]:
582
+ """
583
+ Load token data from a file.
584
+
585
+ Args:
586
+ file_path: Optional path to load the token file from
587
+
588
+ Returns:
589
+ Token dictionary or None if failed
590
+ """
591
+ if not file_path:
592
+ file_path = os.path.expanduser("~/.onedrive_token.json")
593
+
594
+ try:
595
+ if os.path.exists(file_path):
596
+ with open(file_path, 'r') as f:
597
+ token_data = json.load(f)
598
+
599
+ # Update the cached config token
600
+ global _cached_config
601
+ if _cached_config is not None:
602
+ _cached_config["token"] = token_data
603
+ else:
604
+ clear_config_cache() # Force reload on next access
605
+
606
+ print(f"📂 Token loaded from: {file_path}")
607
+ return token_data
608
+ else:
609
+ print(f"ℹ️ No saved token file found at: {file_path}")
610
+ return None
611
+
612
+ except Exception as e:
613
+ print(f"❌ Error loading token: {e}")
614
+ return None
615
+
616
+
617
+ def get_authorization_url() -> str:
618
+ """
619
+ Generate the authorization URL for initial OAuth setup.
620
+ This is needed only for the first-time setup to get the initial tokens.
621
+
622
+ Returns:
623
+ Authorization URL string
624
+ """
625
+ from urllib.parse import urlencode
626
+
627
+ params = {
628
+ 'client_id': CLIENT_ID,
629
+ 'response_type': 'code',
630
+ 'redirect_uri': REDIRECT_URI,
631
+ 'response_mode': 'query',
632
+ 'scope': 'https://graph.microsoft.com/Files.ReadWrite.All offline_access',
633
+ 'state': 'onedrive_auth'
634
+ }
635
+
636
+ auth_url = f"https://login.microsoftonline.com/common/oauth2/v2.0/authorize?{urlencode(params)}"
637
+ return auth_url
638
+
639
+
640
+ def exchange_authorization_code(authorization_code: str) -> Optional[dict[str, Any]]:
641
+ """
642
+ Exchange authorization code for initial tokens.
643
+ This is used during the first-time OAuth setup.
644
+
645
+ Args:
646
+ authorization_code: The authorization code received from the callback
647
+
648
+ Returns:
649
+ Token dictionary or None if failed
650
+ """
651
+ data = {
652
+ 'client_id': CLIENT_ID,
653
+ 'grant_type': 'authorization_code',
654
+ 'code': authorization_code,
655
+ 'redirect_uri': REDIRECT_URI,
656
+ 'scope': 'https://graph.microsoft.com/Files.ReadWrite.All offline_access'
657
+ }
658
+
659
+ # Add client secret if available
660
+ if CLIENT_SECRET and CLIENT_SECRET != "your_client_secret_here":
661
+ data['client_secret'] = CLIENT_SECRET
662
+
663
+ headers = {
664
+ 'Content-Type': 'application/x-www-form-urlencoded'
665
+ }
666
+
667
+ try:
668
+ response = requests.post(OAUTH_TOKEN_ENDPOINT, data=data, headers=headers)
669
+
670
+ if response.status_code == 200:
671
+ token_data = response.json()
672
+
673
+ # Calculate expiry time
674
+ expires_in = token_data.get('expires_in', 3600)
675
+ expiry_time = datetime.now() + timedelta(seconds=expires_in)
676
+
677
+ new_token = {
678
+ 'access_token': token_data['access_token'],
679
+ 'token_type': token_data.get('token_type', 'Bearer'),
680
+ 'refresh_token': token_data['refresh_token'],
681
+ 'expiry': expiry_time.isoformat()
682
+ }
683
+
684
+ # Update cached config and save
685
+ global _cached_config
686
+ if _cached_config is not None:
687
+ _cached_config["token"] = new_token
688
+ else:
689
+ clear_config_cache() # Force reload on next access
690
+ save_token_to_file(new_token)
691
+
692
+ print("✅ Initial tokens obtained successfully!")
693
+ return new_token
694
+
695
+ else:
696
+ print(f"❌ Token exchange failed: {response.status_code}")
697
+ print(f"Response: {response.text}")
698
+ return None
699
+
700
+ except Exception as e:
701
+ print(f"❌ Error exchanging authorization code: {e}")
702
+ return None
703
+
704
+
705
+ def setup_oauth_authentication():
706
+ """
707
+ Interactive setup for OAuth authentication.
708
+ Run this once to set up initial authentication.
709
+ """
710
+ print("🔧 Setting up OneDrive OAuth Authentication")
711
+ print("=" * 50)
712
+
713
+ if CLIENT_ID == "your_client_id_here":
714
+ print("❌ You need to set up Azure App Registration first!")
715
+ print("\n📋 Setup Instructions:")
716
+ print("1. Go to https://portal.azure.com")
717
+ print("2. Navigate to 'Azure Active Directory' > 'App registrations'")
718
+ print("3. Click 'New registration'")
719
+ print("4. Set Name: 'OneDrive API Access'")
720
+ print("5. Set Redirect URI: http://localhost:8080/callback")
721
+ print("6. After creation, copy the 'Application (client) ID'")
722
+ print("7. Go to 'API permissions' > 'Add permission' > 'Microsoft Graph'")
723
+ print("8. Add 'Files.ReadWrite.All' and 'offline_access' permissions")
724
+ print("9. Set environment variables:")
725
+ print(" export ONEDRIVE_CLIENT_ID='your_client_id'")
726
+ print(" export ONEDRIVE_REDIRECT_URI='http://localhost:8080/callback'")
727
+ return
728
+
729
+ print(f"Using Client ID: {CLIENT_ID}")
730
+ print(f"Redirect URI: {REDIRECT_URI}")
731
+
732
+ # Generate authorization URL
733
+ auth_url = get_authorization_url()
734
+ print("\n🌐 Please visit this URL to authorize the application:")
735
+ print(f"{auth_url}")
736
+
737
+ print("\n📋 After authorization, you'll be redirected to:")
738
+ print(f"{REDIRECT_URI}?code=AUTHORIZATION_CODE&state=onedrive_auth")
739
+ print("\n🔑 Copy the 'code' parameter from the URL and paste it below:")
740
+
741
+ auth_code = input("Authorization Code: ").strip()
742
+
743
+ if auth_code:
744
+ token_data = exchange_authorization_code(auth_code)
745
+ if token_data:
746
+ print("\n✅ OAuth setup completed successfully!")
747
+ print("🎉 You can now use the OneDrive functions without rclone!")
748
+ else:
749
+ print("\n❌ OAuth setup failed. Please try again.")
750
+ else:
751
+ print("\n❌ No authorization code provided.")
752
+
753
+
754
+ # Example usage
755
+ if __name__ == "__main__":
756
+ # Try to load existing token from file
757
+ load_token_from_file()
758
+
759
+ print("OneDrive transaction functions loaded.")
760
+ try:
761
+ config = get_config()
762
+ print(f"Drive ID: {get_drive_id()}")
763
+ print(f"Drive Type: {get_drive_type()}")
764
+
765
+ if is_token_valid():
766
+ print("✅ Token is valid and ready to use")
767
+ else:
768
+ print("⚠️ Token has expired or is invalid")
769
+
770
+ # Try to refresh automatically
771
+ if refresh_access_token():
772
+ print("✅ Token refreshed successfully")
773
+ else:
774
+ print("❌ Failed to refresh token automatically")
775
+ print("\n🔧 Setup Instructions:")
776
+ print("1. First-time setup: run setup_oauth_authentication()")
777
+ print("2. Or update rclone token: rclone config reconnect odp")
778
+ except Exception as e:
779
+ print(f"❌ Error loading rclone config: {e}")
780
+ print("Please ensure rclone is configured with an 'odp' section")
781
+
782
+ print("\n📚 Available Functions:")
783
+ print("• push_to_onedrive(local_path, remote_path)")
784
+ print("• pull_from_onedrive(remote_path, local_path)")
785
+ print("• refresh_access_token() - Refresh expired tokens")
786
+ print("• setup_oauth_authentication() - First-time OAuth setup")
787
+ print("• save_token_to_file(token_data) - Save tokens for persistence")
788
+ print("• load_token_from_file() - Load saved tokens")
789
+
790
+ print("\n💡 Example usage:")
791
+ print("push_to_onedrive('/home/user/document.pdf', '/Documents/document.pdf')")
792
+ print("pull_from_onedrive('/Documents/document.pdf', '/home/user/downloaded.pdf')")
793
+
794
+ # Uncomment to test with a file
795
+ # push_to_onedrive('/home/alex/Downloads/users.xlsx', '/Documents/users.xlsx')
796
+