machineconfig 1.94__py3-none-any.whl → 1.96__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.
- machineconfig/cluster/cloud_manager.py +445 -343
- machineconfig/cluster/data_transfer.py +63 -56
- machineconfig/cluster/distribute.py +284 -280
- machineconfig/cluster/file_manager.py +234 -237
- machineconfig/cluster/job_params.py +133 -133
- machineconfig/cluster/loader_runner.py +183 -149
- machineconfig/cluster/remote_machine.py +269 -252
- machineconfig/cluster/script_execution.py +215 -209
- machineconfig/cluster/script_notify_upon_completion.py +50 -43
- machineconfig/cluster/self_ssh.py +52 -54
- machineconfig/cluster/sessions_managers/__init__.py +0 -0
- machineconfig/cluster/sessions_managers/archive/__init__.py +0 -0
- machineconfig/{jobs/python → cluster/sessions_managers/archive}/create_zellij_template.py +5 -3
- machineconfig/cluster/sessions_managers/archive/session_managers.py +184 -0
- machineconfig/cluster/sessions_managers/demo_rich_zellij.py +0 -0
- machineconfig/cluster/sessions_managers/enhanced_command_runner.py +160 -0
- machineconfig/cluster/sessions_managers/wt_local.py +494 -0
- machineconfig/cluster/sessions_managers/wt_local_manager.py +577 -0
- machineconfig/cluster/sessions_managers/wt_remote.py +288 -0
- machineconfig/cluster/sessions_managers/wt_remote_manager.py +483 -0
- machineconfig/cluster/sessions_managers/wt_utils/layout_generator.py +196 -0
- machineconfig/cluster/sessions_managers/wt_utils/process_monitor.py +418 -0
- machineconfig/cluster/sessions_managers/wt_utils/remote_executor.py +175 -0
- machineconfig/cluster/sessions_managers/wt_utils/session_manager.py +300 -0
- machineconfig/cluster/sessions_managers/wt_utils/status_reporter.py +228 -0
- machineconfig/cluster/sessions_managers/zellij_local.py +418 -0
- machineconfig/cluster/sessions_managers/zellij_local_manager.py +533 -0
- machineconfig/cluster/sessions_managers/zellij_remote.py +229 -0
- machineconfig/cluster/sessions_managers/zellij_remote_manager.py +188 -0
- machineconfig/cluster/sessions_managers/zellij_utils/example_usage.py +64 -0
- machineconfig/cluster/sessions_managers/zellij_utils/layout_generator.py +126 -0
- machineconfig/cluster/sessions_managers/zellij_utils/process_monitor.py +334 -0
- machineconfig/cluster/sessions_managers/zellij_utils/remote_executor.py +68 -0
- machineconfig/cluster/sessions_managers/zellij_utils/session_manager.py +119 -0
- machineconfig/cluster/sessions_managers/zellij_utils/status_reporter.py +85 -0
- machineconfig/cluster/templates/cli_click.py +0 -1
- machineconfig/cluster/templates/cli_gooey.py +102 -104
- machineconfig/cluster/templates/run_cloud.py +51 -51
- machineconfig/cluster/templates/run_cluster.py +103 -59
- machineconfig/cluster/templates/run_remote.py +57 -58
- machineconfig/cluster/templates/utils.py +69 -36
- machineconfig/jobs/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/linux/msc/lid.sh +2 -4
- machineconfig/jobs/linux/msc/network.sh +3 -6
- machineconfig/jobs/python/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python/check_installations.py +258 -190
- machineconfig/jobs/python/create_bootable_media.py +7 -3
- machineconfig/jobs/python/python_cargo_build_share.py +50 -50
- machineconfig/jobs/python/python_ve_symlink.py +10 -10
- machineconfig/jobs/python/vscode/__pycache__/select_interpreter.cpython-311.pyc +0 -0
- machineconfig/jobs/python/vscode/api.py +3 -3
- machineconfig/jobs/python/vscode/link_ve.py +6 -6
- machineconfig/jobs/python/vscode/select_interpreter.py +13 -9
- machineconfig/jobs/python/vscode/sync_code.py +14 -11
- machineconfig/jobs/python_custom_installers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python_custom_installers/archive/ngrok.py +5 -5
- machineconfig/jobs/python_custom_installers/dev/aider.py +4 -4
- machineconfig/jobs/python_custom_installers/dev/alacritty.py +7 -6
- machineconfig/jobs/python_custom_installers/dev/brave.py +11 -7
- machineconfig/jobs/python_custom_installers/dev/bypass_paywall.py +7 -8
- machineconfig/jobs/python_custom_installers/dev/code.py +7 -5
- machineconfig/jobs/python_custom_installers/dev/cursor.py +66 -5
- machineconfig/jobs/python_custom_installers/dev/docker_desktop.py +4 -5
- machineconfig/jobs/python_custom_installers/dev/espanso.py +21 -17
- machineconfig/jobs/python_custom_installers/dev/goes.py +6 -12
- machineconfig/jobs/python_custom_installers/dev/lvim.py +7 -6
- machineconfig/jobs/python_custom_installers/dev/nerdfont.py +5 -5
- machineconfig/jobs/python_custom_installers/dev/redis.py +11 -7
- machineconfig/jobs/python_custom_installers/dev/wezterm.py +12 -8
- machineconfig/jobs/python_custom_installers/dev/winget.py +194 -0
- machineconfig/jobs/python_custom_installers/{dev/docker.py → docker.py} +12 -7
- machineconfig/jobs/python_custom_installers/gh.py +10 -9
- machineconfig/jobs/python_custom_installers/hx.py +36 -65
- machineconfig/jobs/python_custom_installers/scripts/linux/brave.sh +4 -8
- machineconfig/jobs/python_custom_installers/scripts/linux/docker.sh +5 -10
- machineconfig/jobs/python_custom_installers/scripts/linux/docker_start.sh +3 -6
- machineconfig/jobs/python_custom_installers/scripts/linux/edge.sh +3 -6
- machineconfig/jobs/python_custom_installers/scripts/linux/nerdfont.sh +5 -10
- machineconfig/jobs/python_custom_installers/scripts/linux/pgsql.sh +4 -8
- machineconfig/jobs/python_custom_installers/scripts/linux/redis.sh +5 -10
- machineconfig/jobs/python_custom_installers/scripts/linux/timescaledb.sh +6 -12
- machineconfig/jobs/python_custom_installers/scripts/linux/vscode.sh +101 -33
- machineconfig/jobs/python_custom_installers/scripts/linux/warp-cli.sh +5 -10
- machineconfig/jobs/python_custom_installers/scripts/linux/wezterm.sh +3 -6
- machineconfig/jobs/python_custom_installers/{dev/warp-cli.py → warp-cli.py} +5 -5
- machineconfig/jobs/python_generic_installers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python_generic_installers/config.json +133 -9
- machineconfig/jobs/python_generic_installers/dev/config.json +208 -37
- machineconfig/jobs/python_generic_installers/update.py +3 -0
- machineconfig/jobs/python_linux_installers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/jobs/python_linux_installers/config.json +42 -6
- machineconfig/jobs/python_linux_installers/dev/config.json +79 -11
- machineconfig/jobs/python_windows_installers/config.json +6 -0
- machineconfig/profile/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/profile/__pycache__/create.cpython-311.pyc +0 -0
- machineconfig/profile/__pycache__/shell.cpython-311.pyc +0 -0
- machineconfig/profile/create.py +5 -5
- machineconfig/profile/create_hardlinks.py +5 -5
- machineconfig/profile/shell.py +69 -63
- machineconfig/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/cloud/init.sh +9 -18
- machineconfig/scripts/linux/fire +5 -24
- machineconfig/scripts/linux/fire_agents +27 -0
- machineconfig/scripts/linux/share_cloud.sh +6 -12
- machineconfig/scripts/linux/wifi_conn +24 -0
- machineconfig/scripts/python/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/cloud_copy.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/cloud_mount.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/cloud_sync.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/croshell.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_backup_retrieve.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_devapps_install.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/devops_update_repos.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_agents.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_jobs.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/fire_jobs.cpython-313.pyc +0 -0
- machineconfig/scripts/python/__pycache__/get_zellij_cmd.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/repos.cpython-311.pyc +0 -0
- machineconfig/scripts/python/archive/im2text.py +28 -30
- machineconfig/scripts/python/archive/tmate_conn.py +10 -13
- machineconfig/scripts/python/archive/tmate_start.py +12 -16
- machineconfig/scripts/python/choose_wezterm_theme.py +12 -21
- machineconfig/scripts/python/cloud_copy.py +49 -104
- machineconfig/scripts/python/cloud_manager.py +77 -91
- machineconfig/scripts/python/cloud_mount.py +36 -46
- machineconfig/scripts/python/cloud_repo_sync.py +33 -79
- machineconfig/scripts/python/cloud_sync.py +35 -45
- machineconfig/scripts/python/croshell.py +70 -92
- machineconfig/scripts/python/devops.py +54 -113
- machineconfig/scripts/python/devops_add_identity.py +71 -109
- machineconfig/scripts/python/devops_add_ssh_key.py +42 -149
- machineconfig/scripts/python/devops_backup_retrieve.py +31 -119
- machineconfig/scripts/python/devops_devapps_install.py +6 -10
- machineconfig/scripts/python/devops_update_repos.py +5 -4
- machineconfig/scripts/python/dotfile.py +10 -7
- machineconfig/scripts/python/fire_agents.py +69 -0
- machineconfig/scripts/python/fire_jobs.py +117 -78
- machineconfig/scripts/python/ftpx.py +8 -8
- machineconfig/scripts/python/get_zellij_cmd.py +3 -3
- machineconfig/scripts/python/gh_models.py +6 -4
- machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/__init__.cpython-313.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/cloud_helpers.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers2.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/helpers4.cpython-313.pyc +0 -0
- machineconfig/scripts/python/helpers/__pycache__/repo_sync_helpers.cpython-311.pyc +0 -0
- machineconfig/scripts/python/helpers/cloud_helpers.py +49 -46
- machineconfig/scripts/python/helpers/helpers2.py +26 -39
- machineconfig/scripts/python/helpers/helpers4.py +23 -35
- machineconfig/scripts/python/helpers/repo_sync_helpers.py +33 -67
- machineconfig/scripts/python/mount_nfs.py +8 -11
- machineconfig/scripts/python/mount_nw_drive.py +4 -4
- machineconfig/scripts/python/mount_ssh.py +2 -2
- machineconfig/scripts/python/onetimeshare.py +56 -57
- machineconfig/scripts/python/pomodoro.py +55 -55
- machineconfig/scripts/python/repos.py +72 -49
- machineconfig/scripts/python/scheduler.py +70 -53
- machineconfig/scripts/python/snapshot.py +21 -24
- machineconfig/scripts/python/start_slidev.py +6 -5
- machineconfig/scripts/python/start_terminals.py +3 -1
- machineconfig/scripts/python/viewer.py +5 -4
- machineconfig/scripts/python/viewer_template.py +138 -140
- machineconfig/scripts/python/wifi_conn.py +416 -62
- machineconfig/scripts/python/wsl_windows_transfer.py +18 -3
- machineconfig/scripts/windows/fire.ps1 +27 -15
- machineconfig/settings/linters/.pylintrc +6 -7
- machineconfig/settings/lvim/windows/config.lua +0 -0
- machineconfig/settings/shells/bash/init.sh +6 -0
- machineconfig/settings/shells/ipy/profiles/default/startup/playext.py +7 -6
- machineconfig/settings/shells/pwsh/init.ps1 +6 -6
- machineconfig/settings/shells/wt/settings.json +51 -266
- machineconfig/setup_linux/nix/cli_installation.sh +9 -18
- machineconfig/setup_linux/others/openssh-server_add_pub_key.sh +3 -6
- machineconfig/setup_linux/web_shortcuts/all.sh +5 -10
- machineconfig/setup_linux/web_shortcuts/ascii_art.sh +7 -14
- machineconfig/setup_linux/web_shortcuts/croshell.sh +6 -12
- machineconfig/setup_linux/web_shortcuts/interactive.sh +39 -70
- machineconfig/setup_linux/web_shortcuts/ssh.sh +8 -16
- machineconfig/setup_linux/web_shortcuts/update_system.sh +7 -14
- machineconfig/setup_windows/wt_and_pwsh/set_pwsh_theme.py +3 -6
- machineconfig/setup_windows/wt_and_pwsh/set_wt_settings.py +27 -21
- machineconfig/utils/ai/browser_user_wrapper.py +60 -45
- machineconfig/utils/ai/generate_file_checklist.py +4 -7
- machineconfig/utils/ai/url2md.py +15 -7
- machineconfig/utils/cloud/onedrive/setup_oauth.py +59 -0
- machineconfig/utils/cloud/onedrive/transaction.py +796 -0
- machineconfig/utils/{utils_code.py → code.py} +25 -22
- machineconfig/utils/installer.py +81 -44
- machineconfig/utils/installer_utils/installer_abc.py +7 -6
- machineconfig/utils/installer_utils/installer_class.py +44 -25
- machineconfig/utils/io_save.py +107 -0
- machineconfig/utils/{utils_links.py → links.py} +28 -35
- machineconfig/utils/{utils_options.py → options.py} +14 -28
- machineconfig/utils/path.py +112 -0
- machineconfig/utils/path_reduced.py +608 -0
- machineconfig/utils/procs.py +134 -73
- machineconfig/utils/scheduling.py +312 -222
- machineconfig/utils/utils.py +19 -26
- machineconfig/utils/utils2.py +42 -0
- machineconfig/utils/utils5.py +84 -0
- machineconfig/utils/ve.py +49 -103
- {machineconfig-1.94.dist-info → machineconfig-1.96.dist-info}/METADATA +5 -3
- machineconfig-1.96.dist-info/RECORD +437 -0
- {machineconfig-1.94.dist-info → machineconfig-1.96.dist-info}/WHEEL +1 -1
- machineconfig/cluster/session_managers.py +0 -183
- machineconfig/cluster/templates/f.py +0 -4
- machineconfig/jobs/python/__pycache__/check_installations.cpython-311.pyc +0 -0
- machineconfig/jobs/python/__pycache__/checkout_version.cpython-311.pyc +0 -0
- machineconfig/jobs/python/__pycache__/python_ve_symlink.cpython-311.pyc +0 -0
- machineconfig/jobs/python/checkout_version.py +0 -123
- machineconfig/jobs/python/vscode/__pycache__/api.cpython-311.pyc +0 -0
- machineconfig/jobs/python/vscode/__pycache__/link_ve.cpython-311.pyc +0 -0
- machineconfig/jobs/python_custom_installers/__pycache__/hx.cpython-311.pyc +0 -0
- machineconfig/jobs/python_windows_installers/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/scripts/python/.mypy_cache/.gitignore +0 -2
- machineconfig/scripts/python/.mypy_cache/3.11/@plugins_snapshot.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/__future__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/__future__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_ast.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_ast.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_bz2.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_bz2.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_codecs.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_codecs.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_collections_abc.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_collections_abc.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_compression.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_compression.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_decimal.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_decimal.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_frozen_importlib.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_frozen_importlib.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_frozen_importlib_external.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_frozen_importlib_external.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_io.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_io.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_locale.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_locale.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_stat.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_stat.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_struct.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_struct.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_thread.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_thread.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_typeshed/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_typeshed/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_typeshed/importlib.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_typeshed/importlib.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_warnings.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_warnings.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_weakref.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_weakref.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_weakrefset.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/_weakrefset.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/abc.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/abc.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/argparse.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/argparse.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/ast.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/ast.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/binascii.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/binascii.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/builtins.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/builtins.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/bz2.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/bz2.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/calendar.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/calendar.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/codecs.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/codecs.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/collections/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/collections/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/collections/abc.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/collections/abc.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/configparser.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/configparser.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/contextlib.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/contextlib.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/dataclasses.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/dataclasses.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/datetime.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/datetime.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/decimal.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/decimal.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/dis.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/dis.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/_policybase.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/_policybase.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/charset.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/charset.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/contentmanager.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/contentmanager.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/errors.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/errors.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/header.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/header.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/message.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/message.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/policy.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/email/policy.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/enum.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/enum.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/fnmatch.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/fnmatch.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/functools.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/functools.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/gc.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/gc.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/genericpath.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/genericpath.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/getpass.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/getpass.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/cmd.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/cmd.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/compat.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/compat.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/config.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/config.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/db.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/db.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/diff.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/diff.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/exc.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/exc.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/index/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/index/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/index/base.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/index/base.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/index/fun.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/index/fun.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/index/typ.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/index/typ.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/index/util.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/index/util.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/base.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/base.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/blob.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/blob.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/commit.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/commit.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/fun.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/fun.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/base.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/base.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/root.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/root.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/util.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/submodule/util.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/tag.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/tag.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/tree.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/tree.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/util.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/objects/util.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/head.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/head.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/log.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/log.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/reference.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/reference.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/remote.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/remote.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/symbolic.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/symbolic.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/tag.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/refs/tag.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/remote.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/remote.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/repo/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/repo/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/repo/base.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/repo/base.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/repo/fun.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/repo/fun.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/types.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/types.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/util.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/git/util.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/glob.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/glob.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/gzip.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/gzip.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/_abc.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/_abc.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/_bootstrap.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/_bootstrap.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/_bootstrap_external.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/_bootstrap_external.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/abc.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/abc.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/machinery.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/machinery.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/metadata/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/metadata/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/metadata/_meta.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/metadata/_meta.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/readers.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/readers.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/_common.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/_common.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/abc.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/importlib/resources/abc.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/inspect.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/inspect.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/io.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/io.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/itertools.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/itertools.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/locale.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/locale.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/logging/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/logging/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/mimetypes.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/mimetypes.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/mmap.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/mmap.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/numbers.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/numbers.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/opcode.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/opcode.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/os/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/os/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/os/path.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/os/path.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/pathlib.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/pathlib.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/platform.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/platform.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/posixpath.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/posixpath.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/re.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/re.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/resource.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/resource.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/shlex.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/shlex.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/shutil.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/shutil.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/signal.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/signal.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/src/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/src/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/scripts/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/scripts/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/scripts/python/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/src/machineconfig/scripts/python/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/sre_compile.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/sre_compile.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/sre_constants.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/sre_constants.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/sre_parse.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/sre_parse.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/stat.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/stat.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/string.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/string.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/struct.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/struct.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/subprocess.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/subprocess.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/sys/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/sys/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/tarfile.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/tarfile.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/tempfile.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/tempfile.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/textwrap.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/textwrap.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/threading.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/threading.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/time.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/time.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/types.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/types.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/typing.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/typing.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/typing_extensions.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/typing_extensions.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/urllib/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/urllib/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/urllib/parse.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/urllib/parse.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/uuid.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/uuid.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/warnings.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/warnings.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/weakref.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/weakref.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/zipfile/__init__.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/zipfile/__init__.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/zlib.data.json +0 -1
- machineconfig/scripts/python/.mypy_cache/3.11/zlib.meta.json +0 -1
- machineconfig/scripts/python/.mypy_cache/CACHEDIR.TAG +0 -3
- machineconfig/scripts/python/__pycache__/cloud_repo_sync.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/gh_models.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/url2md.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/viewer.cpython-311.pyc +0 -0
- machineconfig/scripts/python/__pycache__/vscode_api.cpython-311.pyc +0 -0
- machineconfig/settings/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/settings/linters/.ruff_cache/.gitignore +0 -2
- machineconfig/settings/linters/.ruff_cache/CACHEDIR.TAG +0 -1
- machineconfig/settings/shells/ipy/profiles/default/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/settings/shells/ipy/profiles/default/startup/__pycache__/__init__.cpython-311.pyc +0 -0
- machineconfig/settings/shells/ipy/profiles/default/startup/__pycache__/playext.cpython-311.pyc +0 -0
- machineconfig/utils/utils_path.py +0 -151
- machineconfig/utils/ve_utils/ve1.py +0 -111
- machineconfig/utils/ve_utils/ve2.py +0 -142
- machineconfig-1.94.dist-info/RECORD +0 -710
- {machineconfig-1.94.dist-info → machineconfig-1.96.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
|
+
|