github-sync-agent 0.1.3__py3-none-any.whl → 0.2.0__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.
github_sync/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.3"
1
+ __version__ = "0.2.0"
github_sync/api.py ADDED
@@ -0,0 +1,111 @@
1
+ """HTTP API for the GitHub Sync Agent."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import logging
7
+
8
+ from fastapi import Depends, FastAPI, HTTPException, Security
9
+ from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
10
+ from pydantic import BaseModel, Field
11
+
12
+ from . import __version__
13
+ from . import agent
14
+
15
+ LOG = logging.getLogger(__name__)
16
+ _security = HTTPBearer(auto_error=False)
17
+
18
+
19
+ class SyncRequest(BaseModel):
20
+ dry_run: bool = False
21
+
22
+
23
+ class SyncResult(BaseModel):
24
+ project: str
25
+ success: bool
26
+
27
+
28
+ class SyncResponse(BaseModel):
29
+ success: bool
30
+ synced: int
31
+ failed: int
32
+ results: list[SyncResult]
33
+
34
+
35
+ class StatusRow(BaseModel):
36
+ project: str
37
+ root: str
38
+ git: bool
39
+ remote: bool
40
+ changes: bool
41
+ on_github: bool
42
+
43
+
44
+ class StatusResponse(BaseModel):
45
+ github_user: str
46
+ projects_root: list[str]
47
+ projects: list[StatusRow]
48
+
49
+
50
+ def create_app(cfg: dict) -> FastAPI:
51
+ api_key: str = cfg.get("api_key") or ""
52
+
53
+ app = FastAPI(
54
+ title="GitHub Sync Agent API",
55
+ description="Trigger and inspect GitHub sync operations over HTTP.",
56
+ version=__version__,
57
+ )
58
+
59
+ async def require_auth(
60
+ credentials: HTTPAuthorizationCredentials | None = Security(_security),
61
+ ) -> None:
62
+ if not api_key:
63
+ return
64
+ if credentials is None or credentials.credentials != api_key:
65
+ raise HTTPException(status_code=401, detail="Invalid or missing API key")
66
+
67
+ @app.get("/health")
68
+ async def health() -> dict[str, str]:
69
+ return {"status": "ok", "version": __version__}
70
+
71
+ @app.get("/status", response_model=StatusResponse, dependencies=[Depends(require_auth)])
72
+ async def status() -> StatusResponse:
73
+ rows = await asyncio.to_thread(agent.get_status, cfg)
74
+ return StatusResponse(
75
+ github_user=cfg["github_user"],
76
+ projects_root=cfg["projects_root"],
77
+ projects=[StatusRow(**row) for row in rows],
78
+ )
79
+
80
+ @app.post("/sync", response_model=SyncResponse, dependencies=[Depends(require_auth)])
81
+ async def sync_all(body: SyncRequest = SyncRequest()) -> SyncResponse:
82
+ return await _run_sync(cfg, project=None, dry_run=body.dry_run)
83
+
84
+ @app.post("/sync/{project}", response_model=SyncResponse, dependencies=[Depends(require_auth)])
85
+ async def sync_one(project: str, body: SyncRequest = SyncRequest()) -> SyncResponse:
86
+ return await _run_sync(cfg, project=project, dry_run=body.dry_run)
87
+
88
+ return app
89
+
90
+
91
+ async def _run_sync(cfg: dict, project: str | None, dry_run: bool) -> SyncResponse:
92
+ try:
93
+ projects = await asyncio.to_thread(agent.discover_projects, cfg, project)
94
+ except ValueError as exc:
95
+ raise HTTPException(status_code=404, detail=str(exc)) from exc
96
+
97
+ results: list[SyncResult] = []
98
+ for path in projects:
99
+ ok = await asyncio.to_thread(agent.sync_project, path, cfg, dry_run)
100
+ results.append(SyncResult(project=path.name, success=ok))
101
+ level = logging.INFO if ok else logging.ERROR
102
+ LOG.log(level, "%s %s", path.name, "synced" if ok else "FAILED")
103
+
104
+ synced = sum(1 for r in results if r.success)
105
+ failed = len(results) - synced
106
+ return SyncResponse(
107
+ success=failed == 0,
108
+ synced=synced,
109
+ failed=failed,
110
+ results=results,
111
+ )
github_sync/cli.py CHANGED
@@ -207,8 +207,8 @@ def init() -> None:
207
207
 
208
208
  click.echo()
209
209
 
210
- # ── 6. SSH key setup ────────────────────────────────────────────────────
211
- click.echo(click.style(" Setting up SSH key for GitHub...", bold=True))
210
+ # ── 6. SSH setup + verification ─────────────────────────────────────────
211
+ click.echo(click.style(" Setting up SSH for GitHub...", bold=True))
212
212
 
213
213
  if not setup.ssh_key_exists():
214
214
  click.echo(" No SSH key found — generating one...")
@@ -220,17 +220,25 @@ def init() -> None:
220
220
  else:
221
221
  click.echo(" ✓ SSH key already exists")
222
222
 
223
- setup.add_github_host_key()
223
+ setup.add_github_host_keys()
224
224
 
225
- if not setup.ssh_key_on_github():
226
- click.echo(" Uploading SSH key to GitHub...")
227
- if setup.upload_ssh_key_to_github():
228
- click.echo(" ✓ SSH key added to GitHub")
229
- else:
230
- click.echo(click.style(" ✗ Failed to upload SSH key.", fg="yellow"))
231
- click.echo(" Run manually: gh ssh-key add ~/.ssh/id_ed25519.pub")
225
+ click.echo(" Verifying SSH connection to GitHub...")
226
+ ok, msg, fixes = setup.ensure_github_ssh()
227
+
228
+ for fix in fixes:
229
+ click.echo(f" → applied fix: {fix}")
230
+
231
+ if ok:
232
+ user = setup.ssh_auth_username(msg) or github_user
233
+ click.echo(f" ✓ SSH verified as {user}")
232
234
  else:
233
- click.echo(" SSH key already on GitHub")
235
+ click.echo(click.style(" SSH verification failed.", fg="red"))
236
+ click.echo(f" {msg}")
237
+ click.echo()
238
+ click.echo(" Manual checks:")
239
+ click.echo(" ssh -T git@github.com")
240
+ click.echo(" gh ssh-key add ~/.ssh/id_ed25519.pub")
241
+ sys.exit(1)
234
242
 
235
243
  click.echo()
236
244
 
@@ -245,8 +253,43 @@ def init() -> None:
245
253
  click.echo(f" github-sync status # see all your projects")
246
254
  click.echo(f" github-sync sync --dry-run # preview what will be synced")
247
255
  click.echo(f" github-sync sync # sync everything to GitHub")
256
+ click.echo(f" github-sync serve # start HTTP API (requires [api] extra)")
257
+ click.echo()
258
+
259
+
260
+ # ---------------------------------------------------------------------------
261
+ # serve
262
+ # ---------------------------------------------------------------------------
263
+
264
+ @cli.command()
265
+ @click.option("--host", default=None, help="Bind address (default: api_host from config)")
266
+ @click.option("--port", default=None, type=int, help="Port (default: api_port from config)")
267
+ @click.pass_context
268
+ def serve(ctx: click.Context, host: str | None, port: int | None) -> None:
269
+ """Start the HTTP API server."""
270
+ try:
271
+ import uvicorn
272
+ except ImportError:
273
+ click.echo("Error: API dependencies not installed.", err=True)
274
+ click.echo("Run: pip install 'github-sync-agent[api]'", err=True)
275
+ sys.exit(1)
276
+
277
+ cfg = ctx.obj["cfg"]
278
+ _setup_logging(cfg)
279
+
280
+ from .api import create_app
281
+
282
+ bind_host = host or cfg.get("api_host", "127.0.0.1")
283
+ bind_port = port or int(cfg.get("api_port", 8741))
284
+ app = create_app(cfg)
285
+
286
+ auth_note = "API key required" if cfg.get("api_key") else "no auth (localhost only recommended)"
287
+ click.echo(f"Starting GitHub Sync API on http://{bind_host}:{bind_port} ({auth_note})")
288
+ click.echo("Endpoints: GET /health GET /status POST /sync POST /sync/{project}")
248
289
  click.echo()
249
290
 
291
+ uvicorn.run(app, host=bind_host, port=bind_port, log_level=cfg.get("log_level", "info").lower())
292
+
250
293
 
251
294
  # ---------------------------------------------------------------------------
252
295
  # Entry point
github_sync/config.py CHANGED
@@ -26,6 +26,9 @@ def _apply_defaults(cfg: dict) -> None:
26
26
  cfg.setdefault("exclude", [])
27
27
  cfg.setdefault("log_file", str(Path.home() / ".local" / "share" / "github-sync" / "github-sync.log"))
28
28
  cfg.setdefault("log_level", "INFO")
29
+ cfg.setdefault("api_host", "127.0.0.1")
30
+ cfg.setdefault("api_port", 8741)
31
+ cfg.setdefault("api_key", "")
29
32
 
30
33
  if not cfg.get("github_user"):
31
34
  raise ValueError("Missing required config key: 'github_user'")
github_sync/setup.py CHANGED
@@ -3,6 +3,8 @@ Setup helpers used by `github-sync init`.
3
3
  Checks prerequisites, detects credentials, sets up SSH.
4
4
  """
