devlinker 1.4.0__tar.gz → 1.4.2__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.
- {devlinker-1.4.0/devlinker.egg-info → devlinker-1.4.2}/PKG-INFO +189 -41
- {devlinker-1.4.0 → devlinker-1.4.2}/README.md +187 -40
- devlinker-1.4.2/devlinker/config.py +17 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker/detector_ai.py +6 -1
- devlinker-1.4.2/devlinker/main.py +725 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker/proxy.py +310 -42
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker/runner.py +77 -27
- {devlinker-1.4.0 → devlinker-1.4.2/devlinker.egg-info}/PKG-INFO +189 -41
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker.egg-info/requires.txt +1 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/pyproject.toml +2 -1
- devlinker-1.4.0/devlinker/config.py +0 -8
- devlinker-1.4.0/devlinker/main.py +0 -399
- {devlinker-1.4.0 → devlinker-1.4.2}/LICENSE +0 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/MANIFEST.in +0 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker/__init__.py +0 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker/detection_state.py +0 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker/detector.py +0 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker/devlinker_loader_instant.html +0 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker/devlinker_loader_snippet.html +0 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker/doctor.py +0 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker/fix.py +0 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker/fixer.py +0 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker/global_state.py +0 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker/inspect.py +0 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker/logger.py +0 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker/monitor.py +0 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker/share.py +0 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker/tunnel.py +0 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker.egg-info/SOURCES.txt +0 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker.egg-info/dependency_links.txt +0 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker.egg-info/entry_points.txt +0 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/devlinker.egg-info/top_level.txt +0 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/setup.cfg +0 -0
- {devlinker-1.4.0 → devlinker-1.4.2}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devlinker
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.2
|
|
4
4
|
Summary: A lightweight proxy that combines your frontend and backend into one link for easy development and sharing.
|
|
5
5
|
Author-email: Mani <mani1028@users.noreply.github.com>
|
|
6
6
|
Requires-Python: >=3.7
|
|
@@ -13,24 +13,101 @@ Requires-Dist: httpx
|
|
|
13
13
|
Requires-Dist: pyngrok
|
|
14
14
|
Requires-Dist: qrcode[pil]
|
|
15
15
|
Requires-Dist: requests
|
|
16
|
+
Requires-Dist: rich
|
|
16
17
|
Requires-Dist: uvicorn
|
|
17
18
|
Requires-Dist: websockets
|
|
18
19
|
Dynamic: license-file
|
|
19
20
|
|
|
20
21
|
# Dev Linker
|
|
21
22
|
|
|
22
|
-
Dev Linker
|
|
23
|
+
Dev Linker starts your local development stack and routes frontend and backend traffic through one proxy URL, with optional LAN and public sharing.
|
|
24
|
+
|
|
25
|
+
## ⚡ Quick Start (2 Minutes)
|
|
26
|
+
|
|
27
|
+
Install:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install devlinker
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Run your apps:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Backend (example)
|
|
37
|
+
uvicorn main:app --reload
|
|
38
|
+
|
|
39
|
+
# Frontend (example)
|
|
40
|
+
npm run dev
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Run DevLinker:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
devlinker
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Open:
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
http://localhost:8001
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Done ✅
|
|
56
|
+
|
|
57
|
+
## 🧠 How DevLinker Works
|
|
58
|
+
|
|
59
|
+
Request flow:
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
Browser -> DevLinker Proxy -> Frontend or Backend
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Routing rules:
|
|
66
|
+
|
|
67
|
+
- / routes to frontend (React/Vite)
|
|
68
|
+
- /api/* routes to backend (FastAPI/Flask/Node)
|
|
69
|
+
|
|
70
|
+
DevLinker acts as a smart proxy and optional tunnel layer for local, LAN, and public development links.
|
|
71
|
+
|
|
72
|
+
Architecture diagram:
|
|
73
|
+
|
|
74
|
+
```mermaid
|
|
75
|
+
flowchart LR
|
|
76
|
+
B[Browser / Mobile] --> P[DevLinker Proxy]
|
|
77
|
+
P --> F[Frontend Dev Server\nVite/React]
|
|
78
|
+
P --> A[Backend API\nFastAPI/Flask/Node]
|
|
79
|
+
P --> T[Optional Tunnel\nCloudflare/ngrok]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## 🎯 Use Cases
|
|
83
|
+
|
|
84
|
+
- Test APIs and UI flows on mobile devices over WLAN
|
|
85
|
+
- Share local work instantly with teammates using one public URL
|
|
86
|
+
- Debug frontend-backend integration from a single entrypoint
|
|
87
|
+
- Reduce CORS/preflight issues during development
|
|
88
|
+
|
|
89
|
+
## 🖼️ Demo & Screenshots
|
|
90
|
+
|
|
91
|
+
- Terminal startup output: add screenshot at docs/images/terminal-startup.png
|
|
92
|
+
- Browser app via proxy: add screenshot at docs/images/browser-proxy.png
|
|
93
|
+
- Public URL share demo: add screenshot at docs/images/public-url.png
|
|
23
94
|
|
|
24
95
|
|
|
25
96
|
## Features
|
|
26
97
|
|
|
27
98
|
- 🚀 **Unified Dev Proxy:** Combines frontend (Vite/React) and backend (FastAPI/Flask/Node/Docker) into a single local and public URL.
|
|
28
99
|
- 🔍 **Auto Detection:** Detects frontend/backend ports, runtime, Docker containers, and Vite servers automatically.
|
|
100
|
+
- 📡 **Debug Request Logger:** Live API traffic lines (method, path, status, latency) only in debug mode.
|
|
101
|
+
- 🧩 **Backend-Only Mode:** If no frontend is detected, DevLinker still runs and forwards all traffic to backend.
|
|
102
|
+
- 🔁 **Auto API URL Sync:** Updates `frontend/.env.local` with `VITE_API_URL=http://localhost:<proxy-port>` using a managed block.
|
|
103
|
+
- 🛡️ **Proxy CORS + Preflight:** Handles common CORS/preflight behavior at the proxy layer, including credential-safe Origin handling.
|
|
29
104
|
- 🧠 **Smart Detection & Doctor:** Real-time request analysis, backend intelligence, log analyzer, and `devlinker doctor` for instant diagnostics.
|
|
30
105
|
- 🛡️ **Auto-Fix Engine:** `devlinker fix` applies safe fixes (like VITE_API_URL) and suggests code changes.
|
|
31
106
|
- 🌍 **Public Sharing:** Share your local dev environment instantly with `--url` (startup) or `devlinker share` (runtime, no restart).
|
|
32
107
|
- 🔄 **Dynamic Tunnel Control:** `devlinker unshare` disables public tunnel at runtime.
|
|
33
108
|
- 📡 **WLAN Sharing:** Prints LAN URL for same-network device access.
|
|
109
|
+
- 🔒 **Secure Token Linking:** Optional token gate for LAN/public access with `DEVLINKER_LINK_TOKEN`.
|
|
110
|
+
- 📊 **Browser API Logs Dashboard:** Open `/__devlinker/dashboard` for lightweight live API visibility.
|
|
34
111
|
- 🧑💻 **Interactive CLI:** Modern, colorized, emoji-rich terminal UX for all commands.
|
|
35
112
|
- 🧩 **Zero Config:** Works out-of-the-box for most FastAPI, Flask, Vite, and Docker projects.
|
|
36
113
|
- 🧪 **Runtime Smoke Test:** Built-in test for end-to-end proxy validation.
|
|
@@ -62,8 +139,33 @@ If DevLinker helps you ship faster, consider supporting the project:
|
|
|
62
139
|
- `devlinker --no-lan` — Hide WLAN sharing URL
|
|
63
140
|
- `devlinker --interactive-backend` — Prompt to choose backend if multiple found
|
|
64
141
|
- `devlinker --proxy-port 18000` — Use custom proxy port
|
|
142
|
+
- `devlinker --debug` — Enable debug mode (turns on live API request logger)
|
|
65
143
|
- `devlinker --version` — Show version
|
|
66
144
|
|
|
145
|
+
Security token (optional):
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
set DEVLINKER_LINK_TOKEN=your-secret-token
|
|
149
|
+
devlinker --url
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
When enabled, LAN/public requests must include one of:
|
|
153
|
+
- query param `dl_token=...`
|
|
154
|
+
- header `X-DevLinker-Token: ...`
|
|
155
|
+
- header `Authorization: Bearer ...`
|
|
156
|
+
|
|
157
|
+
Built-in API logs dashboard:
|
|
158
|
+
|
|
159
|
+
```text
|
|
160
|
+
http://localhost:<proxy-port>/__devlinker/dashboard
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
JSON stream endpoint used by the dashboard:
|
|
164
|
+
|
|
165
|
+
```text
|
|
166
|
+
http://localhost:<proxy-port>/__devlinker/logs
|
|
167
|
+
```
|
|
168
|
+
|
|
67
169
|
## Project Structure
|
|
68
170
|
|
|
69
171
|
```text
|
|
@@ -88,6 +190,12 @@ For local development:
|
|
|
88
190
|
pip install .
|
|
89
191
|
```
|
|
90
192
|
|
|
193
|
+
For editable local development:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
pip install -e .
|
|
197
|
+
```
|
|
198
|
+
|
|
91
199
|
After publishing to PyPI:
|
|
92
200
|
|
|
93
201
|
```bash
|
|
@@ -100,34 +208,47 @@ pip install devlinker
|
|
|
100
208
|
devlinker
|
|
101
209
|
```
|
|
102
210
|
|
|
103
|
-
|
|
211
|
+
Direct module run (without installing entrypoint script):
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
python -m devlinker.main
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Typical startup output (TTY with Rich available):
|
|
104
218
|
|
|
105
219
|
```text
|
|
106
|
-
|
|
220
|
+
╭─────────────────────────────╮
|
|
221
|
+
│ ♾️ DevLinker v1.4.1 │
|
|
222
|
+
│ Smart Local Dev Environment │
|
|
223
|
+
╰─────────────────────────────╯
|
|
224
|
+
|
|
225
|
+
✔ Detecting project...
|
|
226
|
+
⏳ Booting local services...
|
|
227
|
+
|
|
228
|
+
╭───────── DevLinker Ready ─────────╮
|
|
229
|
+
│ Proxy http://localhost:8001 │
|
|
230
|
+
│ WLAN http://192.168.1.3:8001 │
|
|
231
|
+
│ Public disabled (use --url) │
|
|
232
|
+
╰───────────────────────────────────╯
|
|
233
|
+
|
|
234
|
+
✨ Ready in 5.5s
|
|
235
|
+
Powered by DevLinker 🚀
|
|
236
|
+
```
|
|
107
237
|
|
|
108
|
-
|
|
109
|
-
[INFO] Booting local services...
|
|
110
|
-
[INFO] Detecting frontend/backend ports...
|
|
111
|
-
[OK] Frontend -> 5173
|
|
112
|
-
[OK] Backend -> 5000
|
|
238
|
+
Enable debug mode with live API request logging:
|
|
113
239
|
|
|
114
|
-
|
|
240
|
+
```bash
|
|
241
|
+
devlinker --debug
|
|
242
|
+
```
|
|
115
243
|
|
|
116
|
-
|
|
117
|
-
[OK] Public URL:
|
|
118
|
-
https://xxxx.trycloudflare.com
|
|
119
|
-
Tip: Press Ctrl+Click to open link
|
|
244
|
+
Debug mode request logger sample:
|
|
120
245
|
|
|
121
|
-
|
|
246
|
+
```text
|
|
247
|
+
🛠 Debug mode enabled: live API request logger is ON
|
|
122
248
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
Access Links:
|
|
127
|
-
Local: http://localhost:8000
|
|
128
|
-
WLAN: http://192.168.1.5:8000
|
|
129
|
-
Public: https://xxxx.trycloudflare.com
|
|
130
|
-
Tip: Press Ctrl+Click to open link
|
|
249
|
+
📡 Requests (Live)
|
|
250
|
+
GET /api/users 200 45ms
|
|
251
|
+
POST /api/login 401 120ms
|
|
131
252
|
```
|
|
132
253
|
|
|
133
254
|
Version check:
|
|
@@ -160,15 +281,6 @@ devlinker --docker
|
|
|
160
281
|
By default, DevLinker starts **fast local proxy only** (no tunnel). To enable a public tunnel, use the `--url` flag:
|
|
161
282
|
|
|
162
283
|
|
|
163
|
-
```bash
|
|
164
|
-
devlinker --url
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
In your terminal output, you'll see:
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
URL, run:
|
|
171
|
-
|
|
172
284
|
```bash
|
|
173
285
|
devlinker --url
|
|
174
286
|
```
|
|
@@ -177,11 +289,11 @@ This will start the proxy and open a public tunnel (Cloudflare or ngrok). The ou
|
|
|
177
289
|
|
|
178
290
|
```text
|
|
179
291
|
🌍 Enabling public tunnel...
|
|
180
|
-
|
|
181
|
-
|
|
292
|
+
✔ Tunnel provider: Cloudflare
|
|
293
|
+
✔ Public URL:
|
|
182
294
|
https://xxxx.trycloudflare.com
|
|
183
|
-
Tip:
|
|
184
|
-
|
|
295
|
+
ℹ Tip: Ctrl+Click to open link
|
|
296
|
+
ℹ Share this link with collaborators.
|
|
185
297
|
```
|
|
186
298
|
|
|
187
299
|
To force tunnel off (even if --url is passed):
|
|
@@ -241,13 +353,11 @@ devlinker --frontend 5173 --backend 5000 --proxy-port 18000
|
|
|
241
353
|
Default behavior also tries fallback ports automatically when 8000 is busy:
|
|
242
354
|
|
|
243
355
|
```text
|
|
244
|
-
|
|
245
|
-
|
|
356
|
+
⚠ Port 8000 in use
|
|
357
|
+
ℹ Using proxy port: 8001
|
|
246
358
|
```
|
|
247
359
|
|
|
248
|
-
|
|
249
|
-
- 8002
|
|
250
|
-
- 18000
|
|
360
|
+
By default it scans the next 10 ports after the requested one, then tries `18000`.
|
|
251
361
|
|
|
252
362
|
Frontend detection behavior:
|
|
253
363
|
|
|
@@ -266,6 +376,41 @@ fetch("/api/endpoint")
|
|
|
266
376
|
|
|
267
377
|
Do not hardcode backend host URLs in frontend code.
|
|
268
378
|
|
|
379
|
+
DevLinker also writes/updates a managed block in `frontend/.env.local`:
|
|
380
|
+
|
|
381
|
+
```env
|
|
382
|
+
# devlinker-managed:start
|
|
383
|
+
VITE_API_URL=http://localhost:8001
|
|
384
|
+
# devlinker-managed:end
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
This keeps frontend API calls consistently routed through the proxy.
|
|
388
|
+
|
|
389
|
+
Use the proxy URL as your single entry point during development:
|
|
390
|
+
|
|
391
|
+
```text
|
|
392
|
+
http://localhost:<proxy-port>
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Avoid direct backend calls like `http://localhost:5000` from browser-facing code.
|
|
396
|
+
|
|
397
|
+
## Configuration File
|
|
398
|
+
|
|
399
|
+
DevLinker loads config from the first file found in this order:
|
|
400
|
+
|
|
401
|
+
1. `devlinker.yaml`
|
|
402
|
+
2. `devlinker.yml`
|
|
403
|
+
3. `devlinker.json`
|
|
404
|
+
|
|
405
|
+
Example:
|
|
406
|
+
|
|
407
|
+
```yaml
|
|
408
|
+
frontend: 5173
|
|
409
|
+
backend: 5000
|
|
410
|
+
proxy_port: 8001
|
|
411
|
+
tunnel: false
|
|
412
|
+
```
|
|
413
|
+
|
|
269
414
|
## Backend Auto-Detection
|
|
270
415
|
|
|
271
416
|
Backend port detection runs in this order:
|
|
@@ -321,12 +466,15 @@ For containerized Flask backends, ensure:
|
|
|
321
466
|
|
|
322
467
|
- runner.py expects frontend project in frontend and Python app in backend/app.py.
|
|
323
468
|
- If those paths do not exist, Dev Linker skips launch and only tries to detect already-running services.
|
|
469
|
+
- If frontend is missing but backend is available, DevLinker continues in backend-only mode.
|
|
324
470
|
- Tunnel selection order is: cloudflared (TryCloudflare), then ngrok.
|
|
325
471
|
- If cloudflared is unavailable and ngrok is not configured, Dev Linker prints one-time setup guidance.
|
|
326
472
|
- You may need to set ngrok auth once on your machine using ngrok config add-authtoken <token>.
|
|
327
473
|
- Dev Linker prints a public URL with `ngrok-skip-browser-warning=true` only when ngrok is used.
|
|
328
474
|
- Startup output includes selected tunnel provider (`cloudflare` or `ngrok`).
|
|
329
475
|
- Proxy layer now supports WebSocket upgrades, including Vite HMR over shared links.
|
|
476
|
+
- Proxy handles common CORS/preflight behavior and adds camera/mic permissions policy headers.
|
|
477
|
+
- Live API request logging is disabled by default and only enabled with `devlinker --debug`.
|
|
330
478
|
- Proxy listens on `0.0.0.0` and can print a WLAN URL for same-network sharing.
|
|
331
479
|
- If WLAN access fails on Windows, allow the proxy port in firewall and confirm devices are on the same network.
|
|
332
480
|
|
|
@@ -1,17 +1,93 @@
|
|
|
1
1
|
# Dev Linker
|
|
2
2
|
|
|
3
|
-
Dev Linker
|
|
3
|
+
Dev Linker starts your local development stack and routes frontend and backend traffic through one proxy URL, with optional LAN and public sharing.
|
|
4
|
+
|
|
5
|
+
## ⚡ Quick Start (2 Minutes)
|
|
6
|
+
|
|
7
|
+
Install:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install devlinker
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Run your apps:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Backend (example)
|
|
17
|
+
uvicorn main:app --reload
|
|
18
|
+
|
|
19
|
+
# Frontend (example)
|
|
20
|
+
npm run dev
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Run DevLinker:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
devlinker
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Open:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
http://localhost:8001
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Done ✅
|
|
36
|
+
|
|
37
|
+
## 🧠 How DevLinker Works
|
|
38
|
+
|
|
39
|
+
Request flow:
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
Browser -> DevLinker Proxy -> Frontend or Backend
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Routing rules:
|
|
46
|
+
|
|
47
|
+
- / routes to frontend (React/Vite)
|
|
48
|
+
- /api/* routes to backend (FastAPI/Flask/Node)
|
|
49
|
+
|
|
50
|
+
DevLinker acts as a smart proxy and optional tunnel layer for local, LAN, and public development links.
|
|
51
|
+
|
|
52
|
+
Architecture diagram:
|
|
53
|
+
|
|
54
|
+
```mermaid
|
|
55
|
+
flowchart LR
|
|
56
|
+
B[Browser / Mobile] --> P[DevLinker Proxy]
|
|
57
|
+
P --> F[Frontend Dev Server\nVite/React]
|
|
58
|
+
P --> A[Backend API\nFastAPI/Flask/Node]
|
|
59
|
+
P --> T[Optional Tunnel\nCloudflare/ngrok]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 🎯 Use Cases
|
|
63
|
+
|
|
64
|
+
- Test APIs and UI flows on mobile devices over WLAN
|
|
65
|
+
- Share local work instantly with teammates using one public URL
|
|
66
|
+
- Debug frontend-backend integration from a single entrypoint
|
|
67
|
+
- Reduce CORS/preflight issues during development
|
|
68
|
+
|
|
69
|
+
## 🖼️ Demo & Screenshots
|
|
70
|
+
|
|
71
|
+
- Terminal startup output: add screenshot at docs/images/terminal-startup.png
|
|
72
|
+
- Browser app via proxy: add screenshot at docs/images/browser-proxy.png
|
|
73
|
+
- Public URL share demo: add screenshot at docs/images/public-url.png
|
|
4
74
|
|
|
5
75
|
|
|
6
76
|
## Features
|
|
7
77
|
|
|
8
78
|
- 🚀 **Unified Dev Proxy:** Combines frontend (Vite/React) and backend (FastAPI/Flask/Node/Docker) into a single local and public URL.
|
|
9
79
|
- 🔍 **Auto Detection:** Detects frontend/backend ports, runtime, Docker containers, and Vite servers automatically.
|
|
80
|
+
- 📡 **Debug Request Logger:** Live API traffic lines (method, path, status, latency) only in debug mode.
|
|
81
|
+
- 🧩 **Backend-Only Mode:** If no frontend is detected, DevLinker still runs and forwards all traffic to backend.
|
|
82
|
+
- 🔁 **Auto API URL Sync:** Updates `frontend/.env.local` with `VITE_API_URL=http://localhost:<proxy-port>` using a managed block.
|
|
83
|
+
- 🛡️ **Proxy CORS + Preflight:** Handles common CORS/preflight behavior at the proxy layer, including credential-safe Origin handling.
|
|
10
84
|
- 🧠 **Smart Detection & Doctor:** Real-time request analysis, backend intelligence, log analyzer, and `devlinker doctor` for instant diagnostics.
|
|
11
85
|
- 🛡️ **Auto-Fix Engine:** `devlinker fix` applies safe fixes (like VITE_API_URL) and suggests code changes.
|
|
12
86
|
- 🌍 **Public Sharing:** Share your local dev environment instantly with `--url` (startup) or `devlinker share` (runtime, no restart).
|
|
13
87
|
- 🔄 **Dynamic Tunnel Control:** `devlinker unshare` disables public tunnel at runtime.
|
|
14
88
|
- 📡 **WLAN Sharing:** Prints LAN URL for same-network device access.
|
|
89
|
+
- 🔒 **Secure Token Linking:** Optional token gate for LAN/public access with `DEVLINKER_LINK_TOKEN`.
|
|
90
|
+
- 📊 **Browser API Logs Dashboard:** Open `/__devlinker/dashboard` for lightweight live API visibility.
|
|
15
91
|
- 🧑💻 **Interactive CLI:** Modern, colorized, emoji-rich terminal UX for all commands.
|
|
16
92
|
- 🧩 **Zero Config:** Works out-of-the-box for most FastAPI, Flask, Vite, and Docker projects.
|
|
17
93
|
- 🧪 **Runtime Smoke Test:** Built-in test for end-to-end proxy validation.
|
|
@@ -43,8 +119,33 @@ If DevLinker helps you ship faster, consider supporting the project:
|
|
|
43
119
|
- `devlinker --no-lan` — Hide WLAN sharing URL
|
|
44
120
|
- `devlinker --interactive-backend` — Prompt to choose backend if multiple found
|
|
45
121
|
- `devlinker --proxy-port 18000` — Use custom proxy port
|
|
122
|
+
- `devlinker --debug` — Enable debug mode (turns on live API request logger)
|
|
46
123
|
- `devlinker --version` — Show version
|
|
47
124
|
|
|
125
|
+
Security token (optional):
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
set DEVLINKER_LINK_TOKEN=your-secret-token
|
|
129
|
+
devlinker --url
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
When enabled, LAN/public requests must include one of:
|
|
133
|
+
- query param `dl_token=...`
|
|
134
|
+
- header `X-DevLinker-Token: ...`
|
|
135
|
+
- header `Authorization: Bearer ...`
|
|
136
|
+
|
|
137
|
+
Built-in API logs dashboard:
|
|
138
|
+
|
|
139
|
+
```text
|
|
140
|
+
http://localhost:<proxy-port>/__devlinker/dashboard
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
JSON stream endpoint used by the dashboard:
|
|
144
|
+
|
|
145
|
+
```text
|
|
146
|
+
http://localhost:<proxy-port>/__devlinker/logs
|
|
147
|
+
```
|
|
148
|
+
|
|
48
149
|
## Project Structure
|
|
49
150
|
|
|
50
151
|
```text
|
|
@@ -69,6 +170,12 @@ For local development:
|
|
|
69
170
|
pip install .
|
|
70
171
|
```
|
|
71
172
|
|
|
173
|
+
For editable local development:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
pip install -e .
|
|
177
|
+
```
|
|
178
|
+
|
|
72
179
|
After publishing to PyPI:
|
|
73
180
|
|
|
74
181
|
```bash
|
|
@@ -81,34 +188,47 @@ pip install devlinker
|
|
|
81
188
|
devlinker
|
|
82
189
|
```
|
|
83
190
|
|
|
84
|
-
|
|
191
|
+
Direct module run (without installing entrypoint script):
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
python -m devlinker.main
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Typical startup output (TTY with Rich available):
|
|
85
198
|
|
|
86
199
|
```text
|
|
87
|
-
|
|
200
|
+
╭─────────────────────────────╮
|
|
201
|
+
│ ♾️ DevLinker v1.4.1 │
|
|
202
|
+
│ Smart Local Dev Environment │
|
|
203
|
+
╰─────────────────────────────╯
|
|
204
|
+
|
|
205
|
+
✔ Detecting project...
|
|
206
|
+
⏳ Booting local services...
|
|
207
|
+
|
|
208
|
+
╭───────── DevLinker Ready ─────────╮
|
|
209
|
+
│ Proxy http://localhost:8001 │
|
|
210
|
+
│ WLAN http://192.168.1.3:8001 │
|
|
211
|
+
│ Public disabled (use --url) │
|
|
212
|
+
╰───────────────────────────────────╯
|
|
213
|
+
|
|
214
|
+
✨ Ready in 5.5s
|
|
215
|
+
Powered by DevLinker 🚀
|
|
216
|
+
```
|
|
88
217
|
|
|
89
|
-
|
|
90
|
-
[INFO] Booting local services...
|
|
91
|
-
[INFO] Detecting frontend/backend ports...
|
|
92
|
-
[OK] Frontend -> 5173
|
|
93
|
-
[OK] Backend -> 5000
|
|
218
|
+
Enable debug mode with live API request logging:
|
|
94
219
|
|
|
95
|
-
|
|
220
|
+
```bash
|
|
221
|
+
devlinker --debug
|
|
222
|
+
```
|
|
96
223
|
|
|
97
|
-
|
|
98
|
-
[OK] Public URL:
|
|
99
|
-
https://xxxx.trycloudflare.com
|
|
100
|
-
Tip: Press Ctrl+Click to open link
|
|
224
|
+
Debug mode request logger sample:
|
|
101
225
|
|
|
102
|
-
|
|
226
|
+
```text
|
|
227
|
+
🛠 Debug mode enabled: live API request logger is ON
|
|
103
228
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
Access Links:
|
|
108
|
-
Local: http://localhost:8000
|
|
109
|
-
WLAN: http://192.168.1.5:8000
|
|
110
|
-
Public: https://xxxx.trycloudflare.com
|
|
111
|
-
Tip: Press Ctrl+Click to open link
|
|
229
|
+
📡 Requests (Live)
|
|
230
|
+
GET /api/users 200 45ms
|
|
231
|
+
POST /api/login 401 120ms
|
|
112
232
|
```
|
|
113
233
|
|
|
114
234
|
Version check:
|
|
@@ -141,15 +261,6 @@ devlinker --docker
|
|
|
141
261
|
By default, DevLinker starts **fast local proxy only** (no tunnel). To enable a public tunnel, use the `--url` flag:
|
|
142
262
|
|
|
143
263
|
|
|
144
|
-
```bash
|
|
145
|
-
devlinker --url
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
In your terminal output, you'll see:
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
URL, run:
|
|
152
|
-
|
|
153
264
|
```bash
|
|
154
265
|
devlinker --url
|
|
155
266
|
```
|
|
@@ -158,11 +269,11 @@ This will start the proxy and open a public tunnel (Cloudflare or ngrok). The ou
|
|
|
158
269
|
|
|
159
270
|
```text
|
|
160
271
|
🌍 Enabling public tunnel...
|
|
161
|
-
|
|
162
|
-
|
|
272
|
+
✔ Tunnel provider: Cloudflare
|
|
273
|
+
✔ Public URL:
|
|
163
274
|
https://xxxx.trycloudflare.com
|
|
164
|
-
Tip:
|
|
165
|
-
|
|
275
|
+
ℹ Tip: Ctrl+Click to open link
|
|
276
|
+
ℹ Share this link with collaborators.
|
|
166
277
|
```
|
|
167
278
|
|
|
168
279
|
To force tunnel off (even if --url is passed):
|
|
@@ -222,13 +333,11 @@ devlinker --frontend 5173 --backend 5000 --proxy-port 18000
|
|
|
222
333
|
Default behavior also tries fallback ports automatically when 8000 is busy:
|
|
223
334
|
|
|
224
335
|
```text
|
|
225
|
-
|
|
226
|
-
|
|
336
|
+
⚠ Port 8000 in use
|
|
337
|
+
ℹ Using proxy port: 8001
|
|
227
338
|
```
|
|
228
339
|
|
|
229
|
-
|
|
230
|
-
- 8002
|
|
231
|
-
- 18000
|
|
340
|
+
By default it scans the next 10 ports after the requested one, then tries `18000`.
|
|
232
341
|
|
|
233
342
|
Frontend detection behavior:
|
|
234
343
|
|
|
@@ -247,6 +356,41 @@ fetch("/api/endpoint")
|
|
|
247
356
|
|
|
248
357
|
Do not hardcode backend host URLs in frontend code.
|
|
249
358
|
|
|
359
|
+
DevLinker also writes/updates a managed block in `frontend/.env.local`:
|
|
360
|
+
|
|
361
|
+
```env
|
|
362
|
+
# devlinker-managed:start
|
|
363
|
+
VITE_API_URL=http://localhost:8001
|
|
364
|
+
# devlinker-managed:end
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
This keeps frontend API calls consistently routed through the proxy.
|
|
368
|
+
|
|
369
|
+
Use the proxy URL as your single entry point during development:
|
|
370
|
+
|
|
371
|
+
```text
|
|
372
|
+
http://localhost:<proxy-port>
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Avoid direct backend calls like `http://localhost:5000` from browser-facing code.
|
|
376
|
+
|
|
377
|
+
## Configuration File
|
|
378
|
+
|
|
379
|
+
DevLinker loads config from the first file found in this order:
|
|
380
|
+
|
|
381
|
+
1. `devlinker.yaml`
|
|
382
|
+
2. `devlinker.yml`
|
|
383
|
+
3. `devlinker.json`
|
|
384
|
+
|
|
385
|
+
Example:
|
|
386
|
+
|
|
387
|
+
```yaml
|
|
388
|
+
frontend: 5173
|
|
389
|
+
backend: 5000
|
|
390
|
+
proxy_port: 8001
|
|
391
|
+
tunnel: false
|
|
392
|
+
```
|
|
393
|
+
|
|
250
394
|
## Backend Auto-Detection
|
|
251
395
|
|
|
252
396
|
Backend port detection runs in this order:
|
|
@@ -302,12 +446,15 @@ For containerized Flask backends, ensure:
|
|
|
302
446
|
|
|
303
447
|
- runner.py expects frontend project in frontend and Python app in backend/app.py.
|
|
304
448
|
- If those paths do not exist, Dev Linker skips launch and only tries to detect already-running services.
|
|
449
|
+
- If frontend is missing but backend is available, DevLinker continues in backend-only mode.
|
|
305
450
|
- Tunnel selection order is: cloudflared (TryCloudflare), then ngrok.
|
|
306
451
|
- If cloudflared is unavailable and ngrok is not configured, Dev Linker prints one-time setup guidance.
|
|
307
452
|
- You may need to set ngrok auth once on your machine using ngrok config add-authtoken <token>.
|
|
308
453
|
- Dev Linker prints a public URL with `ngrok-skip-browser-warning=true` only when ngrok is used.
|
|
309
454
|
- Startup output includes selected tunnel provider (`cloudflare` or `ngrok`).
|
|
310
455
|
- Proxy layer now supports WebSocket upgrades, including Vite HMR over shared links.
|
|
456
|
+
- Proxy handles common CORS/preflight behavior and adds camera/mic permissions policy headers.
|
|
457
|
+
- Live API request logging is disabled by default and only enabled with `devlinker --debug`.
|
|
311
458
|
- Proxy listens on `0.0.0.0` and can print a WLAN URL for same-network sharing.
|
|
312
459
|
- If WLAN access fails on Windows, allow the proxy port in firewall and confirm devices are on the same network.
|
|
313
460
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
import yaml
|
|
5
|
+
|
|
6
|
+
def load_config(config_path: str = "devlinker.yaml") -> dict:
|
|
7
|
+
candidates = [config_path, "devlinker.yml", "devlinker.json"]
|
|
8
|
+
selected = next((path for path in candidates if os.path.exists(path)), None)
|
|
9
|
+
if not selected:
|
|
10
|
+
return {}
|
|
11
|
+
|
|
12
|
+
with open(selected, "r", encoding="utf-8") as handle:
|
|
13
|
+
if selected.endswith(".json"):
|
|
14
|
+
data = json.load(handle)
|
|
15
|
+
else:
|
|
16
|
+
data = yaml.safe_load(handle)
|
|
17
|
+
return data or {}
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
class DevLinkerAI:
|
|
2
2
|
def analyze_failure(self, error_text):
|
|
3
|
+
lowered = error_text.lower()
|
|
3
4
|
if "CORS" in error_text:
|
|
4
5
|
return [
|
|
5
6
|
"Frontend is calling backend directly",
|
|
6
7
|
"Use /api/* instead of localhost:PORT"
|
|
7
8
|
]
|
|
8
9
|
if "404" in error_text:
|
|
10
|
+
if " get / " in lowered or lowered.strip().startswith("get /"):
|
|
11
|
+
return []
|
|
12
|
+
if "/api" not in lowered:
|
|
13
|
+
return ["Route not found"]
|
|
9
14
|
return [
|
|
10
15
|
"Route not found",
|
|
11
16
|
"Check if '/api' prefix is required"
|
|
12
17
|
]
|
|
13
|
-
if "connection refused" in
|
|
18
|
+
if "connection refused" in lowered:
|
|
14
19
|
return [
|
|
15
20
|
"Backend not reachable",
|
|
16
21
|
"Ensure backend is running"
|