Skip to content

Commit

Permalink
curl fallback (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
jahwag authored Jul 27, 2024
1 parent 17d5f0a commit 9a37821
Show file tree
Hide file tree
Showing 7 changed files with 636 additions and 107 deletions.
44 changes: 42 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,47 @@ Set up automatic syncing at regular intervals:
claudesync schedule
```

### Providers

ClaudeSync offers two providers for interacting with the Claude.ai API:

1. **claude.ai (Default)**:
- Uses built-in Python libraries to make API requests.
- No additional dependencies required.
- Recommended for most users.

2. **claude.ai-curl**:
- Uses cURL to make API requests.
- Requires cURL to be installed on your system.
- Can be used as a workaround for certain issues, such as 403 Forbidden errors.

**Note for Windows Users**: To use the claude.ai-curl provider on Windows, you need to have cURL installed. This can be done by:
- Installing [Git for Windows](https://git-scm.com/download/win) (which includes cURL)
- Installing [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/en-us/windows/wsl/install)

Make sure cURL is accessible from your command line before using this provider.

### Troubleshooting

#### 403 Forbidden Error
If you encounter a 403 Forbidden error when using ClaudeSync, it might be due to an issue with the session key or API access. As a workaround, you can try using the `claude.ai-curl` provider:

1. Ensure cURL is installed on your system (see note above for Windows users).

2. Logout from your current session:
```bash
claudesync api logout
```

3. Login using the claude.ai-curl provider:
```bash
claudesync api login claude.ai-curl
```

4. Try your operation again.

If the issue persists, please check your network connection and ensure that you have the necessary permissions to access Claude.ai.

## Contributing

We welcome contributions! Please see our [Contributing Guidelines](CONTRIBUTING.md) for more information.
Expand All @@ -134,5 +175,4 @@ ClaudeSync is licensed under the MIT License. See the [LICENSE](LICENSE) file fo

---

Made with ❤️ by the ClaudeSync team
```
Made with ❤️ by the ClaudeSync team
92 changes: 46 additions & 46 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,46 +1,46 @@
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "claudesync"
version = "0.3.7"
authors = [
{name = "Jahziah Wagner", email = "[email protected]"},
]
description = "A tool to synchronize local files with Claude.ai projects"
license = {file = "LICENSE"}
readme = "README.md"
requires-python = ">=3.7"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]
dependencies = [
"Click",
"requests",
"pathspec",
"crontab",
"setuptools",
"pytest",
"pytest-cov",
"click_completion",
]

[project.urls]
"Homepage" = "https://github.com/jahwag/claudesync"
"Bug Tracker" = "https://github.com/jahwag/claudesync/issues"

[project.scripts]
claudesync = "claudesync.cli.main:cli"

[tool.setuptools.packages.find]
where = ["src"]
include = ["claudesync*"]


[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = "test_*.py"
addopts = "-v --cov=claudesync --cov-report=term-missing"
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "claudesync"
version = "0.3.8"
authors = [
{name = "Jahziah Wagner", email = "[email protected]"},
]
description = "A tool to synchronize local files with Claude.ai projects"
license = {file = "LICENSE"}
readme = "README.md"
requires-python = ">=3.7"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]
dependencies = [
"Click",
"requests",
"pathspec",
"crontab",
"setuptools",
"pytest",
"pytest-cov",
"click_completion",
]

[project.urls]
"Homepage" = "https://github.com/jahwag/claudesync"
"Bug Tracker" = "https://github.com/jahwag/claudesync/issues"

[project.scripts]
claudesync = "claudesync.cli.main:cli"

[tool.setuptools.packages.find]
where = ["src"]
include = ["claudesync*"]


[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = "test_*.py"
addopts = "-v --cov=claudesync --cov-report=term-missing"
84 changes: 43 additions & 41 deletions src/claudesync/provider_factory.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,43 @@
# src/claudesync/provider_factory.py

from .providers.base_provider import BaseProvider
from .providers.claude_ai import ClaudeAIProvider


def get_provider(provider_name=None, session_key=None) -> BaseProvider:
"""
Retrieve an instance of a provider class based on the provider name and session key.
This function serves as a factory to instantiate provider classes. It maintains a registry of available
providers. If a provider name is not specified, it returns a list of available provider names. If a provider
name is specified but not found in the registry, it raises a ValueError. If a session key is provided, it
is passed to the provider class constructor.
Args:
provider_name (str, optional): The name of the provider to retrieve. If None, returns a list of available
provider names.
session_key (str, optional): The session key to be used by the provider for authentication.
Defaults to None.
Returns:
BaseProvider: An instance of the requested provider class if both provider_name and session_key are provided.
list: A list of available provider names if provider_name is None.
Raises:
ValueError: If the specified provider_name is not found in the registry of providers.
"""
providers = {
"claude.ai": ClaudeAIProvider,
# Add other providers here as they are implemented
}

if provider_name is None:
return list(providers.keys())

provider_class = providers.get(provider_name)
if provider_class is None:
raise ValueError(f"Unsupported provider: {provider_name}")

return provider_class(session_key) if session_key else provider_class()
# src/claudesync/provider_factory.py

from .providers.base_provider import BaseProvider
from .providers.claude_ai import ClaudeAIProvider
from .providers.claude_ai_curl import ClaudeAICurlProvider


def get_provider(provider_name=None, session_key=None) -> BaseProvider:
"""
Retrieve an instance of a provider class based on the provider name and session key.
This function serves as a factory to instantiate provider classes. It maintains a registry of available
providers. If a provider name is not specified, it returns a list of available provider names. If a provider
name is specified but not found in the registry, it raises a ValueError. If a session key is provided, it
is passed to the provider class constructor.
Args:
provider_name (str, optional): The name of the provider to retrieve. If None, returns a list of available
provider names.
session_key (str, optional): The session key to be used by the provider for authentication.
Defaults to None.
Returns:
BaseProvider: An instance of the requested provider class if both provider_name and session_key are provided.
list: A list of available provider names if provider_name is None.
Raises:
ValueError: If the specified provider_name is not found in the registry of providers.
"""
providers = {
"claude.ai": ClaudeAIProvider,
"claude.ai-curl": ClaudeAICurlProvider,
# Add other providers here as they are implemented
}

if provider_name is None:
return list(providers.keys())

provider_class = providers.get(provider_name)
if provider_class is None:
raise ValueError(f"Unsupported provider: {provider_name}")

return provider_class(session_key) if session_key else provider_class()
11 changes: 11 additions & 0 deletions src/claudesync/providers/claude_ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,17 @@ def _make_request(self, method, endpoint, **kwargs):
# Update cookies with any new values from the response
self.config.update_cookies(response.cookies.get_dict())

if response.status_code == 403:
error_msg = (
"Received a 403 Forbidden error. Your session key might be invalid. "
"Please try logging out and logging in again. If the issue persists, "
"you can try using the claude.ai-curl provider as a workaround:\n"
"claudesync api logout\n"
"claudesync api login claude.ai-curl"
)
logger.error(error_msg)
raise ProviderError(error_msg)

response.raise_for_status()

if not response.content:
Expand Down
Loading

0 comments on commit 9a37821

Please sign in to comment.