open-edison 0.1.41__tar.gz → 0.1.42__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.
- {open_edison-0.1.41 → open_edison-0.1.42}/PKG-INFO +10 -10
- {open_edison-0.1.41 → open_edison-0.1.42}/README.md +9 -9
- {open_edison-0.1.41 → open_edison-0.1.42}/config.json +5 -38
- {open_edison-0.1.41 → open_edison-0.1.42}/hatch_build.py +19 -14
- {open_edison-0.1.41 → open_edison-0.1.42}/pyproject.toml +2 -2
- {open_edison-0.1.41 → open_edison-0.1.42}/src/cli.py +8 -2
- open_edison-0.1.42/src/mcp_importer/api.py +106 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/mcp_importer/quick_cli.py +15 -12
- {open_edison-0.1.41 → open_edison-0.1.42}/src/oauth_manager.py +1 -1
- open_edison-0.1.41/src/frontend_dist/assets/index-BUUcUfTt.js +0 -51
- open_edison-0.1.41/src/frontend_dist/assets/index-o6_8mdM8.css +0 -1
- open_edison-0.1.41/src/frontend_dist/index.html +0 -21
- open_edison-0.1.41/src/frontend_dist/sw.js +0 -71
- open_edison-0.1.41/src/mcp_importer/api.py +0 -196
- {open_edison-0.1.41 → open_edison-0.1.42}/.gitignore +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/LICENSE +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/desktop_ext/README.md +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/docs/README.md +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/docs/architecture/single_user_design.md +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/docs/core/configuration.md +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/docs/core/project_structure.md +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/docs/core/proxy_usage.md +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/docs/deployment/docker.md +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/docs/deployment/local.md +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/docs/development/contributing.md +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/docs/development/development_guide.md +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/docs/development/testing.md +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/docs/quick-reference/api_reference.md +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/docs/quick-reference/config_quick_start.md +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/installation_test/README.md +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/prompt_permissions.json +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/resource_permissions.json +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/__init__.py +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/__main__.py +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/config.py +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/config.pyi +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/events.py +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/mcp_importer/__init__.py +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/mcp_importer/__main__.py +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/mcp_importer/cli.py +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/mcp_importer/export_cli.py +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/mcp_importer/exporters.py +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/mcp_importer/import_api.py +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/mcp_importer/importers.py +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/mcp_importer/merge.py +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/mcp_importer/parsers.py +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/mcp_importer/paths.py +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/mcp_importer/types.py +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/middleware/data_access_tracker.py +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/middleware/session_tracking.py +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/permissions.py +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/server.py +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/single_user_mcp.py +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/src/telemetry.py +0 -0
- {open_edison-0.1.41 → open_edison-0.1.42}/tool_permissions.json +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: open-edison
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.42
|
4
4
|
Summary: Open-source MCP security, aggregation, and monitoring. Single-user, self-hosted MCP proxy.
|
5
5
|
Author-email: Hugo Berg <hugo@edison.watch>
|
6
6
|
License-File: LICENSE
|
@@ -76,7 +76,7 @@ Optionally, import your existing MCP configs from Cursor, VS Code, or Claude Cod
|
|
76
76
|
|
77
77
|
```bash
|
78
78
|
# From source (no install) — quick one-liner (add --dry-run to preview)
|
79
|
-
uv run python -m
|
79
|
+
uv run python -m mcp_importer.quick_cli --yes
|
80
80
|
```
|
81
81
|
|
82
82
|
<details>
|
@@ -139,29 +139,29 @@ OPEN_EDISON_CONFIG_DIR=~/edison-config open-edison run
|
|
139
139
|
|
140
140
|
```bash
|
141
141
|
# From source (no install)
|
142
|
-
uv run python -m
|
142
|
+
uv run python -m mcp_importer.quick_cli --yes
|
143
143
|
```
|
144
144
|
|
145
145
|
- Preview what will be imported (no writes):
|
146
146
|
|
147
147
|
```bash
|
148
|
-
uv run python -m
|
148
|
+
uv run python -m mcp_importer --source cursor --dry-run
|
149
149
|
```
|
150
150
|
|
151
151
|
- Import servers into Open Edison `config.json` (merge policy defaults to `skip`):
|
152
152
|
|
153
153
|
```bash
|
154
|
-
uv run python -m
|
155
|
-
uv run python -m
|
156
|
-
uv run python -m
|
154
|
+
uv run python -m mcp_importer --source cursor
|
155
|
+
uv run python -m mcp_importer --source vscode
|
156
|
+
uv run python -m mcp_importer --source claude-code
|
157
157
|
```
|
158
158
|
|
159
159
|
- Point your editor to Open Edison (backup original config and replace with a single Open Edison server):
|
160
160
|
|
161
161
|
```bash
|
162
|
-
uv run python -m
|
163
|
-
uv run python -m
|
164
|
-
uv run python -m
|
162
|
+
uv run python -m mcp_importer export --target cursor --yes
|
163
|
+
uv run python -m mcp_importer export --target vscode --yes
|
164
|
+
uv run python -m mcp_importer export --target claude-code --yes
|
165
165
|
```
|
166
166
|
|
167
167
|
</details>
|
@@ -52,7 +52,7 @@ Optionally, import your existing MCP configs from Cursor, VS Code, or Claude Cod
|
|
52
52
|
|
53
53
|
```bash
|
54
54
|
# From source (no install) — quick one-liner (add --dry-run to preview)
|
55
|
-
uv run python -m
|
55
|
+
uv run python -m mcp_importer.quick_cli --yes
|
56
56
|
```
|
57
57
|
|
58
58
|
<details>
|
@@ -115,29 +115,29 @@ OPEN_EDISON_CONFIG_DIR=~/edison-config open-edison run
|
|
115
115
|
|
116
116
|
```bash
|
117
117
|
# From source (no install)
|
118
|
-
uv run python -m
|
118
|
+
uv run python -m mcp_importer.quick_cli --yes
|
119
119
|
```
|
120
120
|
|
121
121
|
- Preview what will be imported (no writes):
|
122
122
|
|
123
123
|
```bash
|
124
|
-
uv run python -m
|
124
|
+
uv run python -m mcp_importer --source cursor --dry-run
|
125
125
|
```
|
126
126
|
|
127
127
|
- Import servers into Open Edison `config.json` (merge policy defaults to `skip`):
|
128
128
|
|
129
129
|
```bash
|
130
|
-
uv run python -m
|
131
|
-
uv run python -m
|
132
|
-
uv run python -m
|
130
|
+
uv run python -m mcp_importer --source cursor
|
131
|
+
uv run python -m mcp_importer --source vscode
|
132
|
+
uv run python -m mcp_importer --source claude-code
|
133
133
|
```
|
134
134
|
|
135
135
|
- Point your editor to Open Edison (backup original config and replace with a single Open Edison server):
|
136
136
|
|
137
137
|
```bash
|
138
|
-
uv run python -m
|
139
|
-
uv run python -m
|
140
|
-
uv run python -m
|
138
|
+
uv run python -m mcp_importer export --target cursor --yes
|
139
|
+
uv run python -m mcp_importer export --target vscode --yes
|
140
|
+
uv run python -m mcp_importer export --target claude-code --yes
|
141
141
|
```
|
142
142
|
|
143
143
|
</details>
|
@@ -38,10 +38,10 @@
|
|
38
38
|
"command": "npx",
|
39
39
|
"args": [
|
40
40
|
"-y",
|
41
|
-
"@
|
41
|
+
"@modelcontextprotocol/server-github"
|
42
42
|
],
|
43
43
|
"env": {
|
44
|
-
"
|
44
|
+
"GITHUB_PERSONAL_ACCESS_TOKEN": "your-github-token-here"
|
45
45
|
},
|
46
46
|
"enabled": false
|
47
47
|
},
|
@@ -62,10 +62,11 @@
|
|
62
62
|
"command": "npx",
|
63
63
|
"args": [
|
64
64
|
"-y",
|
65
|
-
"@supabase/mcp-server
|
65
|
+
"@supabase/mcp-server"
|
66
66
|
],
|
67
67
|
"env": {
|
68
|
-
"
|
68
|
+
"SUPABASE_URL": "https://YOUR_PROJECT.supabase.co",
|
69
|
+
"SUPABASE_ANON_KEY": "your-supabase-anon-key"
|
69
70
|
},
|
70
71
|
"enabled": false
|
71
72
|
},
|
@@ -80,40 +81,6 @@
|
|
80
81
|
"env": {},
|
81
82
|
"enabled": false
|
82
83
|
},
|
83
|
-
{
|
84
|
-
"name": "figma",
|
85
|
-
"command": "npx",
|
86
|
-
"args": [
|
87
|
-
"-y",
|
88
|
-
"figma-developer-mcp",
|
89
|
-
"--stdio"
|
90
|
-
],
|
91
|
-
"env": {
|
92
|
-
"FIGMA_API_KEY": "your-figma-api-key"
|
93
|
-
},
|
94
|
-
"enabled": false
|
95
|
-
},
|
96
|
-
{
|
97
|
-
"name": "figma_dev_mode",
|
98
|
-
"command": "npx",
|
99
|
-
"args": [
|
100
|
-
"-y",
|
101
|
-
"mcp-remote",
|
102
|
-
"http://127.0.0.1:3845/sse"
|
103
|
-
],
|
104
|
-
"env": {},
|
105
|
-
"enabled": false
|
106
|
-
},
|
107
|
-
{
|
108
|
-
"name": "playwright",
|
109
|
-
"command": "npx",
|
110
|
-
"args": [
|
111
|
-
"-y",
|
112
|
-
"@automatalabs/mcp-server-playwright"
|
113
|
-
],
|
114
|
-
"env": {},
|
115
|
-
"enabled": false
|
116
|
-
},
|
117
84
|
{
|
118
85
|
"name": "google_drive",
|
119
86
|
"command": "npx",
|
@@ -1,4 +1,3 @@
|
|
1
|
-
import os
|
2
1
|
import shutil
|
3
2
|
from pathlib import Path
|
4
3
|
|
@@ -21,14 +20,7 @@ class BuildHook(BuildHookInterface): # type: ignore
|
|
21
20
|
src_frontend_dist = project_root / "src" / "frontend_dist"
|
22
21
|
repo_frontend_dist = project_root / "frontend" / "dist"
|
23
22
|
|
24
|
-
#
|
25
|
-
enforce = os.environ.get("OPEN_EDISON_REQUIRE_FRONTEND", "").lower() in {"1", "true", "yes"}
|
26
|
-
if not enforce:
|
27
|
-
self.app.display_info(
|
28
|
-
"Skipping frontend_dist enforcement (set OPEN_EDISON_REQUIRE_FRONTEND=1 to enforce)"
|
29
|
-
)
|
30
|
-
return
|
31
|
-
|
23
|
+
# Always ensure frontend assets are available for packaging
|
32
24
|
# Fast path: already present in src/
|
33
25
|
if (src_frontend_dist / "index.html").exists():
|
34
26
|
self.app.display_info("frontend_dist already present; skipping build/copy")
|
@@ -42,8 +34,21 @@ class BuildHook(BuildHookInterface): # type: ignore
|
|
42
34
|
self.app.display_info("Copied frontend/dist -> src/frontend_dist for packaging")
|
43
35
|
return
|
44
36
|
|
45
|
-
#
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
37
|
+
# If no frontend assets are available, create a minimal placeholder
|
38
|
+
# This prevents build failures while still allowing the package to be built
|
39
|
+
if not src_frontend_dist.exists():
|
40
|
+
src_frontend_dist.mkdir(parents=True, exist_ok=True)
|
41
|
+
# Create a minimal index.html placeholder
|
42
|
+
placeholder_html = """<!DOCTYPE html>
|
43
|
+
<html>
|
44
|
+
<head>
|
45
|
+
<title>Open Edison Dashboard</title>
|
46
|
+
<meta charset="utf-8">
|
47
|
+
</head>
|
48
|
+
<body>
|
49
|
+
<h1>Open Edison Dashboard</h1>
|
50
|
+
<p>Frontend assets not available. Run 'make build_package' to build the full dashboard.</p>
|
51
|
+
</body>
|
52
|
+
</html>"""
|
53
|
+
(src_frontend_dist / "index.html").write_text(placeholder_html)
|
54
|
+
self.app.display_info("Created minimal frontend placeholder for packaging")
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "open-edison"
|
3
|
-
version = "0.1.
|
3
|
+
version = "0.1.42"
|
4
4
|
description = "Open-source MCP security, aggregation, and monitoring. Single-user, self-hosted MCP proxy."
|
5
5
|
readme = "README.md"
|
6
6
|
authors = [
|
@@ -82,7 +82,7 @@ include = [
|
|
82
82
|
"src/frontend_dist/**",
|
83
83
|
"docs/**",
|
84
84
|
]
|
85
|
-
|
85
|
+
|
86
86
|
|
87
87
|
[tool.hatch.build.targets.sdist.hooks.custom]
|
88
88
|
path = "hatch_build.py"
|
@@ -45,7 +45,7 @@ def _parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
|
45
45
|
# import-mcp: import MCP servers from other tools into config.json
|
46
46
|
sp_import = subparsers.add_parser(
|
47
47
|
"import-mcp",
|
48
|
-
help="Import MCP servers from other tools (Cursor,
|
48
|
+
help="Import MCP servers from other tools (Cursor, Windsurf, Cline, Claude Desktop, etc.)",
|
49
49
|
description=(
|
50
50
|
"Import MCP server configurations from other tools into Open Edison config.json.\n"
|
51
51
|
"Use --source to choose the tool and optional flags to control merging."
|
@@ -55,10 +55,16 @@ def _parse_args(argv: list[str] | None = None) -> argparse.Namespace:
|
|
55
55
|
"--source",
|
56
56
|
choices=[
|
57
57
|
"cursor",
|
58
|
+
"windsurf",
|
59
|
+
"cline",
|
60
|
+
"claude-desktop",
|
58
61
|
"vscode",
|
59
62
|
"claude-code",
|
63
|
+
"gemini-cli",
|
64
|
+
"codex",
|
65
|
+
"interactive",
|
60
66
|
],
|
61
|
-
default="
|
67
|
+
default="interactive",
|
62
68
|
help="Source application to import from",
|
63
69
|
)
|
64
70
|
sp_import.add_argument(
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# pyright: reportMissingImports=false, reportUnknownVariableType=false, reportUnknownMemberType=false, reportUnknownArgumentType=false, reportUnknownParameterType=false
|
2
|
+
from enum import Enum
|
3
|
+
from pathlib import Path
|
4
|
+
from typing import Any
|
5
|
+
|
6
|
+
from src.config import Config, MCPServerConfig, get_config_json_path
|
7
|
+
from src.mcp_importer import paths as _paths
|
8
|
+
from src.mcp_importer.exporters import export_to_claude_code, export_to_cursor, export_to_vscode
|
9
|
+
from src.mcp_importer.importers import (
|
10
|
+
import_from_claude_code as _import_from_claude_code,
|
11
|
+
)
|
12
|
+
from src.mcp_importer.importers import (
|
13
|
+
import_from_cursor as _import_from_cursor,
|
14
|
+
)
|
15
|
+
from src.mcp_importer.importers import (
|
16
|
+
import_from_vscode as _import_from_vscode,
|
17
|
+
)
|
18
|
+
from src.mcp_importer.merge import MergePolicy, merge_servers
|
19
|
+
|
20
|
+
|
21
|
+
class CLIENT(str, Enum):
|
22
|
+
CURSOR = "cursor"
|
23
|
+
VSCODE = "vscode"
|
24
|
+
CLAUDE_CODE = "claude-code"
|
25
|
+
|
26
|
+
|
27
|
+
def detect_clients() -> set[CLIENT]:
|
28
|
+
detected: set[CLIENT] = set()
|
29
|
+
if _paths.detect_cursor_config_path() is not None:
|
30
|
+
detected.add(CLIENT.CURSOR)
|
31
|
+
if _paths.detect_vscode_config_path() is not None:
|
32
|
+
detected.add(CLIENT.VSCODE)
|
33
|
+
if _paths.detect_claude_code_config_path() is not None:
|
34
|
+
detected.add(CLIENT.CLAUDE_CODE)
|
35
|
+
return detected
|
36
|
+
|
37
|
+
|
38
|
+
import_cursor = _import_from_cursor
|
39
|
+
import_vscode = _import_from_vscode
|
40
|
+
import_claude_code = _import_from_claude_code
|
41
|
+
|
42
|
+
|
43
|
+
def import_from(client: CLIENT) -> list[MCPServerConfig]:
|
44
|
+
if client == CLIENT.CURSOR:
|
45
|
+
return import_cursor()
|
46
|
+
if client == CLIENT.VSCODE:
|
47
|
+
return import_vscode()
|
48
|
+
if client == CLIENT.CLAUDE_CODE:
|
49
|
+
return import_claude_code()
|
50
|
+
raise ValueError(f"Unsupported client: {client}")
|
51
|
+
|
52
|
+
|
53
|
+
def save_imported_servers(
|
54
|
+
servers: list[MCPServerConfig],
|
55
|
+
*,
|
56
|
+
merge_policy: str = MergePolicy.SKIP,
|
57
|
+
config_dir: Path | None = None,
|
58
|
+
) -> Path:
|
59
|
+
target_path: Path = (
|
60
|
+
get_config_json_path() if config_dir is None else (Path(config_dir) / "config.json")
|
61
|
+
)
|
62
|
+
cfg: Config = Config(target_path)
|
63
|
+
merged = merge_servers(existing=cfg.mcp_servers, imported=servers, policy=merge_policy)
|
64
|
+
cfg.mcp_servers = merged
|
65
|
+
cfg.save(target_path)
|
66
|
+
return target_path
|
67
|
+
|
68
|
+
|
69
|
+
def export_edison_to(
|
70
|
+
client: CLIENT,
|
71
|
+
*,
|
72
|
+
url: str = "http://localhost:3000/mcp/",
|
73
|
+
api_key: str = "dev-api-key-change-me",
|
74
|
+
server_name: str = "open-edison",
|
75
|
+
dry_run: bool = False,
|
76
|
+
force: bool = False,
|
77
|
+
create_if_missing: bool = False,
|
78
|
+
) -> Any:
|
79
|
+
match client:
|
80
|
+
case CLIENT.CURSOR:
|
81
|
+
return export_to_cursor(
|
82
|
+
url=url,
|
83
|
+
api_key=api_key,
|
84
|
+
server_name=server_name,
|
85
|
+
dry_run=dry_run,
|
86
|
+
force=force,
|
87
|
+
create_if_missing=create_if_missing,
|
88
|
+
)
|
89
|
+
case CLIENT.VSCODE:
|
90
|
+
return export_to_vscode(
|
91
|
+
url=url,
|
92
|
+
api_key=api_key,
|
93
|
+
server_name=server_name,
|
94
|
+
dry_run=dry_run,
|
95
|
+
force=force,
|
96
|
+
create_if_missing=create_if_missing,
|
97
|
+
)
|
98
|
+
case CLIENT.CLAUDE_CODE:
|
99
|
+
return export_to_claude_code(
|
100
|
+
url=url,
|
101
|
+
api_key=api_key,
|
102
|
+
server_name=server_name,
|
103
|
+
dry_run=dry_run,
|
104
|
+
force=force,
|
105
|
+
create_if_missing=create_if_missing,
|
106
|
+
)
|
@@ -26,19 +26,10 @@ def run_cli(argv: list[str] | None = None) -> int:
|
|
26
26
|
)
|
27
27
|
parser.add_argument("--dry-run", action="store_true", help="Preview actions without writing")
|
28
28
|
parser.add_argument("--yes", action="store_true", help="Skip confirmations (no effect here)")
|
29
|
-
parser.add_argument(
|
30
|
-
"--source-client", type=CLIENT, help="Client to import from", required=False, default=None
|
31
|
-
)
|
32
29
|
args = parser.parse_args(argv)
|
33
30
|
|
34
31
|
detected = detect_clients()
|
35
|
-
|
36
|
-
if args.source_client:
|
37
|
-
client = args.source_client
|
38
|
-
assert client in detected, f"Client {client} not detected"
|
39
|
-
else:
|
40
|
-
client = _pick_first(detected)
|
41
|
-
print(f"Going to import from client: {client}")
|
32
|
+
client = _pick_first(detected)
|
42
33
|
if client is None:
|
43
34
|
print("No supported clients detected.")
|
44
35
|
return 2
|
@@ -48,8 +39,20 @@ def run_cli(argv: list[str] | None = None) -> int:
|
|
48
39
|
print(f"No servers found to import from '{client.value}'.")
|
49
40
|
return 0
|
50
41
|
|
51
|
-
|
52
|
-
|
42
|
+
if args.dry_run:
|
43
|
+
print(
|
44
|
+
f"[dry-run] Would import {len(servers)} server(s) from '{client.value}' and save to config.json"
|
45
|
+
)
|
46
|
+
# Exercise export path safely (no writes)
|
47
|
+
export_edison_to(client, dry_run=True, force=True, create_if_missing=True)
|
48
|
+
print(
|
49
|
+
f"[dry-run] Would export Open Edison to '{client.value}' (backup and replace editor MCP config)"
|
50
|
+
)
|
51
|
+
print("Dry-run complete.")
|
52
|
+
return 0
|
53
|
+
|
54
|
+
save_imported_servers(servers)
|
55
|
+
export_edison_to(client, dry_run=False, force=True, create_if_missing=True)
|
53
56
|
print(f"Completed quick import/export for {client.value}.")
|
54
57
|
return 0
|
55
58
|
|
@@ -7,7 +7,6 @@ Provides detection, token management, and authentication flow coordination.
|
|
7
7
|
|
8
8
|
import asyncio
|
9
9
|
from dataclasses import dataclass
|
10
|
-
from datetime import datetime, timedelta
|
11
10
|
from enum import Enum
|
12
11
|
from pathlib import Path
|
13
12
|
|
@@ -138,6 +137,7 @@ class OAuthManager:
|
|
138
137
|
expires_in = getattr(existing_tokens, "expires_in", None)
|
139
138
|
if expires_in:
|
140
139
|
# If expires_in is available, we can calculate expiration
|
140
|
+
from datetime import datetime, timedelta
|
141
141
|
|
142
142
|
expiry = datetime.now() + timedelta(seconds=expires_in)
|
143
143
|
token_expires_at = expiry.isoformat()
|