5
5
 
6
+ import platform
7
+ import re
6
8
  import shutil
7
9
  import subprocess
8
10
  from pathlib import Path
@@ -49,6 +51,16 @@ def get_github_username() -> str | None:
49
51
  # ---------------------------------------------------------------------------
50
52
 
51
53
  DEFAULT_KEY = Path.home() / ".ssh" / "id_ed25519"
54
+ SSH_CONFIG = Path.home() / ".ssh" / "config"
55
+
56
+ GITHUB_PORT443_BLOCK = """
57
+ # Added by github-sync — use port 443 when port 22 is blocked
58
+ Host github.com
59
+ HostName ssh.github.com
60
+ Port 443
61
+ User git
62
+ IdentityFile ~/.ssh/id_ed25519
63
+ """
52
64
 
53
65
 
54
66
  def ssh_key_exists() -> bool:
@@ -56,6 +68,7 @@ def ssh_key_exists() -> bool:
56
68
 
57
69
 
58
70
  def generate_ssh_key(passphrase: str = "") -> bool:
71
+ Path.home().joinpath(".ssh").mkdir(mode=0o700, exist_ok=True)
59
72
  result = subprocess.run(
60
73
  ["ssh-keygen", "-t", "ed25519", "-C", "github-sync", "-f", str(DEFAULT_KEY), "-N", passphrase],
61
74
  capture_output=True, text=True,
@@ -63,29 +76,101 @@ def generate_ssh_key(passphrase: str = "") -> bool:
63
76
  return result.returncode == 0
64
77
 
65
78
 
66
- def add_github_host_key() -> bool:
67
- result = subprocess.run(
68
- ["ssh-keyscan", "-t", "ed25519", "github.com"],
69
- capture_output=True, text=True,
70
- )
71
- if result.returncode != 0:
79
+ def _append_known_hosts(hostname: str, port: int | None = None) -> bool:
80
+ cmd = ["ssh-keyscan", "-t", "ed25519"]
81
+ if port:
82
+ cmd.extend(["-p", str(port)])
83
+ cmd.append(hostname)
84
+
85
+ result = subprocess.run(cmd, capture_output=True, text=True)
86
+ if result.returncode != 0 or not result.stdout.strip():
72
87
  return False
88
+
73
89
  known_hosts = Path.home() / ".ssh" / "known_hosts"
74
90
  known_hosts.parent.mkdir(mode=0o700, exist_ok=True)
75
91
  existing = known_hosts.read_text() if known_hosts.exists() else ""
76
- if "github.com" not in existing:
92
+ if hostname not in existing:
77
93
  with open(known_hosts, "a") as f:
78
94
  f.write(result.stdout)
79
95
  return True
80
96
 
81
97
 
82
- def ssh_key_on_github() -> bool:
83
- """Test if the current SSH key authenticates to GitHub."""
98
+ def add_github_host_keys() -> None:
99
+ """Trust GitHub host keys for both port 22 and port 443."""
100
+ _append_known_hosts("github.com")
101
+ _append_known_hosts("ssh.github.com", port=443)
102
+
103
+
104
+ def test_github_ssh() -> tuple[bool, str]:
105
+ """Test SSH auth to GitHub. Returns (success, combined output)."""
84
106
  result = subprocess.run(
85
- ["ssh", "-T", "-o", "BatchMode=yes", "-o", "StrictHostKeyChecking=no", "git@github.com"],
107
+ [
108
+ "ssh", "-T",
109
+ "-o", "BatchMode=yes",
110
+ "-o", "ConnectTimeout=15",
111
+ "-o", "StrictHostKeyChecking=accept-new",
112
+ "git@github.com",
113
+ ],
86
114
  capture_output=True, text=True,
87
115
  )
88
- return "successfully authenticated" in (result.stdout + result.stderr)
116
+ output = (result.stdout + result.stderr).strip()
117
+ ok = "successfully authenticated" in output.lower()
118
+ return ok, output
119
+
120
+
121
+ def ssh_auth_username(output: str) -> str | None:
122
+ match = re.search(r"Hi ([^!]+)!", output)
123
+ return match.group(1) if match else None
124
+
125
+
126
+ def is_ssh_connection_error(message: str) -> bool:
127
+ lower = message.lower()
128
+ return any(
129
+ phrase in lower
130
+ for phrase in ("connection closed", "connection reset", "timed out", "network is unreachable")
131
+ )
132
+
133
+
134
+ def is_ssh_auth_error(message: str) -> bool:
135
+ lower = message.lower()
136
+ return "permission denied" in lower or "publickey" in lower
137
+
138
+
139
+ def has_github_port443_config() -> bool:
140
+ if not SSH_CONFIG.exists():
141
+ return False
142
+ content = SSH_CONFIG.read_text()
143
+ return "ssh.github.com" in content and "443" in content
144
+
145
+
146
+ def configure_github_ssh_port_443() -> bool:
147
+ """Configure ~/.ssh/config to reach GitHub over port 443."""
148
+ if has_github_port443_config():
149
+ return True
150
+
151
+ SSH_CONFIG.parent.mkdir(mode=0o700, exist_ok=True)
152
+ with open(SSH_CONFIG, "a") as f:
153
+ f.write(GITHUB_PORT443_BLOCK)
154
+ SSH_CONFIG.chmod(0o600)
155
+ _append_known_hosts("ssh.github.com", port=443)
156
+ return True
157
+
158
+
159
+ def add_ssh_key_to_agent() -> bool:
160
+ """Load the default key into ssh-agent (required on some macOS setups)."""
161
+ if not DEFAULT_KEY.exists():
162
+ return False
163
+
164
+ commands = []
165
+ if platform.system() == "Darwin":
166
+ commands.append(["ssh-add", "--apple-use-keychain", str(DEFAULT_KEY)])
167
+ commands.append(["ssh-add", str(DEFAULT_KEY)])
168
+
169
+ for cmd in commands:
170
+ result = subprocess.run(cmd, capture_output=True, text=True)
171
+ if result.returncode == 0:
172
+ return True
173
+ return False
89
174
 
90
175
 
91
176
  def upload_ssh_key_to_github(title: str = "github-sync") -> bool:
@@ -99,6 +184,55 @@ def upload_ssh_key_to_github(title: str = "github-sync") -> bool:
99
184
  return result.returncode == 0
100
185
 
101
186
 
187
+ def ssh_key_on_github() -> bool:
188
+ """Back-compat wrapper."""
189
+ ok, _ = test_github_ssh()
190
+ return ok
191
+
192
+
193
+ def ensure_github_ssh() -> tuple[bool, str, list[str]]:
194
+ """
195
+ Verify GitHub SSH works; apply fixes as needed.
196
+ Returns (success, last_message, fixes_applied).
197
+ """
198
+ fixes: list[str] = []
199
+
200
+ ok, msg = test_github_ssh()
201
+ if ok:
202
+ return True, msg, fixes
203
+
204
+ if is_ssh_connection_error(msg):
205
+ configure_github_ssh_port_443()
206
+ fixes.append("configured port 443")
207
+ ok, msg = test_github_ssh()
208
+ if ok:
209
+ return True, msg, fixes
210
+
211
+ if is_ssh_auth_error(msg):
212
+ if upload_ssh_key_to_github():
213
+ fixes.append("uploaded SSH key")
214
+ ok, msg = test_github_ssh()
215
+ if ok:
216
+ return True, msg, fixes
217
+
218
+ if add_ssh_key_to_agent():
219
+ fixes.append("loaded key into ssh-agent")
220
+ ok, msg = test_github_ssh()
221
+ if ok:
222
+ return True, msg, fixes
223
+
224
+ # Last resort: port 443 + agent together
225
+ if is_ssh_connection_error(msg) and not has_github_port443_config():
226
+ configure_github_ssh_port_443()
227
+ add_ssh_key_to_agent()
228
+ fixes.append("port 443 + ssh-agent")
229
+ ok, msg = test_github_ssh()
230
+ if ok:
231
+ return True, msg, fixes
232
+
233
+ return False, msg, fixes
234
+
235
+
102
236
  # ---------------------------------------------------------------------------
103
237
  # Config writer
104
238
  # ---------------------------------------------------------------------------
@@ -142,6 +276,11 @@ exclude: []
142
276
  # Logging
143
277
  log_file: {log_file}
144
278
  log_level: INFO
279
+
280
+ # HTTP API (github-sync serve)
281
+ api_host: "127.0.0.1"
282
+ api_port: 8741
283
+ # api_key: "change-me" # uncomment to require: Authorization: Bearer <key>
145
284
  """
146
285
 
147
286
 
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: github-sync-agent
3
- Version: 0.1.3
3
+ Version: 0.2.0
4
4
  Summary: Sync all your local projects to GitHub automatically
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://github.com/yuvalavni/Agents
7
7
  Project-URL: Repository, https://github.com/yuvalavni/Agents
8
8
  Project-URL: Bug Tracker, https://github.com/yuvalavni/Agents/issues
9
- Keywords: github,git,sync,backup,automation,cli
9
+ Keywords: github,git,sync,backup,automation,cli,api
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: License :: OSI Approved :: MIT License
12
12
  Classifier: Operating System :: POSIX :: Linux
@@ -16,6 +16,9 @@ Requires-Python: >=3.10
16
16
  Description-Content-Type: text/markdown
17
17
  Requires-Dist: pyyaml>=6.0
18
18
  Requires-Dist: click>=8.0
19
+ Provides-Extra: api
20
+ Requires-Dist: fastapi>=0.100; extra == "api"
21
+ Requires-Dist: uvicorn>=0.23; extra == "api"
19
22
 
20
23
  # GitHub Sync Agent
21
24
 
@@ -49,8 +52,11 @@ The wizard will:
49
52
  - Authenticate with GitHub (`gh auth login`)
50
53
  - Ask which folder to scan for projects
51
54
  - Set up your SSH key automatically
55
+ - **Verify SSH works** (auto-fixes port 443 and ssh-agent if needed)
52
56
  - Write a config file to `~/.config/github-sync/config.yaml`
53
57
 
58
+ Setup will **not** complete until `ssh -T git@github.com` succeeds.
59
+
54
60
  ---
55
61
 
56
62
  ## Usage
@@ -71,6 +77,64 @@ github-sync status
71
77
 
72
78
  ---
73
79
 
80
+ ## HTTP API
81
+
82
+ Install API dependencies:
83
+
84
+ ```bash
85
+ pip install 'github-sync-agent[api]'
86
+ ```
87
+
88
+ Start the server:
89
+
90
+ ```bash
91
+ github-sync serve
92
+ # → http://127.0.0.1:8741
93
+ ```
94
+
95
+ ### Endpoints
96
+
97
+ | Method | Path | Description |
98
+ |--------|------|-------------|
99
+ | `GET` | `/health` | Health check (no auth) |
100
+ | `GET` | `/status` | Sync status for all projects |
101
+ | `POST` | `/sync` | Sync all projects |
102
+ | `POST` | `/sync/{project}` | Sync one project |
103
+
104
+ Optional body for POST: `{"dry_run": false}`
105
+
106
+ ### Examples
107
+
108
+ ```bash
109
+ curl http://127.0.0.1:8741/health
110
+ curl http://127.0.0.1:8741/status
111
+ curl -X POST http://127.0.0.1:8741/sync
112
+ curl -X POST http://127.0.0.1:8741/sync/Agents
113
+ ```
114
+
115
+ ### API authentication (optional)
116
+
117
+ Add to `config.yaml`:
118
+
119
+ ```yaml
120
+ api_key: "your-secret-key"
121
+ ```
122
+
123
+ Then pass the key on every request (except `/health`):
124
+
125
+ ```bash
126
+ curl -H "Authorization: Bearer your-secret-key" http://127.0.0.1:8741/status
127
+ ```
128
+
129
+ ### Run as a systemd service
130
+
131
+ ```bash
132
+ cp systemd/github-sync-api.service ~/.config/systemd/user/
133
+ systemctl --user enable --now github-sync-api.service
134
+ ```
135
+
136
+ ---
137
+
74
138
  ## Configuration (`~/.config/github-sync/config.yaml`)
75
139
 
76
140
  Generated by `github-sync init`. You can edit it at any time.
@@ -85,6 +149,9 @@ Generated by `github-sync init`. You can edit it at any time.
85
149
  | `exclude` | `[]` | Directory names to skip |
86
150
  | `log_file` | `~/.local/share/github-sync/github-sync.log` | Log file path |
87
151
  | `log_level` | `INFO` | `DEBUG` / `INFO` / `WARNING` / `ERROR` |
152
+ | `api_host` | `127.0.0.1` | API bind address |
153
+ | `api_port` | `8741` | API port |
154
+ | `api_key` | _(empty)_ | Optional Bearer token for API auth |
88
155
 
89
156
  ### Excluding a project
90
157
 
@@ -0,0 +1,11 @@
1
+ github_sync/__init__.py,sha256=Zn1KFblwuFHiDRdRAiRnDBRkbPttWh44jKa5zG2ov0E,22
2
+ github_sync/agent.py,sha256=2WnJCwupDwT3C9bNZLoj2CAvZW-eoqZChqFTk4wNRKw,6692
3
+ github_sync/api.py,sha256=4EK1ZXgfQyGD3WBPsRlMipwyZTS0DWZLSLuvRV8tU7Y,3320
4
+ github_sync/cli.py,sha256=fOC532dUM5aZUaKcR0ljYc8OcoUs299JWzT6uEdrAf8,11407
5
+ github_sync/config.py,sha256=mnBYyIbtAnMGtjrs3gl-mg35Argos0sk_zOBeUdJpfA,1559
6
+ github_sync/setup.py,sha256=yxast9nVJiTC2OCxaEnKk0wwTmH2kOO2pLG2CKJDOmg,8650
7
+ github_sync_agent-0.2.0.dist-info/METADATA,sha256=HFlq_rklk8ExcLmRxTpm_HcgcPBInOMCA932Dz7TP6o,4115
8
+ github_sync_agent-0.2.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
9
+ github_sync_agent-0.2.0.dist-info/entry_points.txt,sha256=-LmJUTvRzj5cvyS-dEWdipouBj3dgkW6hqrTz1GmyGw,53
10
+ github_sync_agent-0.2.0.dist-info/top_level.txt,sha256=fPYTA83USK1N4JwfouV8zcejgWfWmqRAKQVDCAT8a5I,12
11
+ github_sync_agent-0.2.0.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- github_sync/__init__.py,sha256=XEqb2aiIn8fzGE68Mph4ck1FtQqsR_am0wRWvrYPffQ,22
2
- github_sync/agent.py,sha256=2WnJCwupDwT3C9bNZLoj2CAvZW-eoqZChqFTk4wNRKw,6692
3
- github_sync/cli.py,sha256=xsbYJXhlSGSDgEOlTm43XcDSdn5M83u2uz6UuzfG5s0,9827
4
- github_sync/config.py,sha256=BQrSW7DyhcZB6gZnDKkc-W6u1vtD-l76Rn_gW8Egypk,1444
5
- github_sync/setup.py,sha256=2ZO0ZCCxQ_LyVSdQbWrObwjjBQCDLqdu_IHplUqW0qU,4720
6
- github_sync_agent-0.1.3.dist-info/METADATA,sha256=_FnEkvuZNeHDyHSojYdDEg6z-cxLbVWlwn6Upf3jIxo,2644
7
- github_sync_agent-0.1.3.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
8
- github_sync_agent-0.1.3.dist-info/entry_points.txt,sha256=-LmJUTvRzj5cvyS-dEWdipouBj3dgkW6hqrTz1GmyGw,53
9
- github_sync_agent-0.1.3.dist-info/top_level.txt,sha256=fPYTA83USK1N4JwfouV8zcejgWfWmqRAKQVDCAT8a5I,12
10
- github_sync_agent-0.1.3.dist-info/RECORD,,