cloudscope 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cloudscope-0.3.0/PKG-INFO +241 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/pyproject.toml +2 -1
- cloudscope-0.2.0/PKG-INFO +0 -22
- {cloudscope-0.2.0 → cloudscope-0.3.0}/.github/workflows/python-publish.yml +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/.gitignore +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/README.md +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/package-lock.json +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/package.json +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/__init__.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/__main__.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/app.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/auth/__init__.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/auth/aws.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/auth/drive_oauth.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/auth/gcp.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/backends/__init__.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/backends/base.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/backends/drive.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/backends/gcs.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/backends/registry.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/backends/s3.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/config.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/models/__init__.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/models/cloud_file.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/models/sync_state.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/models/transfer.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/sync/__init__.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/sync/differ.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/sync/engine.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/sync/plan.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/sync/resolver.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/sync/state.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/transfer/__init__.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/transfer/manager.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/transfer/progress.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/__init__.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/commands.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/modals/__init__.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/modals/confirm_dialog.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/modals/download_dialog.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/modals/new_folder.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/modals/sync_dialog.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/modals/upload_dialog.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/screens/__init__.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/screens/auth_setup.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/screens/browse.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/screens/settings.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/screens/sync_config.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/styles/cloudscope.tcss +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/widgets/__init__.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/widgets/app_footer.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/widgets/breadcrumb.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/widgets/cloud_tree.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/widgets/file_table.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/widgets/preview_panel.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/widgets/status_bar.py +0 -0
- {cloudscope-0.2.0 → cloudscope-0.3.0}/src/cloudscope/tui/widgets/transfer_panel.py +0 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cloudscope
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: TUI for browsing and syncing files across S3, GCS, and Google Drive
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: aiofiles>=23.0.0
|
|
7
|
+
Requires-Dist: boto3>=1.28.0
|
|
8
|
+
Requires-Dist: google-api-python-client>=2.90.0
|
|
9
|
+
Requires-Dist: google-auth-httplib2>=0.1.0
|
|
10
|
+
Requires-Dist: google-auth-oauthlib>=1.0.0
|
|
11
|
+
Requires-Dist: google-cloud-storage>=2.10.0
|
|
12
|
+
Requires-Dist: humanize>=4.0.0
|
|
13
|
+
Requires-Dist: platformdirs>=3.0.0
|
|
14
|
+
Requires-Dist: textual>=1.0.0
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: moto>=4.0; extra == 'dev'
|
|
17
|
+
Requires-Dist: mypy>=1.5; extra == 'dev'
|
|
18
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
|
|
19
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
20
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
21
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
22
|
+
Requires-Dist: textual-dev>=1.0.0; extra == 'dev'
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# CloudScope
|
|
26
|
+
|
|
27
|
+
A terminal UI for browsing, downloading, uploading, and bi-directionally syncing files across **AWS S3**, **Google Cloud Storage**, and **Google Drive**.
|
|
28
|
+
|
|
29
|
+
Built with [Textual](https://textual.textualize.io/) for a keyboard-driven, Linear.app-inspired dark interface.
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
- **Multi-backend browsing** — Switch between S3, GCS, and Google Drive with a single keypress (`1`, `2`, `3`)
|
|
34
|
+
- **Lazy-loading tree sidebar** — Expand buckets/drives on demand without loading everything upfront
|
|
35
|
+
- **Sortable file table** — Click column headers to sort by name, size, modified date, or type
|
|
36
|
+
- **File preview** — Metadata panel appears when a file is highlighted (size, modified date, ETag, content type)
|
|
37
|
+
- **Downloads and uploads** — File picker dialogs for selecting local paths
|
|
38
|
+
- **Folder creation** — Create new folders/prefixes directly from the TUI
|
|
39
|
+
- **Bi-directional sync** — Three-way diff engine with conflict resolution (newer wins, local wins, remote wins, keep both, or ask)
|
|
40
|
+
- **Command palette** — Press `Ctrl+K` to search and run any command
|
|
41
|
+
- **Settings screen** — Configure default backend, AWS profile/region, GCP project, Drive OAuth credentials
|
|
42
|
+
- **Auth setup** — Guided authentication testing for each backend
|
|
43
|
+
|
|
44
|
+
## Requirements
|
|
45
|
+
|
|
46
|
+
- Python 3.11+
|
|
47
|
+
- AWS credentials (for S3) — environment variables, `~/.aws/credentials`, or IAM role
|
|
48
|
+
- GCP credentials (for GCS) — Application Default Credentials (`gcloud auth application-default login`)
|
|
49
|
+
- OAuth2 client secrets (for Google Drive) — `client_secrets.json` placed in config directory
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
### From source
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git clone https://github.com/your-username/cloudscope.git
|
|
57
|
+
cd cloudscope
|
|
58
|
+
pip install -e .
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### With dev dependencies
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install -e ".[dev]"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Run
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
cloudscope
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Or via module:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
python -m cloudscope
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Authentication Setup
|
|
80
|
+
|
|
81
|
+
### AWS S3
|
|
82
|
+
|
|
83
|
+
CloudScope uses standard boto3 credential resolution. Any of the following will work:
|
|
84
|
+
|
|
85
|
+
- Environment variables (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`)
|
|
86
|
+
- Shared credentials file (`~/.aws/credentials`)
|
|
87
|
+
- AWS config file (`~/.aws/config`) with named profiles
|
|
88
|
+
- IAM instance role (on EC2)
|
|
89
|
+
|
|
90
|
+
Configure a specific profile in Settings (`,`) or pass it through the config file at `~/.config/cloudscope/config.toml`.
|
|
91
|
+
|
|
92
|
+
### Google Cloud Storage
|
|
93
|
+
|
|
94
|
+
Uses [Application Default Credentials](https://cloud.google.com/docs/authentication/application-default-credentials):
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
gcloud auth application-default login
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Or point to a service account key in Settings.
|
|
101
|
+
|
|
102
|
+
### Google Drive
|
|
103
|
+
|
|
104
|
+
Requires OAuth2 consent flow:
|
|
105
|
+
|
|
106
|
+
1. Create OAuth credentials in the [Google Cloud Console](https://console.cloud.google.com/apis/credentials)
|
|
107
|
+
2. Download the client secrets JSON
|
|
108
|
+
3. Place it at `~/.config/cloudscope/drive_client_secrets.json` (or configure the path in Settings)
|
|
109
|
+
4. Press `a` in CloudScope to run the auth flow — a browser window will open for consent
|
|
110
|
+
|
|
111
|
+
The token is persisted at `~/.config/cloudscope/drive_token.json` and refreshed automatically.
|
|
112
|
+
|
|
113
|
+
## Keyboard Shortcuts
|
|
114
|
+
|
|
115
|
+
### Browsing
|
|
116
|
+
|
|
117
|
+
| Key | Action |
|
|
118
|
+
|---|---|
|
|
119
|
+
| `1` | Switch to S3 |
|
|
120
|
+
| `2` | Switch to GCS |
|
|
121
|
+
| `3` | Switch to Google Drive |
|
|
122
|
+
| `d` | Download selected file |
|
|
123
|
+
| `u` | Upload a file |
|
|
124
|
+
| `n` | Create new folder |
|
|
125
|
+
| `Delete` | Delete selected file |
|
|
126
|
+
| `r` | Refresh current view |
|
|
127
|
+
| `Tab` | Focus next panel |
|
|
128
|
+
| `Shift+Tab` | Focus previous panel |
|
|
129
|
+
| `Ctrl+K` | Open command palette |
|
|
130
|
+
| `s` | Open sync configuration |
|
|
131
|
+
| `,` | Open settings |
|
|
132
|
+
| `a` | Open auth setup |
|
|
133
|
+
| `?` | Show help |
|
|
134
|
+
| `q` | Quit |
|
|
135
|
+
|
|
136
|
+
### In file table
|
|
137
|
+
|
|
138
|
+
| Key | Action |
|
|
139
|
+
|---|---|
|
|
140
|
+
| `Up`/`Down` | Navigate files |
|
|
141
|
+
| `Enter` | Open folder or select file |
|
|
142
|
+
| Click column header | Sort by that column |
|
|
143
|
+
|
|
144
|
+
## Configuration
|
|
145
|
+
|
|
146
|
+
Settings are stored at `~/.config/cloudscope/config.toml`:
|
|
147
|
+
|
|
148
|
+
```toml
|
|
149
|
+
default_backend = "s3"
|
|
150
|
+
max_concurrent_transfers = 3
|
|
151
|
+
|
|
152
|
+
[backends.s3]
|
|
153
|
+
profile = "default"
|
|
154
|
+
region = "us-east-1"
|
|
155
|
+
|
|
156
|
+
[backends.gcs]
|
|
157
|
+
project = "my-project-id"
|
|
158
|
+
|
|
159
|
+
[backends.drive]
|
|
160
|
+
client_secrets_path = "/path/to/client_secrets.json"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
All settings can also be edited from the TUI via the Settings screen (`,`).
|
|
164
|
+
|
|
165
|
+
## Project Structure
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
src/cloudscope/
|
|
169
|
+
├── app.py # Main Textual application
|
|
170
|
+
├── config.py # TOML config management
|
|
171
|
+
├── __main__.py # CLI entry point
|
|
172
|
+
├── auth/
|
|
173
|
+
│ ├── aws.py # AWS profile listing and client creation
|
|
174
|
+
│ ├── gcp.py # GCS client creation (ADC or service account)
|
|
175
|
+
│ └── drive_oauth.py # Google Drive OAuth2 flow
|
|
176
|
+
├── backends/
|
|
177
|
+
│ ├── base.py # CloudBackend Protocol and error hierarchy
|
|
178
|
+
│ ├── registry.py # Backend factory (register/get/list)
|
|
179
|
+
│ ├── s3.py # AWS S3 backend (boto3)
|
|
180
|
+
│ ├── gcs.py # Google Cloud Storage backend
|
|
181
|
+
│ └── drive.py # Google Drive backend (path-to-ID cache, export)
|
|
182
|
+
├── models/
|
|
183
|
+
│ ├── cloud_file.py # CloudFile, CloudFileType, CloudLocation
|
|
184
|
+
│ ├── transfer.py # TransferJob, TransferDirection, TransferStatus
|
|
185
|
+
│ └── sync_state.py # SyncRecord, SyncConflict, SyncPlan, etc.
|
|
186
|
+
├── sync/
|
|
187
|
+
│ ├── state.py # SQLite-backed sync state persistence
|
|
188
|
+
│ ├── differ.py # Three-way diff algorithm
|
|
189
|
+
│ ├── resolver.py # Conflict resolution strategies
|
|
190
|
+
│ ├── plan.py # Converts SyncDiff to executable SyncPlan
|
|
191
|
+
│ └── engine.py # Sync orchestrator (diff → plan → execute)
|
|
192
|
+
├── transfer/
|
|
193
|
+
│ ├── manager.py # Concurrent transfer queue (asyncio.Semaphore)
|
|
194
|
+
│ └── progress.py # Progress adapter for SDK callbacks
|
|
195
|
+
└── tui/
|
|
196
|
+
├── commands.py # Command palette provider (Ctrl+K)
|
|
197
|
+
├── screens/
|
|
198
|
+
│ ├── browse.py # Main browsing screen
|
|
199
|
+
│ ├── settings.py # Settings form
|
|
200
|
+
│ ├── sync_config.py # Sync configuration and execution
|
|
201
|
+
│ └── auth_setup.py # Guided auth testing
|
|
202
|
+
├── modals/
|
|
203
|
+
│ ├── confirm_dialog.py
|
|
204
|
+
│ ├── download_dialog.py
|
|
205
|
+
│ ├── upload_dialog.py
|
|
206
|
+
│ ├── new_folder.py
|
|
207
|
+
│ └── sync_dialog.py # Sync conflict resolution
|
|
208
|
+
├── widgets/
|
|
209
|
+
│ ├── status_bar.py # AppHeader (slim 1-line header)
|
|
210
|
+
│ ├── app_footer.py # Context-sensitive keybind hints
|
|
211
|
+
│ ├── cloud_tree.py # Lazy-loading tree sidebar
|
|
212
|
+
│ ├── file_table.py # Sortable file listing (DataTable)
|
|
213
|
+
│ ├── breadcrumb.py # Path breadcrumb with › separators
|
|
214
|
+
│ ├── preview_panel.py # File metadata display
|
|
215
|
+
│ └── transfer_panel.py# Transfer progress bar
|
|
216
|
+
└── styles/
|
|
217
|
+
└── cloudscope.tcss # Linear-inspired dark theme
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Development
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
# Install with dev dependencies
|
|
224
|
+
pip install -e ".[dev]"
|
|
225
|
+
|
|
226
|
+
# Lint
|
|
227
|
+
ruff check src/
|
|
228
|
+
|
|
229
|
+
# Type check
|
|
230
|
+
mypy src/cloudscope/
|
|
231
|
+
|
|
232
|
+
# Test
|
|
233
|
+
pytest
|
|
234
|
+
|
|
235
|
+
# Run with Textual dev tools (live CSS reloading, etc.)
|
|
236
|
+
textual run --dev cloudscope.app:CloudScopeApp
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## License
|
|
240
|
+
|
|
241
|
+
MIT
|
|
@@ -4,8 +4,9 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cloudscope"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "TUI for browsing and syncing files across S3, GCS, and Google Drive"
|
|
9
|
+
readme = "README.md"
|
|
9
10
|
requires-python = ">=3.11"
|
|
10
11
|
dependencies = [
|
|
11
12
|
"textual>=1.0.0",
|
cloudscope-0.2.0/PKG-INFO
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: cloudscope
|
|
3
|
-
Version: 0.2.0
|
|
4
|
-
Summary: TUI for browsing and syncing files across S3, GCS, and Google Drive
|
|
5
|
-
Requires-Python: >=3.11
|
|
6
|
-
Requires-Dist: aiofiles>=23.0.0
|
|
7
|
-
Requires-Dist: boto3>=1.28.0
|
|
8
|
-
Requires-Dist: google-api-python-client>=2.90.0
|
|
9
|
-
Requires-Dist: google-auth-httplib2>=0.1.0
|
|
10
|
-
Requires-Dist: google-auth-oauthlib>=1.0.0
|
|
11
|
-
Requires-Dist: google-cloud-storage>=2.10.0
|
|
12
|
-
Requires-Dist: humanize>=4.0.0
|
|
13
|
-
Requires-Dist: platformdirs>=3.0.0
|
|
14
|
-
Requires-Dist: textual>=1.0.0
|
|
15
|
-
Provides-Extra: dev
|
|
16
|
-
Requires-Dist: moto>=4.0; extra == 'dev'
|
|
17
|
-
Requires-Dist: mypy>=1.5; extra == 'dev'
|
|
18
|
-
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
|
|
19
|
-
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
20
|
-
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
21
|
-
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
22
|
-
Requires-Dist: textual-dev>=1.0.0; extra == 'dev'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|