Fix YouTube playback: let Home Relay use fresh Firefox cookies; drop leaked secrets
Browse filesRoot cause of "no format" / import-cookies error despite Firefox auto-load:
resolvePlayer did `cookieHeader || firefoxCookiesStr`, so any cookie forwarded by
the Worker (always non-empty via CONSENT/SOCS) shadowed the freshly auto-loaded
Firefox session entirely, giving stale auth and LOGIN_REQUIRED.
- home-relay/server.js: merge forwarded cookies with the Firefox session, Firefox
last so slimCookieHeader keeps its fresher value per cookie name.
- worker.js: empty BUILTIN_YOUTUBE_COOKIE; it held a leaked, expired personal
Google session that both leaked credentials and shadowed the relay cookies.
- start-relay.ps1 / home-relay/run-relay.bat: read RELAY_SECRET from the
environment (generate and persist on first run) instead of hardcoding it.
Also adds relay/autostart tooling, docs, and the localtunnel devDependency.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- DIAGNOSE-RELAY.md +90 -0
- RESTRICTED-MODE.md +100 -0
- home-relay/README.md +160 -0
- home-relay/WORKER_HOOK.md +170 -0
- home-relay/launch-relay.vbs +15 -0
- home-relay/launch-tunnel.vbs +9 -0
- home-relay/package-lock.json +729 -0
- home-relay/package.json +17 -0
- home-relay/run-relay.bat +12 -0
- home-relay/run-tunnel.bat +2 -0
- home-relay/server.js +871 -0
- install-autostart.ps1 +119 -0
- package-lock.json +1780 -0
- package.json +1 -0
- start-relay.ps1 +126 -0
- start_bypass_relay.vbs +2 -0
- stop-relay.ps1 +11 -0
- worker.js +222 -68
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# אבחון Relay / Tunnel (502 + health תקוע)
|
| 2 |
+
|
| 3 |
+
## מה מצאנו (סיבת השורש) — מאומת בלוגים
|
| 4 |
+
|
| 5 |
+
| תסמין | סיבה |
|
| 6 |
+
|--------|------|
|
| 7 |
+
| localtunnel 502 | השרת לא מגיע / מנהרה מתה / **URL לא תואם** |
|
| 8 |
+
| Worker מצביע על `etrog-bypass-mirik-999.loca.lt` | בלוג בפועל נוצר `https://weak-crab-83.loca.lt` — subdomain קבוע **לא נשמר** |
|
| 9 |
+
| `127.0.0.1:8788/health` timeout | תהליך Node **לא רץ** — connection refused (לא IPv6) |
|
| 10 |
+
| cloudflared 502 | `Unable to reach origin … 127.0.0.1:8788 … refused` = **relay down**, לא באג tunnel |
|
| 11 |
+
| LOGIN_REQUIRED מ-CF | צפוי — בלי relay חי אין resolve |
|
| 12 |
+
|
| 13 |
+
**אימות:** אחרי restart של Node, `cloudflared --protocol http2` + `/health` הציבורי החזיר `ok: true`.
|
| 14 |
+
|
| 15 |
+
**localtunnel** בשירות החינמי:
|
| 16 |
+
- מתעלם לעיתים מ-subdomain ומחזיר שם אקראי
|
| 17 |
+
- דורש לעיתים דף "tunnel password" בדפדפן (Worker לא עובר אותו)
|
| 18 |
+
- 502 נפוץ מאוד
|
| 19 |
+
|
| 20 |
+
**הפתרון:** `cloudflared` quick tunnel + עדכון `YT_RELAY_URL` ל-URL **האמיתי** שנוצר.
|
| 21 |
+
|
| 22 |
+
---
|
| 23 |
+
|
| 24 |
+
## הפעלה נכונה (Windows)
|
| 25 |
+
|
| 26 |
+
```powershell
|
| 27 |
+
cd "C:\Users\user\Documents\מעקף פרוקסי 2.0"
|
| 28 |
+
|
| 29 |
+
# טוקן CF — רק מקומית, לא בצ'אט
|
| 30 |
+
$env:CLOUDFLARE_API_TOKEN = "YOUR_NEW_TOKEN"
|
| 31 |
+
|
| 32 |
+
# אופציונלי — אחרת משתמשים בברירת המחדל בסקריפט
|
| 33 |
+
# $env:RELAY_SECRET = "same-secret-as-worker"
|
| 34 |
+
|
| 35 |
+
.\stop-relay.ps1
|
| 36 |
+
.\start-relay.ps1
|
| 37 |
+
```
|
| 38 |
+
|
| 39 |
+
בדיקות:
|
| 40 |
+
|
| 41 |
+
```powershell
|
| 42 |
+
Invoke-RestMethod http://127.0.0.1:8788/health
|
| 43 |
+
Get-Content $env:TEMP\yt-relay-url.txt
|
| 44 |
+
$url = (Get-Content $env:TEMP\yt-relay-url.txt).Trim()
|
| 45 |
+
Invoke-RestMethod "$url/health"
|
| 46 |
+
```
|
| 47 |
+
|
| 48 |
+
Player:
|
| 49 |
+
|
| 50 |
+
```text
|
| 51 |
+
https://etrog-bypass-proxy.miriklain616.workers.dev/ytp
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
---
|
| 55 |
+
|
| 56 |
+
## IPv4 / IPv6
|
| 57 |
+
|
| 58 |
+
- השרת מאזין על `127.0.0.1` (IPv4 loopback) — זה מה ש-cloudflared מצפה.
|
| 59 |
+
- אין צורך ב-`allow_invalid_cert` ל-localtunnel אם לא משתמשים בו.
|
| 60 |
+
- אם health נכשל: `netstat -ano | findstr 8788` — חייב LISTENING.
|
| 61 |
+
|
| 62 |
+
---
|
| 63 |
+
|
| 64 |
+
## cloudflared מול localtunnel
|
| 65 |
+
|
| 66 |
+
| | localtunnel | cloudflared |
|
| 67 |
+
|--|-------------|-------------|
|
| 68 |
+
| יציבות | גרועה | טובה ל-quick tunnel |
|
| 69 |
+
| URL קבוע | כמעט לא | named tunnel + דומיין |
|
| 70 |
+
| סיסמת דף | לפעמים | לא |
|
| 71 |
+
| מומלץ לפרויקט | לא | **כן** |
|
| 72 |
+
|
| 73 |
+
`http2: stream closed` ב-cloudflared לרוב = תהליך local מת / sleep / restart באמצע סטרים. שמור PC ער; לנגן מחדש אחרי reconnect.
|
| 74 |
+
|
| 75 |
+
---
|
| 76 |
+
|
| 77 |
+
## Etrog `RWC_BLOCK` / `lighttpd` ב-SW
|
| 78 |
+
|
| 79 |
+
ה-SW בודק תשובות ל-`/assets/chunk-*` וכו'. אם **אין** `X-Etrog-Mode` / seal וה-filter שם `server: lighttpd` — מחזירים 502.
|
| 80 |
+
|
| 81 |
+
זה אומר שהבקשה **לא** עברה דרך ה-Worker (או ש-MITM החליף את הגוף).
|
| 82 |
+
כשה-relay + worker מסמנים `X-Etrog-Mode: stream-unsealed` או sealed — הבדיקה אמורה לדלג.
|
| 83 |
+
|
| 84 |
+
---
|
| 85 |
+
|
| 86 |
+
## אבטחה
|
| 87 |
+
|
| 88 |
+
- **בטל** כל Cloudflare API token שנשלח בצ'אט.
|
| 89 |
+
- אל תשים טוקנים ב-`start-relay.ps1` (תוקן).
|
| 90 |
+
- סובב גם `RELAY_SECRET` אם דלף.
|
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Restricted Mode / Bot check / MRM — מדריך מעשי
|
| 2 |
+
|
| 3 |
+
## שלוש שכבות חסימה שונות (לא לבלבל)
|
| 4 |
+
|
| 5 |
+
| שכבה | הודעה טיפוסית | מי חוסם | פתרון |
|
| 6 |
+
|------|----------------|---------|--------|
|
| 7 |
+
| **A. Cloudflare IP** | LOGIN_REQUIRED מ-Worker | YouTube מול DC | Home Relay (כבר יש) |
|
| 8 |
+
| **B. Bot / age / login** | "צריך להיכנס כדי לאמת שלא מדובר בבוט" | YouTube (חשבון/סשן) | עוגיות + SAPISIDHASH |
|
| 9 |
+
| **C. Network Restricted Mode** | MRM / VIDEO_BLOCK_BY_MRM / מצב מוגן | אתרוג↔Google על ה-IP | חשבון Google בלי Restricted, או egress "נקי" |
|
| 10 |
+
|
| 11 |
+
הרילאי הביתי פותר **A**.
|
| 12 |
+
**B** דורש סשן YouTube אמיתי ב-Firefox + הזרקה נכונה (עודכן ב-`server.js`).
|
| 13 |
+
**C** לא נפתר רק ע"י קליינט InnerTube אחר — Google רואה את ה-IP/הרשת המסוננת.
|
| 14 |
+
|
| 15 |
+
---
|
| 16 |
+
|
| 17 |
+
## מה תוקן ב-relay (B)
|
| 18 |
+
|
| 19 |
+
1. עוגיות מ-Firefox גם מ-`.google.com` (SAPISID/SID)
|
| 20 |
+
2. `SAPISIDHASH` על WEB/MWEB
|
| 21 |
+
3. עוגיות גם ל-ANDROID (לא רק WEB)
|
| 22 |
+
4. רענון עוגיות כל 15 דקות
|
| 23 |
+
5. `RELAY_INSECURE_TLS=1` אם Node נכשל בגלל תעודת MITM של אתרוג
|
| 24 |
+
|
| 25 |
+
### בדיקה ידנית אחרי restart
|
| 26 |
+
|
| 27 |
+
```powershell
|
| 28 |
+
# ודא שיש session
|
| 29 |
+
Invoke-RestMethod http://127.0.0.1:8788/health
|
| 30 |
+
# בלוג: SAPISID=yes, session=yes
|
| 31 |
+
|
| 32 |
+
# נגן סרטון "בעייתי"
|
| 33 |
+
$secret = "YOUR_RELAY_SECRET"
|
| 34 |
+
Invoke-RestMethod -Method POST http://127.0.0.1:8788/v1/yt/player `
|
| 35 |
+
-Headers @{ Authorization = "Bearer $secret" } `
|
| 36 |
+
-ContentType "application/json" `
|
| 37 |
+
-Body '{"videoId":"VIDEO_ID"}' | ConvertTo-Json -Depth 5
|
| 38 |
+
```
|
| 39 |
+
|
| 40 |
+
חפש ב-`attempts`: `WEB+ck` / `status: OK`.
|
| 41 |
+
|
| 42 |
+
### אם עדיין LOGIN_REQUIRED
|
| 43 |
+
|
| 44 |
+
1. בפיירפוקס: היכנס ל-YouTube, נגן סרטון אחד **ישירות** (אם האתר פתוח ברשת)
|
| 45 |
+
2. בחשבון Google:
|
| 46 |
+
https://myaccount.google.com/u/0/restricted-mode
|
| 47 |
+
וגם הגדרות YouTube → Restricted Mode = **Off**
|
| 48 |
+
3. הפעל מחדש relay (רענון cookies)
|
| 49 |
+
4. אם `fetch failed` / certificate:
|
| 50 |
+
```powershell
|
| 51 |
+
[Environment]::SetEnvironmentVariable("RELAY_INSECURE_TLS","1","User")
|
| 52 |
+
```
|
| 53 |
+
ואז restart
|
| 54 |
+
|
| 55 |
+
---
|
| 56 |
+
|
| 57 |
+
## לגבי "איזה client עוקף Restricted Mode?"
|
| 58 |
+
|
| 59 |
+
| Client | מתי עוזר |
|
| 60 |
+
|--------|----------|
|
| 61 |
+
| **WEB + cookies + SAPISIDHASH** | הכי טוב ל-bot check / age (שכבה B) |
|
| 62 |
+
| **TVHTML5_SIMPLY_EMBEDDED** | חלק מהסרטונים בלי login |
|
| 63 |
+
| **ANDROID בלי cookies** | רוב הסרטונים "רגילים"; נכשל על restricted |
|
| 64 |
+
| **ANDROID_EMBEDDED / WEB_CREATOR** | לא קסם מול MRM רשתי |
|
| 65 |
+
|
| 66 |
+
**אין client קסם** שעוקף Restricted Mode ברמת רשת (שכבה C) כש-Google/אתרוג כופים אותו על ה-IP.
|
| 67 |
+
|
| 68 |
+
### אופציות אמיתיות ל-C (MRM רשתי)
|
| 69 |
+
|
| 70 |
+
1. **Hotspot סלולרי** לבדיקה — אם שם עובד, זו שכבה C
|
| 71 |
+
2. **Egress נקי** (VPN resid שאינו מאחורי אתרוג / מחשב אחר / VPS) רק ל-InnerTube+googlevideo
|
| 72 |
+
3. **חשבון Google** עם Restricted Mode כבוי + cookies (עוזר לחלק מהמקרים שמסומנים כ"מוגן" ברמת חשבון)
|
| 73 |
+
4. לא לצפות ש-CF Worker יפתור MRM — ה-IP של CF גרוע יותר
|
| 74 |
+
|
| 75 |
+
---
|
| 76 |
+
|
| 77 |
+
## Windows autostart (בעיה #2)
|
| 78 |
+
|
| 79 |
+
מומלץ: **Task Scheduler** (לא רק Startup VBS).
|
| 80 |
+
|
| 81 |
+
```powershell
|
| 82 |
+
cd "C:\Users\user\Documents\מעקף פרוקסי 2.0"
|
| 83 |
+
|
| 84 |
+
# טוקן כבר אמור להיות ב-User env:
|
| 85 |
+
# [Environment]::SetEnvironmentVariable("CLOUDFLARE_API_TOKEN","...","User")
|
| 86 |
+
|
| 87 |
+
.\install-autostart.ps1
|
| 88 |
+
schtasks /Run /TN EtrogYtHomeRelay
|
| 89 |
+
```
|
| 90 |
+
|
| 91 |
+
למה לא VBS בלבד:
|
| 92 |
+
- נתיב עברית ב-Startup לפעמים נשבר בקידוד
|
| 93 |
+
- אין restart on failure
|
| 94 |
+
- Scheduled Task עם Delay 45s + RestartOnFailure יציב יותר
|
| 95 |
+
|
| 96 |
+
הסר כפילות:
|
| 97 |
+
|
| 98 |
+
```powershell
|
| 99 |
+
Remove-Item "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\start_bypass_relay.vbs" -ErrorAction SilentlyContinue
|
| 100 |
+
```
|
|
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# YouTube Home Relay (residential egress)
|
| 2 |
+
|
| 3 |
+
Small Node.js process that runs on your **home PC**. Your Cloudflare Worker calls it for:
|
| 4 |
+
|
| 5 |
+
1. **InnerTube** `/youtubei/v1/player` (format resolve)
|
| 6 |
+
2. **googlevideo** byte streams
|
| 7 |
+
|
| 8 |
+
Both use the **same residential IP**, so YouTube stops returning `LOGIN_REQUIRED` / IP-bound 403s that you get from Cloudflare hosting ranges.
|
| 9 |
+
|
| 10 |
+
```
|
| 11 |
+
Browser ──► CF Worker ──► Home relay (this) ──► YouTube / googlevideo
|
| 12 |
+
│
|
| 13 |
+
└── other sites still direct from CF (Brave, etc.)
|
| 14 |
+
```
|
| 15 |
+
|
| 16 |
+
This is the simplest reliable solo-project option. Paid “residential proxy APIs” are optional; your home IP is free and real.
|
| 17 |
+
|
| 18 |
+
---
|
| 19 |
+
|
| 20 |
+
## 1. Run the relay on your PC
|
| 21 |
+
|
| 22 |
+
Requirements: **Node 18+**
|
| 23 |
+
|
| 24 |
+
```powershell
|
| 25 |
+
cd home-relay
|
| 26 |
+
# Generate a secret (keep private — same value goes into the Worker)
|
| 27 |
+
$env:RELAY_SECRET = -join ((48..57 + 65..90 + 97..122 | Get-Random -Count 40 | ForEach-Object { [char]$_ }))
|
| 28 |
+
$env:PORT = "8788"
|
| 29 |
+
npm start
|
| 30 |
+
```
|
| 31 |
+
|
| 32 |
+
Health check (no auth):
|
| 33 |
+
|
| 34 |
+
```text
|
| 35 |
+
http://127.0.0.1:8788/health
|
| 36 |
+
```
|
| 37 |
+
|
| 38 |
+
---
|
| 39 |
+
|
| 40 |
+
## 2. Expose it with Cloudflare Tunnel (free, no port-forward)
|
| 41 |
+
|
| 42 |
+
Install [cloudflared](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/), then:
|
| 43 |
+
|
| 44 |
+
### Quick test (random `*.trycloudflare.com` URL — changes each run)
|
| 45 |
+
|
| 46 |
+
```powershell
|
| 47 |
+
cloudflared tunnel --url http://127.0.0.1:8788
|
| 48 |
+
```
|
| 49 |
+
|
| 50 |
+
Copy the `https://….trycloudflare.com` URL → that is `YT_RELAY_URL`.
|
| 51 |
+
|
| 52 |
+
### Stable setup (recommended)
|
| 53 |
+
|
| 54 |
+
```powershell
|
| 55 |
+
cloudflared tunnel login
|
| 56 |
+
cloudflared tunnel create etrog-yt-relay
|
| 57 |
+
# route a subdomain you control, e.g. yt-relay.yourdomain.com
|
| 58 |
+
cloudflared tunnel route dns etrog-yt-relay yt-relay.yourdomain.com
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
Config file example (`%USERPROFILE%\.cloudflared\config.yml`):
|
| 62 |
+
|
| 63 |
+
```yaml
|
| 64 |
+
tunnel: <TUNNEL_ID>
|
| 65 |
+
credentials-file: C:\Users\YOU\.cloudflared\<TUNNEL_ID>.json
|
| 66 |
+
|
| 67 |
+
ingress:
|
| 68 |
+
- hostname: yt-relay.yourdomain.com
|
| 69 |
+
service: http://127.0.0.1:8788
|
| 70 |
+
- service: http_status:404
|
| 71 |
+
```
|
| 72 |
+
|
| 73 |
+
```powershell
|
| 74 |
+
cloudflared tunnel run etrog-yt-relay
|
| 75 |
+
```
|
| 76 |
+
|
| 77 |
+
Optional: install as a Windows service so it starts with the PC.
|
| 78 |
+
|
| 79 |
+
---
|
| 80 |
+
|
| 81 |
+
## 3. Wire the Worker
|
| 82 |
+
|
| 83 |
+
Set secrets (do **not** commit them):
|
| 84 |
+
|
| 85 |
+
```powershell
|
| 86 |
+
npx wrangler secret put YT_RELAY_URL
|
| 87 |
+
# paste: https://yt-relay.yourdomain.com (no trailing slash)
|
| 88 |
+
|
| 89 |
+
npx wrangler secret put YT_RELAY_SECRET
|
| 90 |
+
# paste: same value as RELAY_SECRET on the PC
|
| 91 |
+
```
|
| 92 |
+
|
| 93 |
+
In `wrangler.toml` you only need the names documented; values come from secrets.
|
| 94 |
+
|
| 95 |
+
Worker integration (minimal) — see [WORKER_HOOK.md](./WORKER_HOOK.md).
|
| 96 |
+
|
| 97 |
+
---
|
| 98 |
+
|
| 99 |
+
## 4. API (for debugging)
|
| 100 |
+
|
| 101 |
+
All routes except `/health` require:
|
| 102 |
+
|
| 103 |
+
```http
|
| 104 |
+
Authorization: Bearer <RELAY_SECRET>
|
| 105 |
+
```
|
| 106 |
+
|
| 107 |
+
### Resolve formats (home IP)
|
| 108 |
+
|
| 109 |
+
```powershell
|
| 110 |
+
curl -s -X POST "https://YOUR-TUNNEL/v1/yt/player" `
|
| 111 |
+
-H "Authorization: Bearer $env:RELAY_SECRET" `
|
| 112 |
+
-H "Content-Type: application/json" `
|
| 113 |
+
-d "{\"videoId\":\"dQw4w9WgXcQ\"}"
|
| 114 |
+
```
|
| 115 |
+
|
| 116 |
+
### Stream media (same IP as resolve)
|
| 117 |
+
|
| 118 |
+
```powershell
|
| 119 |
+
# After you have a googlevideo URL from the player response:
|
| 120 |
+
curl -L "https://YOUR-TUNNEL/v1/fetch?url=$( [uri]::EscapeDataString($gvUrl) )" `
|
| 121 |
+
-H "Authorization: Bearer $env:RELAY_SECRET" `
|
| 122 |
+
-o test.mp4
|
| 123 |
+
```
|
| 124 |
+
|
| 125 |
+
---
|
| 126 |
+
|
| 127 |
+
## Security notes
|
| 128 |
+
|
| 129 |
+
| Control | Why |
|
| 130 |
+
|--------|-----|
|
| 131 |
+
| Shared secret | Only your Worker can use the relay |
|
| 132 |
+
| Host allowlist | Blocks SSRF (only YouTube-related hosts) |
|
| 133 |
+
| Bind `127.0.0.1` | Local only; tunnel is the public front |
|
| 134 |
+
| Tunnel HTTPS | Encryption in transit |
|
| 135 |
+
|
| 136 |
+
Anyone with the secret can use **your home IP** for YouTube traffic — treat it like a password. Rotate if it leaks.
|
| 137 |
+
|
| 138 |
+
This relay is for **your own lawful access** through over-blocking filters, same as the main proxy.
|
| 139 |
+
|
| 140 |
+
---
|
| 141 |
+
|
| 142 |
+
## Ops tips
|
| 143 |
+
|
| 144 |
+
- PC must be **awake** (disable sleep while using YouTube through the proxy).
|
| 145 |
+
- If formats work but media 403s, you resolved on relay but streamed from CF — both must go through the relay.
|
| 146 |
+
- Cookies (optional): POST `{ "videoId":"…", "cookie":"…" }` to `/v1/yt/player`. Prefer cookies **on the home machine**, not CF.
|
| 147 |
+
- Check public egress IP: open `https://api.ipify.org` from a quick script on the PC and confirm it is residential.
|
| 148 |
+
|
| 149 |
+
---
|
| 150 |
+
|
| 151 |
+
## Difficulty
|
| 152 |
+
|
| 153 |
+
| Piece | Effort |
|
| 154 |
+
|-------|--------|
|
| 155 |
+
| This Node server | Done (copy folder, `npm start`) |
|
| 156 |
+
| Quick cloudflared URL | ~5 minutes |
|
| 157 |
+
| Named tunnel + DNS | ~15–30 minutes once |
|
| 158 |
+
| Worker hooks | ~30–60 minutes (see WORKER_HOOK.md) |
|
| 159 |
+
|
| 160 |
+
No extra paid services required.
|
|
@@ -0,0 +1,170 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Worker integration hooks
|
| 2 |
+
|
| 3 |
+
Minimal changes so `/ytp` media uses home egress when secrets are set.
|
| 4 |
+
|
| 5 |
+
## Env secrets
|
| 6 |
+
|
| 7 |
+
| Name | Example |
|
| 8 |
+
|------|---------|
|
| 9 |
+
| `YT_RELAY_URL` | `https://yt-relay.example.com` |
|
| 10 |
+
| `YT_RELAY_SECRET` | same as PC `RELAY_SECRET` |
|
| 11 |
+
|
| 12 |
+
Export in Worker:
|
| 13 |
+
|
| 14 |
+
```js
|
| 15 |
+
export default {
|
| 16 |
+
async fetch(request, env, ctx) {
|
| 17 |
+
// pass env into handlers that resolve/stream YouTube
|
| 18 |
+
}
|
| 19 |
+
}
|
| 20 |
+
```
|
| 21 |
+
|
| 22 |
+
If your `worker.js` still uses the classic `addEventListener('fetch')` form without `env`, switch to module syntax **or** hardcode temporarily only for local tests (never commit secrets).
|
| 23 |
+
|
| 24 |
+
With Wrangler module Worker, secrets appear as `env.YT_RELAY_URL`.
|
| 25 |
+
|
| 26 |
+
---
|
| 27 |
+
|
| 28 |
+
## Drop-in helpers (paste near top of worker.js)
|
| 29 |
+
|
| 30 |
+
```js
|
| 31 |
+
function relayConfigured(env) {
|
| 32 |
+
return !!(env && env.YT_RELAY_URL && env.YT_RELAY_SECRET);
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
function relayBase(env) {
|
| 36 |
+
return String(env.YT_RELAY_URL || '').replace(/\/+$/, '');
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
async function relayPlayer(env, videoId, cookie) {
|
| 40 |
+
const r = await fetch(relayBase(env) + '/v1/yt/player', {
|
| 41 |
+
method: 'POST',
|
| 42 |
+
headers: {
|
| 43 |
+
authorization: 'Bearer ' + env.YT_RELAY_SECRET,
|
| 44 |
+
'content-type': 'application/json',
|
| 45 |
+
},
|
| 46 |
+
body: JSON.stringify({ videoId, cookie: cookie || '' }),
|
| 47 |
+
});
|
| 48 |
+
const j = await r.json().catch(() => null);
|
| 49 |
+
if (!j || !j.ok || !j.formats || !j.formats.length) {
|
| 50 |
+
return { ok: false, playability: j?.playability, attemptsLog: j?.attempts || [], source: 'home-relay' };
|
| 51 |
+
}
|
| 52 |
+
return {
|
| 53 |
+
ok: true,
|
| 54 |
+
formats: j.formats.map((f) => ({
|
| 55 |
+
itag: f.itag,
|
| 56 |
+
url: f.url,
|
| 57 |
+
mime: (f.mimeType || '').split(';')[0],
|
| 58 |
+
progressive: !!f.progressive,
|
| 59 |
+
hasAudio: !!f.hasAudio,
|
| 60 |
+
hasVideo: !!f.hasVideo,
|
| 61 |
+
bitrate: f.bitrate || 0,
|
| 62 |
+
width: f.width || 0,
|
| 63 |
+
height: f.height || 0,
|
| 64 |
+
})),
|
| 65 |
+
playability: j.playability,
|
| 66 |
+
client: j.client,
|
| 67 |
+
hasCookies: !!cookie,
|
| 68 |
+
hasAuth: !!cookie,
|
| 69 |
+
attemptsLog: j.attempts || [],
|
| 70 |
+
via: 'home-relay',
|
| 71 |
+
};
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
/** Stream googlevideo (or any allowlisted URL) through home IP */
|
| 75 |
+
async function relayFetch(env, targetUrl, init = {}) {
|
| 76 |
+
const u = new URL(relayBase(env) + '/v1/fetch');
|
| 77 |
+
u.searchParams.set('url', targetUrl);
|
| 78 |
+
const headers = {
|
| 79 |
+
authorization: 'Bearer ' + env.YT_RELAY_SECRET,
|
| 80 |
+
};
|
| 81 |
+
if (init.range) headers.range = init.range;
|
| 82 |
+
if (init.userAgent) headers['x-upstream-ua'] = init.userAgent;
|
| 83 |
+
return fetch(u.toString(), { headers, signal: init.signal });
|
| 84 |
+
}
|
| 85 |
+
```
|
| 86 |
+
|
| 87 |
+
---
|
| 88 |
+
|
| 89 |
+
## Where to call them
|
| 90 |
+
|
| 91 |
+
### 1) Prefer relay in `fetchPlayerFormats` / `getPlayerFormatsCached`
|
| 92 |
+
|
| 93 |
+
At the **start** of format resolve:
|
| 94 |
+
|
| 95 |
+
```js
|
| 96 |
+
async function fetchPlayerFormats(videoId, request, env) {
|
| 97 |
+
if (relayConfigured(env)) {
|
| 98 |
+
const ytCookies = await getYoutubeCookieHeader(request); // optional
|
| 99 |
+
const pack = await relayPlayer(env, videoId, ytCookies || '');
|
| 100 |
+
if (pack.ok) return pack;
|
| 101 |
+
// fall through to existing CF multi-client path only as last resort
|
| 102 |
+
}
|
| 103 |
+
// … existing tryInnertubeClient races …
|
| 104 |
+
}
|
| 105 |
+
```
|
| 106 |
+
|
| 107 |
+
### 2) Prefer relay when fetching media bytes in `handleYtStream`
|
| 108 |
+
|
| 109 |
+
Wherever you currently:
|
| 110 |
+
|
| 111 |
+
```js
|
| 112 |
+
const mediaRes = await fetch(directMediaUrl, { headers: fetchHeaders, ... });
|
| 113 |
+
```
|
| 114 |
+
|
| 115 |
+
change to:
|
| 116 |
+
|
| 117 |
+
```js
|
| 118 |
+
let mediaRes;
|
| 119 |
+
if (relayConfigured(env)) {
|
| 120 |
+
mediaRes = await relayFetch(env, directMediaUrl, {
|
| 121 |
+
range: request.headers.get('range') || undefined,
|
| 122 |
+
userAgent: fetchHeaders['user-agent'],
|
| 123 |
+
});
|
| 124 |
+
} else {
|
| 125 |
+
mediaRes = await fetch(directMediaUrl, { headers: fetchHeaders, redirect: 'follow' });
|
| 126 |
+
}
|
| 127 |
+
```
|
| 128 |
+
|
| 129 |
+
**Critical:** formats from relay + media from relay (never mix CF media with home-resolved URLs).
|
| 130 |
+
|
| 131 |
+
### 3) Pass `env` through
|
| 132 |
+
|
| 133 |
+
Today many handlers only take `(request, url)`. Thread `env` from the top-level `fetch(request, env, ctx)`.
|
| 134 |
+
|
| 135 |
+
---
|
| 136 |
+
|
| 137 |
+
## Suggested policy once relay is online
|
| 138 |
+
|
| 139 |
+
```text
|
| 140 |
+
if (YT_RELAY configured):
|
| 141 |
+
resolve on home
|
| 142 |
+
stream on home
|
| 143 |
+
skip CF InnerTube races (saves CPU + avoids false LOGIN_REQUIRED cache)
|
| 144 |
+
else:
|
| 145 |
+
existing behavior (client Origin:null iframe + CF best-effort)
|
| 146 |
+
```
|
| 147 |
+
|
| 148 |
+
---
|
| 149 |
+
|
| 150 |
+
## Smoke test matrix
|
| 151 |
+
|
| 152 |
+
| Test | Expect |
|
| 153 |
+
|------|--------|
|
| 154 |
+
| PC `curl` `/v1/yt/player` | `ok:true`, formats with `https://…googlevideo…` |
|
| 155 |
+
| Worker `/api/health` (optional) | show `relay: up` if you add a check |
|
| 156 |
+
| `/ytp?v=…` play | `X-Etrog-Source` or status text mentioning relay |
|
| 157 |
+
| Kill home relay | Worker falls back or shows clear “egress offline” |
|
| 158 |
+
|
| 159 |
+
Optional health from Worker:
|
| 160 |
+
|
| 161 |
+
```js
|
| 162 |
+
async function relayHealth(env) {
|
| 163 |
+
try {
|
| 164 |
+
const r = await fetch(relayBase(env) + '/health', { signal: AbortSignal.timeout(4000) });
|
| 165 |
+
return r.ok;
|
| 166 |
+
} catch { return false; }
|
| 167 |
+
}
|
| 168 |
+
```
|
| 169 |
+
|
| 170 |
+
(Health is public on purpose so you can ping without shipping the secret to browsers.)
|
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Set sh = CreateObject("WScript.Shell")
|
| 2 |
+
Dim relayDir, relayLog, relayErr, secret, nodeExe
|
| 3 |
+
relayDir = WScript.Arguments(0)
|
| 4 |
+
relayLog = WScript.Arguments(1)
|
| 5 |
+
relayErr = WScript.Arguments(2)
|
| 6 |
+
secret = WScript.Arguments(3)
|
| 7 |
+
|
| 8 |
+
sh.Environment("Process")("RELAY_SECRET") = secret
|
| 9 |
+
sh.Environment("Process")("PORT") = "8788"
|
| 10 |
+
|
| 11 |
+
Dim cmd
|
| 12 |
+
cmd = "cmd.exe /c node server.js >> """ & relayLog & """ 2>> """ & relayErr & """"
|
| 13 |
+
|
| 14 |
+
sh.CurrentDirectory = relayDir
|
| 15 |
+
sh.Run cmd, 0, False
|
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Set sh = CreateObject("WScript.Shell")
|
| 2 |
+
Dim cfExe, cfLog
|
| 3 |
+
cfExe = WScript.Arguments(0)
|
| 4 |
+
cfLog = WScript.Arguments(1)
|
| 5 |
+
|
| 6 |
+
Dim cmd
|
| 7 |
+
cmd = "cmd.exe /c """ & cfExe & """ tunnel --protocol http2 --url http://127.0.0.1:8788 --logfile """ & cfLog & """"
|
| 8 |
+
|
| 9 |
+
sh.Run cmd, 0, False
|
|
@@ -0,0 +1,729 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "etrog-yt-home-relay",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"lockfileVersion": 3,
|
| 5 |
+
"requires": true,
|
| 6 |
+
"packages": {
|
| 7 |
+
"": {
|
| 8 |
+
"name": "etrog-yt-home-relay",
|
| 9 |
+
"version": "1.0.0",
|
| 10 |
+
"dependencies": {
|
| 11 |
+
"sqlite3": "^6.0.1"
|
| 12 |
+
},
|
| 13 |
+
"engines": {
|
| 14 |
+
"node": ">=18"
|
| 15 |
+
}
|
| 16 |
+
},
|
| 17 |
+
"node_modules/@isaacs/fs-minipass": {
|
| 18 |
+
"version": "4.0.1",
|
| 19 |
+
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
|
| 20 |
+
"integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
|
| 21 |
+
"license": "ISC",
|
| 22 |
+
"dependencies": {
|
| 23 |
+
"minipass": "^7.0.4"
|
| 24 |
+
},
|
| 25 |
+
"engines": {
|
| 26 |
+
"node": ">=18.0.0"
|
| 27 |
+
}
|
| 28 |
+
},
|
| 29 |
+
"node_modules/abbrev": {
|
| 30 |
+
"version": "4.0.0",
|
| 31 |
+
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz",
|
| 32 |
+
"integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==",
|
| 33 |
+
"license": "ISC",
|
| 34 |
+
"optional": true,
|
| 35 |
+
"engines": {
|
| 36 |
+
"node": "^20.17.0 || >=22.9.0"
|
| 37 |
+
}
|
| 38 |
+
},
|
| 39 |
+
"node_modules/base64-js": {
|
| 40 |
+
"version": "1.5.1",
|
| 41 |
+
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
| 42 |
+
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
| 43 |
+
"funding": [
|
| 44 |
+
{
|
| 45 |
+
"type": "github",
|
| 46 |
+
"url": "https://github.com/sponsors/feross"
|
| 47 |
+
},
|
| 48 |
+
{
|
| 49 |
+
"type": "patreon",
|
| 50 |
+
"url": "https://www.patreon.com/feross"
|
| 51 |
+
},
|
| 52 |
+
{
|
| 53 |
+
"type": "consulting",
|
| 54 |
+
"url": "https://feross.org/support"
|
| 55 |
+
}
|
| 56 |
+
],
|
| 57 |
+
"license": "MIT"
|
| 58 |
+
},
|
| 59 |
+
"node_modules/bindings": {
|
| 60 |
+
"version": "1.5.0",
|
| 61 |
+
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
| 62 |
+
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
|
| 63 |
+
"license": "MIT",
|
| 64 |
+
"dependencies": {
|
| 65 |
+
"file-uri-to-path": "1.0.0"
|
| 66 |
+
}
|
| 67 |
+
},
|
| 68 |
+
"node_modules/bl": {
|
| 69 |
+
"version": "4.1.0",
|
| 70 |
+
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
|
| 71 |
+
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
|
| 72 |
+
"license": "MIT",
|
| 73 |
+
"dependencies": {
|
| 74 |
+
"buffer": "^5.5.0",
|
| 75 |
+
"inherits": "^2.0.4",
|
| 76 |
+
"readable-stream": "^3.4.0"
|
| 77 |
+
}
|
| 78 |
+
},
|
| 79 |
+
"node_modules/buffer": {
|
| 80 |
+
"version": "5.7.1",
|
| 81 |
+
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
| 82 |
+
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
| 83 |
+
"funding": [
|
| 84 |
+
{
|
| 85 |
+
"type": "github",
|
| 86 |
+
"url": "https://github.com/sponsors/feross"
|
| 87 |
+
},
|
| 88 |
+
{
|
| 89 |
+
"type": "patreon",
|
| 90 |
+
"url": "https://www.patreon.com/feross"
|
| 91 |
+
},
|
| 92 |
+
{
|
| 93 |
+
"type": "consulting",
|
| 94 |
+
"url": "https://feross.org/support"
|
| 95 |
+
}
|
| 96 |
+
],
|
| 97 |
+
"license": "MIT",
|
| 98 |
+
"dependencies": {
|
| 99 |
+
"base64-js": "^1.3.1",
|
| 100 |
+
"ieee754": "^1.1.13"
|
| 101 |
+
}
|
| 102 |
+
},
|
| 103 |
+
"node_modules/chownr": {
|
| 104 |
+
"version": "3.0.0",
|
| 105 |
+
"resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
|
| 106 |
+
"integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
|
| 107 |
+
"license": "BlueOak-1.0.0",
|
| 108 |
+
"engines": {
|
| 109 |
+
"node": ">=18"
|
| 110 |
+
}
|
| 111 |
+
},
|
| 112 |
+
"node_modules/decompress-response": {
|
| 113 |
+
"version": "6.0.0",
|
| 114 |
+
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
|
| 115 |
+
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
|
| 116 |
+
"license": "MIT",
|
| 117 |
+
"dependencies": {
|
| 118 |
+
"mimic-response": "^3.1.0"
|
| 119 |
+
},
|
| 120 |
+
"engines": {
|
| 121 |
+
"node": ">=10"
|
| 122 |
+
},
|
| 123 |
+
"funding": {
|
| 124 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 125 |
+
}
|
| 126 |
+
},
|
| 127 |
+
"node_modules/deep-extend": {
|
| 128 |
+
"version": "0.6.0",
|
| 129 |
+
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
| 130 |
+
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
|
| 131 |
+
"license": "MIT",
|
| 132 |
+
"engines": {
|
| 133 |
+
"node": ">=4.0.0"
|
| 134 |
+
}
|
| 135 |
+
},
|
| 136 |
+
"node_modules/detect-libc": {
|
| 137 |
+
"version": "2.1.2",
|
| 138 |
+
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
| 139 |
+
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
| 140 |
+
"license": "Apache-2.0",
|
| 141 |
+
"engines": {
|
| 142 |
+
"node": ">=8"
|
| 143 |
+
}
|
| 144 |
+
},
|
| 145 |
+
"node_modules/end-of-stream": {
|
| 146 |
+
"version": "1.4.5",
|
| 147 |
+
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
| 148 |
+
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
|
| 149 |
+
"license": "MIT",
|
| 150 |
+
"dependencies": {
|
| 151 |
+
"once": "^1.4.0"
|
| 152 |
+
}
|
| 153 |
+
},
|
| 154 |
+
"node_modules/env-paths": {
|
| 155 |
+
"version": "2.2.1",
|
| 156 |
+
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
|
| 157 |
+
"integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
|
| 158 |
+
"license": "MIT",
|
| 159 |
+
"optional": true,
|
| 160 |
+
"engines": {
|
| 161 |
+
"node": ">=6"
|
| 162 |
+
}
|
| 163 |
+
},
|
| 164 |
+
"node_modules/expand-template": {
|
| 165 |
+
"version": "2.0.3",
|
| 166 |
+
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
|
| 167 |
+
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
|
| 168 |
+
"license": "(MIT OR WTFPL)",
|
| 169 |
+
"engines": {
|
| 170 |
+
"node": ">=6"
|
| 171 |
+
}
|
| 172 |
+
},
|
| 173 |
+
"node_modules/exponential-backoff": {
|
| 174 |
+
"version": "3.1.3",
|
| 175 |
+
"resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz",
|
| 176 |
+
"integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==",
|
| 177 |
+
"license": "Apache-2.0",
|
| 178 |
+
"optional": true
|
| 179 |
+
},
|
| 180 |
+
"node_modules/fdir": {
|
| 181 |
+
"version": "6.5.0",
|
| 182 |
+
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
| 183 |
+
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
| 184 |
+
"license": "MIT",
|
| 185 |
+
"optional": true,
|
| 186 |
+
"engines": {
|
| 187 |
+
"node": ">=12.0.0"
|
| 188 |
+
},
|
| 189 |
+
"peerDependencies": {
|
| 190 |
+
"picomatch": "^3 || ^4"
|
| 191 |
+
},
|
| 192 |
+
"peerDependenciesMeta": {
|
| 193 |
+
"picomatch": {
|
| 194 |
+
"optional": true
|
| 195 |
+
}
|
| 196 |
+
}
|
| 197 |
+
},
|
| 198 |
+
"node_modules/file-uri-to-path": {
|
| 199 |
+
"version": "1.0.0",
|
| 200 |
+
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
| 201 |
+
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
|
| 202 |
+
"license": "MIT"
|
| 203 |
+
},
|
| 204 |
+
"node_modules/fs-constants": {
|
| 205 |
+
"version": "1.0.0",
|
| 206 |
+
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
| 207 |
+
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
|
| 208 |
+
"license": "MIT"
|
| 209 |
+
},
|
| 210 |
+
"node_modules/github-from-package": {
|
| 211 |
+
"version": "0.0.0",
|
| 212 |
+
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
|
| 213 |
+
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
|
| 214 |
+
"license": "MIT"
|
| 215 |
+
},
|
| 216 |
+
"node_modules/graceful-fs": {
|
| 217 |
+
"version": "4.2.11",
|
| 218 |
+
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
| 219 |
+
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
| 220 |
+
"license": "ISC",
|
| 221 |
+
"optional": true
|
| 222 |
+
},
|
| 223 |
+
"node_modules/ieee754": {
|
| 224 |
+
"version": "1.2.1",
|
| 225 |
+
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
| 226 |
+
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
| 227 |
+
"funding": [
|
| 228 |
+
{
|
| 229 |
+
"type": "github",
|
| 230 |
+
"url": "https://github.com/sponsors/feross"
|
| 231 |
+
},
|
| 232 |
+
{
|
| 233 |
+
"type": "patreon",
|
| 234 |
+
"url": "https://www.patreon.com/feross"
|
| 235 |
+
},
|
| 236 |
+
{
|
| 237 |
+
"type": "consulting",
|
| 238 |
+
"url": "https://feross.org/support"
|
| 239 |
+
}
|
| 240 |
+
],
|
| 241 |
+
"license": "BSD-3-Clause"
|
| 242 |
+
},
|
| 243 |
+
"node_modules/inherits": {
|
| 244 |
+
"version": "2.0.4",
|
| 245 |
+
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
| 246 |
+
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
| 247 |
+
"license": "ISC"
|
| 248 |
+
},
|
| 249 |
+
"node_modules/ini": {
|
| 250 |
+
"version": "1.3.8",
|
| 251 |
+
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
| 252 |
+
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
| 253 |
+
"license": "ISC"
|
| 254 |
+
},
|
| 255 |
+
"node_modules/isexe": {
|
| 256 |
+
"version": "4.0.0",
|
| 257 |
+
"resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz",
|
| 258 |
+
"integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==",
|
| 259 |
+
"license": "BlueOak-1.0.0",
|
| 260 |
+
"optional": true,
|
| 261 |
+
"engines": {
|
| 262 |
+
"node": ">=20"
|
| 263 |
+
}
|
| 264 |
+
},
|
| 265 |
+
"node_modules/mimic-response": {
|
| 266 |
+
"version": "3.1.0",
|
| 267 |
+
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
|
| 268 |
+
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
|
| 269 |
+
"license": "MIT",
|
| 270 |
+
"engines": {
|
| 271 |
+
"node": ">=10"
|
| 272 |
+
},
|
| 273 |
+
"funding": {
|
| 274 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 275 |
+
}
|
| 276 |
+
},
|
| 277 |
+
"node_modules/minimist": {
|
| 278 |
+
"version": "1.2.8",
|
| 279 |
+
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
| 280 |
+
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
| 281 |
+
"license": "MIT",
|
| 282 |
+
"funding": {
|
| 283 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 284 |
+
}
|
| 285 |
+
},
|
| 286 |
+
"node_modules/minipass": {
|
| 287 |
+
"version": "7.1.3",
|
| 288 |
+
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
|
| 289 |
+
"integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
|
| 290 |
+
"license": "BlueOak-1.0.0",
|
| 291 |
+
"engines": {
|
| 292 |
+
"node": ">=16 || 14 >=14.17"
|
| 293 |
+
}
|
| 294 |
+
},
|
| 295 |
+
"node_modules/minizlib": {
|
| 296 |
+
"version": "3.1.0",
|
| 297 |
+
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz",
|
| 298 |
+
"integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
|
| 299 |
+
"license": "MIT",
|
| 300 |
+
"dependencies": {
|
| 301 |
+
"minipass": "^7.1.2"
|
| 302 |
+
},
|
| 303 |
+
"engines": {
|
| 304 |
+
"node": ">= 18"
|
| 305 |
+
}
|
| 306 |
+
},
|
| 307 |
+
"node_modules/mkdirp-classic": {
|
| 308 |
+
"version": "0.5.3",
|
| 309 |
+
"resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
|
| 310 |
+
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
|
| 311 |
+
"license": "MIT"
|
| 312 |
+
},
|
| 313 |
+
"node_modules/napi-build-utils": {
|
| 314 |
+
"version": "2.0.0",
|
| 315 |
+
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
|
| 316 |
+
"integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
|
| 317 |
+
"license": "MIT"
|
| 318 |
+
},
|
| 319 |
+
"node_modules/node-abi": {
|
| 320 |
+
"version": "3.94.0",
|
| 321 |
+
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.94.0.tgz",
|
| 322 |
+
"integrity": "sha512-W5ZNO5KRPB5TkYmGVD9F6YqhsglXJzE6etpbmT+f6EQElhiX/UTG551cnsRGvLG3fyZEg9HwaDmNmj5nwJ4z9g==",
|
| 323 |
+
"license": "MIT",
|
| 324 |
+
"dependencies": {
|
| 325 |
+
"semver": "^7.3.5"
|
| 326 |
+
},
|
| 327 |
+
"engines": {
|
| 328 |
+
"node": ">=10"
|
| 329 |
+
}
|
| 330 |
+
},
|
| 331 |
+
"node_modules/node-addon-api": {
|
| 332 |
+
"version": "8.9.0",
|
| 333 |
+
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.9.0.tgz",
|
| 334 |
+
"integrity": "sha512-ekZMeaaIzSQTSpr7X2X3iJM7lTzgnx8ahAG9pJfT/7+14mlEM8ZYQ9cgCDvSSRbReFK0oHli3WrZdCiRsgAT9Q==",
|
| 335 |
+
"license": "MIT",
|
| 336 |
+
"engines": {
|
| 337 |
+
"node": "^18 || ^20 || >= 21"
|
| 338 |
+
}
|
| 339 |
+
},
|
| 340 |
+
"node_modules/node-gyp": {
|
| 341 |
+
"version": "12.4.0",
|
| 342 |
+
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.4.0.tgz",
|
| 343 |
+
"integrity": "sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==",
|
| 344 |
+
"license": "MIT",
|
| 345 |
+
"optional": true,
|
| 346 |
+
"dependencies": {
|
| 347 |
+
"env-paths": "^2.2.0",
|
| 348 |
+
"exponential-backoff": "^3.1.1",
|
| 349 |
+
"graceful-fs": "^4.2.6",
|
| 350 |
+
"nopt": "^9.0.0",
|
| 351 |
+
"proc-log": "^6.0.0",
|
| 352 |
+
"semver": "^7.3.5",
|
| 353 |
+
"tar": "^7.5.4",
|
| 354 |
+
"tinyglobby": "^0.2.12",
|
| 355 |
+
"undici": "^6.25.0",
|
| 356 |
+
"which": "^6.0.0"
|
| 357 |
+
},
|
| 358 |
+
"bin": {
|
| 359 |
+
"node-gyp": "bin/node-gyp.js"
|
| 360 |
+
},
|
| 361 |
+
"engines": {
|
| 362 |
+
"node": "^20.17.0 || >=22.9.0"
|
| 363 |
+
}
|
| 364 |
+
},
|
| 365 |
+
"node_modules/nopt": {
|
| 366 |
+
"version": "9.0.0",
|
| 367 |
+
"resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz",
|
| 368 |
+
"integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==",
|
| 369 |
+
"license": "ISC",
|
| 370 |
+
"optional": true,
|
| 371 |
+
"dependencies": {
|
| 372 |
+
"abbrev": "^4.0.0"
|
| 373 |
+
},
|
| 374 |
+
"bin": {
|
| 375 |
+
"nopt": "bin/nopt.js"
|
| 376 |
+
},
|
| 377 |
+
"engines": {
|
| 378 |
+
"node": "^20.17.0 || >=22.9.0"
|
| 379 |
+
}
|
| 380 |
+
},
|
| 381 |
+
"node_modules/once": {
|
| 382 |
+
"version": "1.4.0",
|
| 383 |
+
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
| 384 |
+
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
| 385 |
+
"license": "ISC",
|
| 386 |
+
"dependencies": {
|
| 387 |
+
"wrappy": "1"
|
| 388 |
+
}
|
| 389 |
+
},
|
| 390 |
+
"node_modules/picomatch": {
|
| 391 |
+
"version": "4.0.5",
|
| 392 |
+
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
|
| 393 |
+
"integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
|
| 394 |
+
"license": "MIT",
|
| 395 |
+
"optional": true,
|
| 396 |
+
"peer": true,
|
| 397 |
+
"engines": {
|
| 398 |
+
"node": ">=12"
|
| 399 |
+
},
|
| 400 |
+
"funding": {
|
| 401 |
+
"url": "https://github.com/sponsors/jonschlinkert"
|
| 402 |
+
}
|
| 403 |
+
},
|
| 404 |
+
"node_modules/prebuild-install": {
|
| 405 |
+
"version": "7.1.3",
|
| 406 |
+
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
|
| 407 |
+
"integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
|
| 408 |
+
"deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.",
|
| 409 |
+
"license": "MIT",
|
| 410 |
+
"dependencies": {
|
| 411 |
+
"detect-libc": "^2.0.0",
|
| 412 |
+
"expand-template": "^2.0.3",
|
| 413 |
+
"github-from-package": "0.0.0",
|
| 414 |
+
"minimist": "^1.2.3",
|
| 415 |
+
"mkdirp-classic": "^0.5.3",
|
| 416 |
+
"napi-build-utils": "^2.0.0",
|
| 417 |
+
"node-abi": "^3.3.0",
|
| 418 |
+
"pump": "^3.0.0",
|
| 419 |
+
"rc": "^1.2.7",
|
| 420 |
+
"simple-get": "^4.0.0",
|
| 421 |
+
"tar-fs": "^2.0.0",
|
| 422 |
+
"tunnel-agent": "^0.6.0"
|
| 423 |
+
},
|
| 424 |
+
"bin": {
|
| 425 |
+
"prebuild-install": "bin.js"
|
| 426 |
+
},
|
| 427 |
+
"engines": {
|
| 428 |
+
"node": ">=10"
|
| 429 |
+
}
|
| 430 |
+
},
|
| 431 |
+
"node_modules/proc-log": {
|
| 432 |
+
"version": "6.1.0",
|
| 433 |
+
"resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz",
|
| 434 |
+
"integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==",
|
| 435 |
+
"license": "ISC",
|
| 436 |
+
"optional": true,
|
| 437 |
+
"engines": {
|
| 438 |
+
"node": "^20.17.0 || >=22.9.0"
|
| 439 |
+
}
|
| 440 |
+
},
|
| 441 |
+
"node_modules/pump": {
|
| 442 |
+
"version": "3.0.4",
|
| 443 |
+
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
|
| 444 |
+
"integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
|
| 445 |
+
"license": "MIT",
|
| 446 |
+
"dependencies": {
|
| 447 |
+
"end-of-stream": "^1.1.0",
|
| 448 |
+
"once": "^1.3.1"
|
| 449 |
+
}
|
| 450 |
+
},
|
| 451 |
+
"node_modules/rc": {
|
| 452 |
+
"version": "1.2.8",
|
| 453 |
+
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
| 454 |
+
"integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
|
| 455 |
+
"license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
|
| 456 |
+
"dependencies": {
|
| 457 |
+
"deep-extend": "^0.6.0",
|
| 458 |
+
"ini": "~1.3.0",
|
| 459 |
+
"minimist": "^1.2.0",
|
| 460 |
+
"strip-json-comments": "~2.0.1"
|
| 461 |
+
},
|
| 462 |
+
"bin": {
|
| 463 |
+
"rc": "cli.js"
|
| 464 |
+
}
|
| 465 |
+
},
|
| 466 |
+
"node_modules/readable-stream": {
|
| 467 |
+
"version": "3.6.2",
|
| 468 |
+
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
| 469 |
+
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
| 470 |
+
"license": "MIT",
|
| 471 |
+
"dependencies": {
|
| 472 |
+
"inherits": "^2.0.3",
|
| 473 |
+
"string_decoder": "^1.1.1",
|
| 474 |
+
"util-deprecate": "^1.0.1"
|
| 475 |
+
},
|
| 476 |
+
"engines": {
|
| 477 |
+
"node": ">= 6"
|
| 478 |
+
}
|
| 479 |
+
},
|
| 480 |
+
"node_modules/safe-buffer": {
|
| 481 |
+
"version": "5.2.1",
|
| 482 |
+
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
| 483 |
+
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
| 484 |
+
"funding": [
|
| 485 |
+
{
|
| 486 |
+
"type": "github",
|
| 487 |
+
"url": "https://github.com/sponsors/feross"
|
| 488 |
+
},
|
| 489 |
+
{
|
| 490 |
+
"type": "patreon",
|
| 491 |
+
"url": "https://www.patreon.com/feross"
|
| 492 |
+
},
|
| 493 |
+
{
|
| 494 |
+
"type": "consulting",
|
| 495 |
+
"url": "https://feross.org/support"
|
| 496 |
+
}
|
| 497 |
+
],
|
| 498 |
+
"license": "MIT"
|
| 499 |
+
},
|
| 500 |
+
"node_modules/semver": {
|
| 501 |
+
"version": "7.8.5",
|
| 502 |
+
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
|
| 503 |
+
"integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
|
| 504 |
+
"license": "ISC",
|
| 505 |
+
"bin": {
|
| 506 |
+
"semver": "bin/semver.js"
|
| 507 |
+
},
|
| 508 |
+
"engines": {
|
| 509 |
+
"node": ">=10"
|
| 510 |
+
}
|
| 511 |
+
},
|
| 512 |
+
"node_modules/simple-concat": {
|
| 513 |
+
"version": "1.0.1",
|
| 514 |
+
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
|
| 515 |
+
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
|
| 516 |
+
"funding": [
|
| 517 |
+
{
|
| 518 |
+
"type": "github",
|
| 519 |
+
"url": "https://github.com/sponsors/feross"
|
| 520 |
+
},
|
| 521 |
+
{
|
| 522 |
+
"type": "patreon",
|
| 523 |
+
"url": "https://www.patreon.com/feross"
|
| 524 |
+
},
|
| 525 |
+
{
|
| 526 |
+
"type": "consulting",
|
| 527 |
+
"url": "https://feross.org/support"
|
| 528 |
+
}
|
| 529 |
+
],
|
| 530 |
+
"license": "MIT"
|
| 531 |
+
},
|
| 532 |
+
"node_modules/simple-get": {
|
| 533 |
+
"version": "4.0.1",
|
| 534 |
+
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz",
|
| 535 |
+
"integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
|
| 536 |
+
"funding": [
|
| 537 |
+
{
|
| 538 |
+
"type": "github",
|
| 539 |
+
"url": "https://github.com/sponsors/feross"
|
| 540 |
+
},
|
| 541 |
+
{
|
| 542 |
+
"type": "patreon",
|
| 543 |
+
"url": "https://www.patreon.com/feross"
|
| 544 |
+
},
|
| 545 |
+
{
|
| 546 |
+
"type": "consulting",
|
| 547 |
+
"url": "https://feross.org/support"
|
| 548 |
+
}
|
| 549 |
+
],
|
| 550 |
+
"license": "MIT",
|
| 551 |
+
"dependencies": {
|
| 552 |
+
"decompress-response": "^6.0.0",
|
| 553 |
+
"once": "^1.3.1",
|
| 554 |
+
"simple-concat": "^1.0.0"
|
| 555 |
+
}
|
| 556 |
+
},
|
| 557 |
+
"node_modules/sqlite3": {
|
| 558 |
+
"version": "6.0.1",
|
| 559 |
+
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-6.0.1.tgz",
|
| 560 |
+
"integrity": "sha512-X0czUUMG2tmSqJpEQa3tCuZSHKIx8PwM53vLZzKp/o6Rpy25fiVfjdbnZ988M8+O3ZWR1ih0K255VumCb3MAnQ==",
|
| 561 |
+
"hasInstallScript": true,
|
| 562 |
+
"license": "BSD-3-Clause",
|
| 563 |
+
"dependencies": {
|
| 564 |
+
"bindings": "^1.5.0",
|
| 565 |
+
"node-addon-api": "^8.0.0",
|
| 566 |
+
"prebuild-install": "^7.1.3",
|
| 567 |
+
"tar": "^7.5.10"
|
| 568 |
+
},
|
| 569 |
+
"engines": {
|
| 570 |
+
"node": ">=20.17.0"
|
| 571 |
+
},
|
| 572 |
+
"optionalDependencies": {
|
| 573 |
+
"node-gyp": "12.x"
|
| 574 |
+
},
|
| 575 |
+
"peerDependencies": {
|
| 576 |
+
"node-gyp": "12.x"
|
| 577 |
+
},
|
| 578 |
+
"peerDependenciesMeta": {
|
| 579 |
+
"node-gyp": {
|
| 580 |
+
"optional": true
|
| 581 |
+
}
|
| 582 |
+
}
|
| 583 |
+
},
|
| 584 |
+
"node_modules/string_decoder": {
|
| 585 |
+
"version": "1.3.0",
|
| 586 |
+
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
| 587 |
+
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
| 588 |
+
"license": "MIT",
|
| 589 |
+
"dependencies": {
|
| 590 |
+
"safe-buffer": "~5.2.0"
|
| 591 |
+
}
|
| 592 |
+
},
|
| 593 |
+
"node_modules/strip-json-comments": {
|
| 594 |
+
"version": "2.0.1",
|
| 595 |
+
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
| 596 |
+
"integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
|
| 597 |
+
"license": "MIT",
|
| 598 |
+
"engines": {
|
| 599 |
+
"node": ">=0.10.0"
|
| 600 |
+
}
|
| 601 |
+
},
|
| 602 |
+
"node_modules/tar": {
|
| 603 |
+
"version": "7.5.20",
|
| 604 |
+
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.20.tgz",
|
| 605 |
+
"integrity": "sha512-9FcyK4PA6+WbzlTM9WhQm6vB5W7cP7dUiPsv1g7YDwEQnQ1CGpK3MGlKk/ITVWMk05kHZuBhmVhiv8LZoy/PFQ==",
|
| 606 |
+
"license": "BlueOak-1.0.0",
|
| 607 |
+
"dependencies": {
|
| 608 |
+
"@isaacs/fs-minipass": "^4.0.0",
|
| 609 |
+
"chownr": "^3.0.0",
|
| 610 |
+
"minipass": "^7.1.2",
|
| 611 |
+
"minizlib": "^3.1.0",
|
| 612 |
+
"yallist": "^5.0.0"
|
| 613 |
+
},
|
| 614 |
+
"engines": {
|
| 615 |
+
"node": ">=18"
|
| 616 |
+
}
|
| 617 |
+
},
|
| 618 |
+
"node_modules/tar-fs": {
|
| 619 |
+
"version": "2.1.5",
|
| 620 |
+
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.5.tgz",
|
| 621 |
+
"integrity": "sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==",
|
| 622 |
+
"license": "MIT",
|
| 623 |
+
"dependencies": {
|
| 624 |
+
"chownr": "^1.1.1",
|
| 625 |
+
"mkdirp-classic": "^0.5.2",
|
| 626 |
+
"pump": "^3.0.0",
|
| 627 |
+
"tar-stream": "^2.1.4"
|
| 628 |
+
}
|
| 629 |
+
},
|
| 630 |
+
"node_modules/tar-fs/node_modules/chownr": {
|
| 631 |
+
"version": "1.1.4",
|
| 632 |
+
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
| 633 |
+
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
|
| 634 |
+
"license": "ISC"
|
| 635 |
+
},
|
| 636 |
+
"node_modules/tar-stream": {
|
| 637 |
+
"version": "2.2.0",
|
| 638 |
+
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
|
| 639 |
+
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
|
| 640 |
+
"license": "MIT",
|
| 641 |
+
"dependencies": {
|
| 642 |
+
"bl": "^4.0.3",
|
| 643 |
+
"end-of-stream": "^1.4.1",
|
| 644 |
+
"fs-constants": "^1.0.0",
|
| 645 |
+
"inherits": "^2.0.3",
|
| 646 |
+
"readable-stream": "^3.1.1"
|
| 647 |
+
},
|
| 648 |
+
"engines": {
|
| 649 |
+
"node": ">=6"
|
| 650 |
+
}
|
| 651 |
+
},
|
| 652 |
+
"node_modules/tinyglobby": {
|
| 653 |
+
"version": "0.2.17",
|
| 654 |
+
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
|
| 655 |
+
"integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
|
| 656 |
+
"license": "MIT",
|
| 657 |
+
"optional": true,
|
| 658 |
+
"dependencies": {
|
| 659 |
+
"fdir": "^6.5.0",
|
| 660 |
+
"picomatch": "^4.0.4"
|
| 661 |
+
},
|
| 662 |
+
"engines": {
|
| 663 |
+
"node": ">=12.0.0"
|
| 664 |
+
},
|
| 665 |
+
"funding": {
|
| 666 |
+
"url": "https://github.com/sponsors/SuperchupuDev"
|
| 667 |
+
}
|
| 668 |
+
},
|
| 669 |
+
"node_modules/tunnel-agent": {
|
| 670 |
+
"version": "0.6.0",
|
| 671 |
+
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
| 672 |
+
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
|
| 673 |
+
"license": "Apache-2.0",
|
| 674 |
+
"dependencies": {
|
| 675 |
+
"safe-buffer": "^5.0.1"
|
| 676 |
+
},
|
| 677 |
+
"engines": {
|
| 678 |
+
"node": "*"
|
| 679 |
+
}
|
| 680 |
+
},
|
| 681 |
+
"node_modules/undici": {
|
| 682 |
+
"version": "6.27.0",
|
| 683 |
+
"resolved": "https://registry.npmjs.org/undici/-/undici-6.27.0.tgz",
|
| 684 |
+
"integrity": "sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==",
|
| 685 |
+
"license": "MIT",
|
| 686 |
+
"optional": true,
|
| 687 |
+
"engines": {
|
| 688 |
+
"node": ">=18.17"
|
| 689 |
+
}
|
| 690 |
+
},
|
| 691 |
+
"node_modules/util-deprecate": {
|
| 692 |
+
"version": "1.0.2",
|
| 693 |
+
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
| 694 |
+
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
| 695 |
+
"license": "MIT"
|
| 696 |
+
},
|
| 697 |
+
"node_modules/which": {
|
| 698 |
+
"version": "6.0.1",
|
| 699 |
+
"resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz",
|
| 700 |
+
"integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==",
|
| 701 |
+
"license": "ISC",
|
| 702 |
+
"optional": true,
|
| 703 |
+
"dependencies": {
|
| 704 |
+
"isexe": "^4.0.0"
|
| 705 |
+
},
|
| 706 |
+
"bin": {
|
| 707 |
+
"node-which": "bin/which.js"
|
| 708 |
+
},
|
| 709 |
+
"engines": {
|
| 710 |
+
"node": "^20.17.0 || >=22.9.0"
|
| 711 |
+
}
|
| 712 |
+
},
|
| 713 |
+
"node_modules/wrappy": {
|
| 714 |
+
"version": "1.0.2",
|
| 715 |
+
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
| 716 |
+
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
| 717 |
+
"license": "ISC"
|
| 718 |
+
},
|
| 719 |
+
"node_modules/yallist": {
|
| 720 |
+
"version": "5.0.0",
|
| 721 |
+
"resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
|
| 722 |
+
"integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
|
| 723 |
+
"license": "BlueOak-1.0.0",
|
| 724 |
+
"engines": {
|
| 725 |
+
"node": ">=18"
|
| 726 |
+
}
|
| 727 |
+
}
|
| 728 |
+
}
|
| 729 |
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "etrog-yt-home-relay",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"private": true,
|
| 5 |
+
"description": "Home residential egress for YouTube InnerTube + googlevideo (pairs with etrog-bypass-proxy Worker)",
|
| 6 |
+
"type": "module",
|
| 7 |
+
"scripts": {
|
| 8 |
+
"start": "node server.js",
|
| 9 |
+
"dev": "node --watch server.js"
|
| 10 |
+
},
|
| 11 |
+
"engines": {
|
| 12 |
+
"node": ">=18"
|
| 13 |
+
},
|
| 14 |
+
"dependencies": {
|
| 15 |
+
"sqlite3": "^6.0.1"
|
| 16 |
+
}
|
| 17 |
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@echo off
|
| 2 |
+
rem RELAY_SECRET must come from the environment (set once via start-relay.ps1 or setx).
|
| 3 |
+
rem Never hardcode it here — this file is committed.
|
| 4 |
+
if "%RELAY_SECRET%"=="" (
|
| 5 |
+
echo ERROR: RELAY_SECRET is not set.
|
| 6 |
+
echo Set it once: setx RELAY_SECRET "your-long-random-secret"
|
| 7 |
+
echo Then open a NEW terminal and re-run this script.
|
| 8 |
+
exit /b 1
|
| 9 |
+
)
|
| 10 |
+
set PORT=8788
|
| 11 |
+
cd /d "%~dp0"
|
| 12 |
+
node server.js >> "%TEMP%\relay.log" 2>> "%TEMP%\relay-error.log"
|
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@echo off
|
| 2 |
+
"C:\Program Files (x86)\cloudflared\cloudflared.exe" tunnel --protocol http2 --url http://127.0.0.1:8788 --logfile "%TEMP%\cloudflared-relay.log"
|
|
@@ -0,0 +1,871 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Etrog YT Home Relay — residential egress for YouTube only
|
| 3 |
+
* ─────────────────────────────────────────────────────────
|
| 4 |
+
* Runs on your home PC (real residential IP). Cloudflare Worker calls this
|
| 5 |
+
* for InnerTube /player and googlevideo bytes so both share the SAME IP.
|
| 6 |
+
*
|
| 7 |
+
* Endpoints (all require Authorization: Bearer <RELAY_SECRET>):
|
| 8 |
+
* GET /health → { ok, ip hint }
|
| 9 |
+
* POST /v1/yt/player → { videoId } → formats + playability (from home IP)
|
| 10 |
+
* GET /v1/fetch?url=… → stream arbitrary allowed URL (googlevideo, etc.)
|
| 11 |
+
* POST /v1/fetch → { url, method?, headers?, body? } JSON or stream
|
| 12 |
+
*
|
| 13 |
+
* Quick start:
|
| 14 |
+
* set RELAY_SECRET=long-random-string
|
| 15 |
+
* npm start
|
| 16 |
+
* cloudflared tunnel --url http://127.0.0.1:8788
|
| 17 |
+
* # put the https URL + secret into Worker env (YT_RELAY_URL, YT_RELAY_SECRET)
|
| 18 |
+
*/
|
| 19 |
+
|
| 20 |
+
import http from 'node:http';
|
| 21 |
+
import { URL } from 'node:url';
|
| 22 |
+
import { randomUUID, createHash } from 'node:crypto';
|
| 23 |
+
import { Readable } from 'node:stream';
|
| 24 |
+
import fs from 'node:fs';
|
| 25 |
+
import path from 'node:path';
|
| 26 |
+
import sqlite3Pkg from 'sqlite3';
|
| 27 |
+
const sqlite3 = sqlite3Pkg.default || sqlite3Pkg;
|
| 28 |
+
|
| 29 |
+
// Always bind loopback — cloudflared/localtunnel connect here.
|
| 30 |
+
// Do NOT use 0.0.0.0 unless you intentionally expose the port on LAN.
|
| 31 |
+
const PORT = Number(process.env.PORT || 8788);
|
| 32 |
+
const HOST = process.env.HOST || '127.0.0.1';
|
| 33 |
+
const RELAY_SECRET = process.env.RELAY_SECRET || '';
|
| 34 |
+
const FETCH_TIMEOUT_MS = Number(process.env.FETCH_TIMEOUT_MS || 25000);
|
| 35 |
+
// Etrog/Rimon MITM: set RELAY_INSECURE_TLS=1 if Node fetch fails with cert errors
|
| 36 |
+
const INSECURE_TLS = process.env.RELAY_INSECURE_TLS === '1' || process.env.RELAY_INSECURE_TLS === 'true';
|
| 37 |
+
if (INSECURE_TLS) {
|
| 38 |
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
| 39 |
+
console.warn('[relay] RELAY_INSECURE_TLS=1 — TLS cert verification DISABLED (filter MITM mode)');
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
// Cookies that matter for Innertube auth (keep header small)
|
| 43 |
+
const AUTH_COOKIE_NAMES = new Set([
|
| 44 |
+
'LOGIN_INFO', 'SID', 'HSID', 'SSID', 'APISID', 'SAPISID',
|
| 45 |
+
'__Secure-1PSID', '__Secure-3PSID', '__Secure-1PAPISID', '__Secure-3PAPISID',
|
| 46 |
+
'__Secure-1PSIDTS', '__Secure-3PSIDTS', '__Secure-1PSIDCC', '__Secure-3PSIDCC',
|
| 47 |
+
'PREF', 'SOCS', 'VISITOR_INFO1_LIVE', 'YSC', 'CONSENT',
|
| 48 |
+
]);
|
| 49 |
+
|
| 50 |
+
// Only these host suffixes may be fetched through the relay (SSRF guard)
|
| 51 |
+
const ALLOWED_HOST_SUFFIXES = [
|
| 52 |
+
'youtube.com',
|
| 53 |
+
'youtubei.googleapis.com',
|
| 54 |
+
'googleapis.com',
|
| 55 |
+
'googlevideo.com',
|
| 56 |
+
'gvt1.com',
|
| 57 |
+
'gvt2.com',
|
| 58 |
+
'ytimg.com',
|
| 59 |
+
'ggpht.com',
|
| 60 |
+
'googleusercontent.com',
|
| 61 |
+
];
|
| 62 |
+
|
| 63 |
+
const INNERTUBE_KEY_ANDROID = 'AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39w';
|
| 64 |
+
const INNERTUBE_KEY_WEB = 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8';
|
| 65 |
+
|
| 66 |
+
// ── helpers ──────────────────────────────────────────────────────────────
|
| 67 |
+
|
| 68 |
+
function log(...args) {
|
| 69 |
+
const ts = new Date().toISOString().slice(11, 19);
|
| 70 |
+
console.log(`[${ts}]`, ...args);
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
function json(res, status, obj) {
|
| 74 |
+
const body = JSON.stringify(obj);
|
| 75 |
+
res.writeHead(status, {
|
| 76 |
+
'content-type': 'application/json; charset=utf-8',
|
| 77 |
+
'cache-control': 'no-store',
|
| 78 |
+
'access-control-allow-origin': '*',
|
| 79 |
+
});
|
| 80 |
+
res.end(body);
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
function unauthorized(res) {
|
| 84 |
+
json(res, 401, { error: 'unauthorized' });
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
function badRequest(res, msg) {
|
| 88 |
+
json(res, 400, { error: msg || 'bad request' });
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
function readBody(req, limit = 2 * 1024 * 1024) {
|
| 92 |
+
return new Promise((resolve, reject) => {
|
| 93 |
+
const chunks = [];
|
| 94 |
+
let size = 0;
|
| 95 |
+
req.on('data', (c) => {
|
| 96 |
+
size += c.length;
|
| 97 |
+
if (size > limit) {
|
| 98 |
+
reject(new Error('body too large'));
|
| 99 |
+
req.destroy();
|
| 100 |
+
return;
|
| 101 |
+
}
|
| 102 |
+
chunks.push(c);
|
| 103 |
+
});
|
| 104 |
+
req.on('end', () => resolve(Buffer.concat(chunks)));
|
| 105 |
+
req.on('error', reject);
|
| 106 |
+
});
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
function checkAuth(req) {
|
| 110 |
+
if (!RELAY_SECRET) return false;
|
| 111 |
+
const h = req.headers.authorization || '';
|
| 112 |
+
if (h === `Bearer ${RELAY_SECRET}`) return true;
|
| 113 |
+
// Also accept header used by some gateways
|
| 114 |
+
if (req.headers['x-relay-secret'] === RELAY_SECRET) return true;
|
| 115 |
+
return false;
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
function hostAllowed(hostname) {
|
| 119 |
+
const h = String(hostname || '').toLowerCase().replace(/\.$/, '');
|
| 120 |
+
if (!h) return false;
|
| 121 |
+
return ALLOWED_HOST_SUFFIXES.some(
|
| 122 |
+
(s) => h === s || h.endsWith('.' + s)
|
| 123 |
+
);
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
function assertAllowedUrl(urlStr) {
|
| 127 |
+
let u;
|
| 128 |
+
try {
|
| 129 |
+
u = new URL(urlStr);
|
| 130 |
+
} catch {
|
| 131 |
+
throw new Error('invalid url');
|
| 132 |
+
}
|
| 133 |
+
if (u.protocol !== 'https:' && u.protocol !== 'http:') {
|
| 134 |
+
throw new Error('only http(s)');
|
| 135 |
+
}
|
| 136 |
+
if (!hostAllowed(u.hostname)) {
|
| 137 |
+
throw new Error('host not allowed: ' + u.hostname);
|
| 138 |
+
}
|
| 139 |
+
return u;
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
/** WEB Innertube expects SAPISIDHASH authorization when cookies are present */
|
| 143 |
+
function computeSapisidHash(cookieStr) {
|
| 144 |
+
try {
|
| 145 |
+
const m = String(cookieStr || '').match(
|
| 146 |
+
/(?:^|;\s*)(?:__Secure-3PAPISID|__Secure-1PAPISID|SAPISID)=([^;]+)/
|
| 147 |
+
);
|
| 148 |
+
if (!m) return null;
|
| 149 |
+
const ts = Math.floor(Date.now() / 1000).toString();
|
| 150 |
+
const origin = 'https://www.youtube.com';
|
| 151 |
+
const hash = createHash('sha1').update(`${ts} ${m[1]} ${origin}`).digest('hex');
|
| 152 |
+
return `SAPISIDHASH ${ts}_${hash}`;
|
| 153 |
+
} catch {
|
| 154 |
+
return null;
|
| 155 |
+
}
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
function slimCookieHeader(cookieStr) {
|
| 159 |
+
if (!cookieStr) return '';
|
| 160 |
+
const map = new Map();
|
| 161 |
+
for (const part of String(cookieStr).split(';')) {
|
| 162 |
+
const eq = part.indexOf('=');
|
| 163 |
+
if (eq === -1) continue;
|
| 164 |
+
const name = part.slice(0, eq).trim();
|
| 165 |
+
const val = part.slice(eq + 1).trim();
|
| 166 |
+
if (!name || !AUTH_COOKIE_NAMES.has(name)) continue;
|
| 167 |
+
// Later hosts overwrite earlier — prefer youtube-specific values already in string order
|
| 168 |
+
map.set(name, val);
|
| 169 |
+
}
|
| 170 |
+
return [...map.entries()].map(([k, v]) => `${k}=${v}`).join('; ');
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
function collectFormats(playerJson) {
|
| 174 |
+
const sd = (playerJson && playerJson.streamingData) || {};
|
| 175 |
+
const raw = [].concat(sd.formats || [], sd.adaptiveFormats || []);
|
| 176 |
+
const out = [];
|
| 177 |
+
for (const f of raw) {
|
| 178 |
+
if (!f) continue;
|
| 179 |
+
// Prefer cipher-free URLs; skip signatureCipher-only (needs nsig on client)
|
| 180 |
+
const url = f.url || null;
|
| 181 |
+
if (!url) continue;
|
| 182 |
+
const mt = String(f.mimeType || '');
|
| 183 |
+
const hasA = !!(
|
| 184 |
+
f.audioQuality ||
|
| 185 |
+
f.audioSampleRate ||
|
| 186 |
+
mt.includes('audio') ||
|
| 187 |
+
f.audioChannels != null
|
| 188 |
+
);
|
| 189 |
+
const hasV = !!(f.width || f.height || mt.includes('video'));
|
| 190 |
+
if (mt.includes('mp4') && f.audioChannels != null) {
|
| 191 |
+
// progressive mp4 often has both
|
| 192 |
+
}
|
| 193 |
+
out.push({
|
| 194 |
+
itag: f.itag,
|
| 195 |
+
url,
|
| 196 |
+
mimeType: mt,
|
| 197 |
+
bitrate: f.bitrate || 0,
|
| 198 |
+
width: f.width || 0,
|
| 199 |
+
height: f.height || 0,
|
| 200 |
+
contentLength: f.contentLength || f.content_length || null,
|
| 201 |
+
hasAudio: hasA,
|
| 202 |
+
hasVideo: hasV,
|
| 203 |
+
progressive: !!(hasA && hasV),
|
| 204 |
+
});
|
| 205 |
+
}
|
| 206 |
+
return out;
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
async function tryPlayerClient(videoId, cl, workerUrl = null) {
|
| 210 |
+
if (workerUrl) {
|
| 211 |
+
const ac = new AbortController();
|
| 212 |
+
const t = setTimeout(() => ac.abort(), cl.timeoutMs || 12000);
|
| 213 |
+
try {
|
| 214 |
+
const helperEndpoint = workerUrl.replace(/\/+$/, '') + '/api/resolve-helper';
|
| 215 |
+
const helperRes = await fetch(helperEndpoint, {
|
| 216 |
+
method: 'POST',
|
| 217 |
+
headers: {
|
| 218 |
+
'content-type': 'application/json',
|
| 219 |
+
authorization: 'Bearer ' + RELAY_SECRET,
|
| 220 |
+
},
|
| 221 |
+
body: JSON.stringify({
|
| 222 |
+
videoId,
|
| 223 |
+
client: cl,
|
| 224 |
+
cookie: cl.cookie || '',
|
| 225 |
+
}),
|
| 226 |
+
signal: ac.signal,
|
| 227 |
+
});
|
| 228 |
+
clearTimeout(t);
|
| 229 |
+
if (helperRes.ok) {
|
| 230 |
+
const j = await helperRes.json();
|
| 231 |
+
if (j && j.source) {
|
| 232 |
+
j.source = j.source + '+cloud';
|
| 233 |
+
return j;
|
| 234 |
+
}
|
| 235 |
+
}
|
| 236 |
+
} catch (_) {
|
| 237 |
+
clearTimeout(t);
|
| 238 |
+
}
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
const endpoint =
|
| 242 |
+
(cl.host || 'https://www.youtube.com') +
|
| 243 |
+
'/youtubei/v1/player?prettyPrint=false&key=' +
|
| 244 |
+
encodeURIComponent(cl.apiKey);
|
| 245 |
+
|
| 246 |
+
const client = {
|
| 247 |
+
clientName: cl.clientName,
|
| 248 |
+
clientVersion: cl.clientVersion,
|
| 249 |
+
hl: cl.hl || 'en',
|
| 250 |
+
gl: cl.gl || 'US',
|
| 251 |
+
userAgent: cl.ua,
|
| 252 |
+
...(cl.extra || {}),
|
| 253 |
+
};
|
| 254 |
+
|
| 255 |
+
const body = {
|
| 256 |
+
context: { client, user: { lockedSafetyMode: false } },
|
| 257 |
+
videoId,
|
| 258 |
+
contentCheckOk: true,
|
| 259 |
+
racyCheckOk: true,
|
| 260 |
+
};
|
| 261 |
+
|
| 262 |
+
if (cl.playbackContext) {
|
| 263 |
+
body.playbackContext = cl.playbackContext;
|
| 264 |
+
}
|
| 265 |
+
|
| 266 |
+
const isWebFamily = /WEB|TVHTML5|MWEB/i.test(cl.clientName);
|
| 267 |
+
const headers = {
|
| 268 |
+
'content-type': 'application/json',
|
| 269 |
+
'user-agent': cl.ua,
|
| 270 |
+
'x-youtube-client-name': String(cl.clientNameHeader || ''),
|
| 271 |
+
'x-youtube-client-version': cl.clientVersion,
|
| 272 |
+
origin: 'https://www.youtube.com',
|
| 273 |
+
referer: cl.embedded
|
| 274 |
+
? 'https://www.youtube.com/embed/' + videoId
|
| 275 |
+
: 'https://www.youtube.com/watch?v=' + videoId,
|
| 276 |
+
'accept-language': 'he-IL,he;q=0.9,en-US;q=0.8,en;q=0.7',
|
| 277 |
+
'x-goog-api-key': cl.apiKey,
|
| 278 |
+
};
|
| 279 |
+
if (cl.cookie) {
|
| 280 |
+
headers.cookie = cl.cookie;
|
| 281 |
+
// WEB-family: SAPISIDHASH proves cookie ownership (fixes many LOGIN_REQUIRED / bot checks)
|
| 282 |
+
if (isWebFamily || cl.forceAuth) {
|
| 283 |
+
const auth = computeSapisidHash(cl.cookie);
|
| 284 |
+
if (auth) {
|
| 285 |
+
headers.authorization = auth;
|
| 286 |
+
headers['x-origin'] = 'https://www.youtube.com';
|
| 287 |
+
headers['x-goog-authuser'] = '0';
|
| 288 |
+
}
|
| 289 |
+
}
|
| 290 |
+
}
|
| 291 |
+
|
| 292 |
+
const ac = new AbortController();
|
| 293 |
+
const t = setTimeout(() => ac.abort(), cl.timeoutMs || 12000);
|
| 294 |
+
try {
|
| 295 |
+
const r = await fetch(endpoint, {
|
| 296 |
+
method: 'POST',
|
| 297 |
+
headers,
|
| 298 |
+
body: JSON.stringify(body),
|
| 299 |
+
signal: ac.signal,
|
| 300 |
+
});
|
| 301 |
+
const j = await r.json().catch(() => null);
|
| 302 |
+
const formats = collectFormats(j);
|
| 303 |
+
const playability = (j && j.playabilityStatus) || {};
|
| 304 |
+
const status = playability.status || null;
|
| 305 |
+
const reason = playability.reason || playability.messages?.[0] || null;
|
| 306 |
+
const errScreen = playability.errorScreen || null;
|
| 307 |
+
let errorCode = null;
|
| 308 |
+
try {
|
| 309 |
+
errorCode =
|
| 310 |
+
errScreen?.playerErrorMessageRenderer?.errorMessage?.runs?.[0]?.text ||
|
| 311 |
+
errScreen?.playerErrorMessageRenderer?.reason?.simpleText ||
|
| 312 |
+
playability.reason ||
|
| 313 |
+
null;
|
| 314 |
+
} catch (_) {}
|
| 315 |
+
return {
|
| 316 |
+
ok: formats.length > 0 && status !== 'LOGIN_REQUIRED' && status !== 'UNPLAYABLE',
|
| 317 |
+
source: cl.clientName + (cl.cookie ? '+ck' : ''),
|
| 318 |
+
http: r.status,
|
| 319 |
+
playability: {
|
| 320 |
+
status,
|
| 321 |
+
reason,
|
| 322 |
+
errorCode: errorCode || null,
|
| 323 |
+
},
|
| 324 |
+
formats,
|
| 325 |
+
rawStatus: status,
|
| 326 |
+
};
|
| 327 |
+
} catch (e) {
|
| 328 |
+
return {
|
| 329 |
+
ok: false,
|
| 330 |
+
source: cl.clientName + (cl.cookie ? '+ck' : ''),
|
| 331 |
+
error: String(e && e.message ? e.message : e),
|
| 332 |
+
formats: [],
|
| 333 |
+
};
|
| 334 |
+
} finally {
|
| 335 |
+
clearTimeout(t);
|
| 336 |
+
}
|
| 337 |
+
}
|
| 338 |
+
|
| 339 |
+
async function resolvePlayer(videoId, cookieHeader, workerUrl = null) {
|
| 340 |
+
// The freshly auto-loaded Firefox session is the whole point of this relay, so it
|
| 341 |
+
// must win per-cookie over whatever the Worker forwarded — the Worker may send a
|
| 342 |
+
// stale/hardcoded builtin cookie, and slimCookieHeader keeps the LAST value per name,
|
| 343 |
+
// so Firefox goes last. Falls back to the forwarded cookie for any name Firefox lacks.
|
| 344 |
+
const merged = [cookieHeader || '', firefoxCookiesStr || ''].filter(Boolean).join('; ');
|
| 345 |
+
const ck = slimCookieHeader(merged);
|
| 346 |
+
const hasAuth = /SAPISID|__Secure-3PAPISID|LOGIN_INFO|__Secure-3PSID/i.test(ck);
|
| 347 |
+
|
| 348 |
+
// Order matters: authenticated WEB first for Restricted Mode / age / bot checks.
|
| 349 |
+
// ANDROID without cookies often hits MRM / LOGIN_REQUIRED on filtered ISPs.
|
| 350 |
+
const clients = [];
|
| 351 |
+
|
| 352 |
+
if (hasAuth) {
|
| 353 |
+
clients.push({
|
| 354 |
+
clientName: 'WEB',
|
| 355 |
+
clientVersion: '2.20250620.00.00',
|
| 356 |
+
clientNameHeader: '1',
|
| 357 |
+
ua: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
| 358 |
+
apiKey: INNERTUBE_KEY_WEB,
|
| 359 |
+
host: 'https://www.youtube.com',
|
| 360 |
+
cookie: ck,
|
| 361 |
+
forceAuth: true,
|
| 362 |
+
playbackContext: {
|
| 363 |
+
contentPlaybackContext: {
|
| 364 |
+
html5Preference: 'HTML5_PREF_WANTS',
|
| 365 |
+
signatureTimestamp: 20468,
|
| 366 |
+
},
|
| 367 |
+
},
|
| 368 |
+
timeoutMs: 14000,
|
| 369 |
+
});
|
| 370 |
+
clients.push({
|
| 371 |
+
clientName: 'MWEB',
|
| 372 |
+
clientVersion: '2.20250620.00.00',
|
| 373 |
+
clientNameHeader: '2',
|
| 374 |
+
ua: 'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36',
|
| 375 |
+
apiKey: INNERTUBE_KEY_WEB,
|
| 376 |
+
host: 'https://www.youtube.com',
|
| 377 |
+
cookie: ck,
|
| 378 |
+
forceAuth: true,
|
| 379 |
+
playbackContext: {
|
| 380 |
+
contentPlaybackContext: {
|
| 381 |
+
html5Preference: 'HTML5_PREF_WANTS',
|
| 382 |
+
signatureTimestamp: 20468,
|
| 383 |
+
},
|
| 384 |
+
},
|
| 385 |
+
timeoutMs: 12000,
|
| 386 |
+
});
|
| 387 |
+
// Mobile clients WITH account cookies (helps age-gate; not always)
|
| 388 |
+
clients.push({
|
| 389 |
+
clientName: 'ANDROID',
|
| 390 |
+
clientVersion: '20.50.37',
|
| 391 |
+
clientNameHeader: '3',
|
| 392 |
+
ua: 'com.google.android.youtube/20.50.37 (Linux; U; Android 14) gzip',
|
| 393 |
+
apiKey: INNERTUBE_KEY_ANDROID,
|
| 394 |
+
host: 'https://youtubei.googleapis.com',
|
| 395 |
+
cookie: ck,
|
| 396 |
+
forceAuth: true,
|
| 397 |
+
extra: { androidSdkVersion: 34, osName: 'Android', osVersion: '14' },
|
| 398 |
+
timeoutMs: 12000,
|
| 399 |
+
});
|
| 400 |
+
}
|
| 401 |
+
|
| 402 |
+
// Embedded TV player — sometimes returns progressive formats without login
|
| 403 |
+
clients.push({
|
| 404 |
+
clientName: 'TVHTML5_SIMPLY_EMBEDDED_PLAYER',
|
| 405 |
+
clientVersion: '2.0',
|
| 406 |
+
clientNameHeader: '85',
|
| 407 |
+
ua: 'Mozilla/5.0 (ChromiumStylePlatform) Cobalt/Version',
|
| 408 |
+
apiKey: INNERTUBE_KEY_WEB,
|
| 409 |
+
host: 'https://www.youtube.com',
|
| 410 |
+
embedded: true,
|
| 411 |
+
cookie: hasAuth ? ck : undefined,
|
| 412 |
+
playbackContext: {
|
| 413 |
+
contentPlaybackContext: {
|
| 414 |
+
html5Preference: 'HTML5_PREF_WANTS',
|
| 415 |
+
signatureTimestamp: 20468,
|
| 416 |
+
},
|
| 417 |
+
},
|
| 418 |
+
timeoutMs: 12000,
|
| 419 |
+
});
|
| 420 |
+
|
| 421 |
+
// Anonymous ANDROID / IOS fallback (works for many non-restricted videos)
|
| 422 |
+
clients.push(
|
| 423 |
+
{
|
| 424 |
+
clientName: 'ANDROID',
|
| 425 |
+
clientVersion: '20.50.37',
|
| 426 |
+
clientNameHeader: '3',
|
| 427 |
+
ua: 'com.google.android.youtube/20.50.37 (Linux; U; Android 14) gzip',
|
| 428 |
+
apiKey: INNERTUBE_KEY_ANDROID,
|
| 429 |
+
host: 'https://youtubei.googleapis.com',
|
| 430 |
+
extra: { androidSdkVersion: 34, osName: 'Android', osVersion: '14' },
|
| 431 |
+
timeoutMs: 12000,
|
| 432 |
+
},
|
| 433 |
+
{
|
| 434 |
+
clientName: 'IOS',
|
| 435 |
+
clientVersion: '20.12.4',
|
| 436 |
+
clientNameHeader: '5',
|
| 437 |
+
ua: 'com.google.ios.youtube/20.12.4 (iPhone16,2; U; CPU iOS 18_0 like Mac OS X;)',
|
| 438 |
+
apiKey: 'AIzaSyB-63vPrdThhKuerbB2N_l7Kwwcxj6yUAc',
|
| 439 |
+
host: 'https://www.youtube.com',
|
| 440 |
+
extra: {
|
| 441 |
+
deviceModel: 'iPhone16,2',
|
| 442 |
+
osName: 'iPhone',
|
| 443 |
+
osVersion: '18.0',
|
| 444 |
+
},
|
| 445 |
+
timeoutMs: 12000,
|
| 446 |
+
}
|
| 447 |
+
);
|
| 448 |
+
|
| 449 |
+
if (!hasAuth) {
|
| 450 |
+
clients.push({
|
| 451 |
+
clientName: 'WEB',
|
| 452 |
+
clientVersion: '2.20250620.00.00',
|
| 453 |
+
clientNameHeader: '1',
|
| 454 |
+
ua: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
| 455 |
+
apiKey: INNERTUBE_KEY_WEB,
|
| 456 |
+
host: 'https://www.youtube.com',
|
| 457 |
+
playbackContext: {
|
| 458 |
+
contentPlaybackContext: {
|
| 459 |
+
html5Preference: 'HTML5_PREF_WANTS',
|
| 460 |
+
signatureTimestamp: 20468,
|
| 461 |
+
},
|
| 462 |
+
},
|
| 463 |
+
timeoutMs: 12000,
|
| 464 |
+
});
|
| 465 |
+
}
|
| 466 |
+
|
| 467 |
+
const attempts = [];
|
| 468 |
+
// Sequential for auth clients first would be slower; race all, pick best
|
| 469 |
+
const results = await Promise.all(clients.map((c) => tryPlayerClient(videoId, c, workerUrl)));
|
| 470 |
+
|
| 471 |
+
// Prefer any ok with formats; among failures surface LOGIN_REQUIRED / MRM clearly
|
| 472 |
+
let bestFail = null;
|
| 473 |
+
for (const r of results) {
|
| 474 |
+
attempts.push({
|
| 475 |
+
source: r.source,
|
| 476 |
+
ok: r.ok,
|
| 477 |
+
http: r.http,
|
| 478 |
+
status: r.playability?.status,
|
| 479 |
+
reason: r.playability?.reason || r.playability?.errorCode,
|
| 480 |
+
n: (r.formats || []).length,
|
| 481 |
+
error: r.error,
|
| 482 |
+
});
|
| 483 |
+
if (r.ok && r.formats.length) {
|
| 484 |
+
return {
|
| 485 |
+
ok: true,
|
| 486 |
+
videoId,
|
| 487 |
+
client: r.source,
|
| 488 |
+
playability: r.playability,
|
| 489 |
+
formats: r.formats,
|
| 490 |
+
attempts,
|
| 491 |
+
hasAuthCookies: hasAuth,
|
| 492 |
+
egress: 'home-relay',
|
| 493 |
+
};
|
| 494 |
+
}
|
| 495 |
+
if (
|
| 496 |
+
r.playability?.status === 'LOGIN_REQUIRED' ||
|
| 497 |
+
/MRM|bot|restricted|login/i.test(String(r.playability?.reason || r.playability?.errorCode || ''))
|
| 498 |
+
) {
|
| 499 |
+
bestFail = r;
|
| 500 |
+
}
|
| 501 |
+
}
|
| 502 |
+
|
| 503 |
+
const last = bestFail || results[results.length - 1] || {};
|
| 504 |
+
return {
|
| 505 |
+
ok: false,
|
| 506 |
+
videoId,
|
| 507 |
+
playability: last.playability || { status: 'ERROR' },
|
| 508 |
+
formats: [],
|
| 509 |
+
attempts,
|
| 510 |
+
hasAuthCookies: hasAuth,
|
| 511 |
+
hint: hasAuth
|
| 512 |
+
? 'Auth cookies present but YouTube still blocked — try re-login in Firefox, disable Restricted Mode on Google account, or use mobile hotspot without Etrog for test.'
|
| 513 |
+
: 'No SAPISID/LOGIN_INFO from Firefox — open YouTube in Firefox while logged in, then restart relay.',
|
| 514 |
+
egress: 'home-relay',
|
| 515 |
+
};
|
| 516 |
+
}
|
| 517 |
+
|
| 518 |
+
async function proxyFetch(urlStr, opts = {}) {
|
| 519 |
+
const u = assertAllowedUrl(urlStr);
|
| 520 |
+
const method = (opts.method || 'GET').toUpperCase();
|
| 521 |
+
const headers = { ...(opts.headers || {}) };
|
| 522 |
+
// Strip hop-by-hop / dangerous
|
| 523 |
+
for (const k of [
|
| 524 |
+
'host',
|
| 525 |
+
'connection',
|
| 526 |
+
'content-length',
|
| 527 |
+
'transfer-encoding',
|
| 528 |
+
'authorization', // never forward Worker→relay auth upstream
|
| 529 |
+
]) {
|
| 530 |
+
delete headers[k];
|
| 531 |
+
delete headers[k.toLowerCase()];
|
| 532 |
+
}
|
| 533 |
+
|
| 534 |
+
const ac = new AbortController();
|
| 535 |
+
const t = setTimeout(() => ac.abort(), opts.timeoutMs || FETCH_TIMEOUT_MS);
|
| 536 |
+
try {
|
| 537 |
+
const init = {
|
| 538 |
+
method,
|
| 539 |
+
headers,
|
| 540 |
+
redirect: 'follow',
|
| 541 |
+
signal: ac.signal,
|
| 542 |
+
};
|
| 543 |
+
if (opts.body != null && method !== 'GET' && method !== 'HEAD') {
|
| 544 |
+
init.body = opts.body;
|
| 545 |
+
}
|
| 546 |
+
const upstream = await fetch(u.toString(), init);
|
| 547 |
+
return upstream;
|
| 548 |
+
} finally {
|
| 549 |
+
clearTimeout(t);
|
| 550 |
+
}
|
| 551 |
+
}
|
| 552 |
+
|
| 553 |
+
function pipeWebToNode(webRes, nodeRes, extraHeaders = {}) {
|
| 554 |
+
const headers = { ...extraHeaders };
|
| 555 |
+
// Forward useful upstream headers
|
| 556 |
+
const pass = [
|
| 557 |
+
'content-type',
|
| 558 |
+
'content-length',
|
| 559 |
+
'content-range',
|
| 560 |
+
'accept-ranges',
|
| 561 |
+
'cache-control',
|
| 562 |
+
'etag',
|
| 563 |
+
'last-modified',
|
| 564 |
+
];
|
| 565 |
+
for (const name of pass) {
|
| 566 |
+
const v = webRes.headers.get(name);
|
| 567 |
+
if (v) headers[name] = v;
|
| 568 |
+
}
|
| 569 |
+
headers['x-relay'] = 'home';
|
| 570 |
+
headers['access-control-allow-origin'] = '*';
|
| 571 |
+
headers['access-control-expose-headers'] =
|
| 572 |
+
'Content-Length, Content-Range, Accept-Ranges, Content-Type, X-Relay';
|
| 573 |
+
|
| 574 |
+
nodeRes.writeHead(webRes.status, headers);
|
| 575 |
+
|
| 576 |
+
if (!webRes.body) {
|
| 577 |
+
nodeRes.end();
|
| 578 |
+
return Promise.resolve();
|
| 579 |
+
}
|
| 580 |
+
|
| 581 |
+
const readable = Readable.fromWeb(webRes.body);
|
| 582 |
+
readable.pipe(nodeRes);
|
| 583 |
+
|
| 584 |
+
return new Promise((resolve) => {
|
| 585 |
+
readable.on('end', resolve);
|
| 586 |
+
readable.on('error', (err) => {
|
| 587 |
+
log('pipe error', err.message);
|
| 588 |
+
try { nodeRes.destroy(err); } catch (_) {}
|
| 589 |
+
resolve();
|
| 590 |
+
});
|
| 591 |
+
});
|
| 592 |
+
}
|
| 593 |
+
|
| 594 |
+
// ── server ───────────────────────────────────────────────────────────────
|
| 595 |
+
|
| 596 |
+
const server = http.createServer(async (req, res) => {
|
| 597 |
+
const reqId = randomUUID().slice(0, 8);
|
| 598 |
+
const url = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`);
|
| 599 |
+
|
| 600 |
+
// CORS preflight (Worker browser tooling / curl)
|
| 601 |
+
if (req.method === 'OPTIONS') {
|
| 602 |
+
res.writeHead(204, {
|
| 603 |
+
'access-control-allow-origin': '*',
|
| 604 |
+
'access-control-allow-methods': 'GET,POST,OPTIONS',
|
| 605 |
+
'access-control-allow-headers': 'Authorization, Content-Type, X-Relay-Secret, Range',
|
| 606 |
+
'access-control-max-age': '86400',
|
| 607 |
+
});
|
| 608 |
+
res.end();
|
| 609 |
+
return;
|
| 610 |
+
}
|
| 611 |
+
|
| 612 |
+
// Health must stay synchronous and tiny — used by start scripts + tunnel probes
|
| 613 |
+
if (url.pathname === '/health' || url.pathname === '/') {
|
| 614 |
+
json(res, 200, {
|
| 615 |
+
ok: true,
|
| 616 |
+
service: 'etrog-yt-home-relay',
|
| 617 |
+
secretConfigured: !!RELAY_SECRET,
|
| 618 |
+
host: HOST,
|
| 619 |
+
port: PORT,
|
| 620 |
+
cookies: firefoxCookiesStr ? 'loaded' : 'none',
|
| 621 |
+
time: new Date().toISOString(),
|
| 622 |
+
});
|
| 623 |
+
return;
|
| 624 |
+
}
|
| 625 |
+
|
| 626 |
+
if (!checkAuth(req)) {
|
| 627 |
+
log(reqId, '401', req.method, url.pathname);
|
| 628 |
+
unauthorized(res);
|
| 629 |
+
return;
|
| 630 |
+
}
|
| 631 |
+
|
| 632 |
+
try {
|
| 633 |
+
// ── POST /v1/yt/player ──
|
| 634 |
+
if (req.method === 'POST' && url.pathname === '/v1/yt/player') {
|
| 635 |
+
const raw = await readBody(req);
|
| 636 |
+
let body = {};
|
| 637 |
+
try {
|
| 638 |
+
body = JSON.parse(raw.toString('utf8') || '{}');
|
| 639 |
+
} catch {
|
| 640 |
+
return badRequest(res, 'invalid json');
|
| 641 |
+
}
|
| 642 |
+
const videoId = String(body.videoId || body.v || '').trim();
|
| 643 |
+
if (!/^[a-zA-Z0-9_-]{6,20}$/.test(videoId)) {
|
| 644 |
+
return badRequest(res, 'invalid videoId');
|
| 645 |
+
}
|
| 646 |
+
log(reqId, 'player', videoId);
|
| 647 |
+
const workerUrl = req.headers['x-worker-url'] || null;
|
| 648 |
+
const pack = await resolvePlayer(videoId, body.cookie || firefoxCookiesStr || '', workerUrl);
|
| 649 |
+
log(
|
| 650 |
+
reqId,
|
| 651 |
+
'player done',
|
| 652 |
+
videoId,
|
| 653 |
+
pack.ok ? `ok n=${pack.formats.length} via ${pack.client}` : 'fail',
|
| 654 |
+
pack.playability?.status || ''
|
| 655 |
+
);
|
| 656 |
+
return json(res, pack.ok ? 200 : 502, pack);
|
| 657 |
+
}
|
| 658 |
+
|
| 659 |
+
// ── GET /v1/fetch?url= ──
|
| 660 |
+
if (req.method === 'GET' && url.pathname === '/v1/fetch') {
|
| 661 |
+
const target = url.searchParams.get('url');
|
| 662 |
+
if (!target) return badRequest(res, 'missing url');
|
| 663 |
+
const range = req.headers.range;
|
| 664 |
+
const headers = {
|
| 665 |
+
'user-agent':
|
| 666 |
+
req.headers['x-upstream-ua'] ||
|
| 667 |
+
'com.google.android.youtube/20.50.37 (Linux; U; Android 14) gzip',
|
| 668 |
+
accept: '*/*',
|
| 669 |
+
origin: 'https://www.youtube.com',
|
| 670 |
+
referer: 'https://www.youtube.com/',
|
| 671 |
+
};
|
| 672 |
+
if (range) headers.range = range;
|
| 673 |
+
log(reqId, 'fetch GET', target.slice(0, 80));
|
| 674 |
+
try {
|
| 675 |
+
const upstream = await proxyFetch(target, { method: 'GET', headers });
|
| 676 |
+
log(reqId, 'upstream status:', upstream.status);
|
| 677 |
+
await pipeWebToNode(upstream, res);
|
| 678 |
+
log(reqId, 'pipe complete');
|
| 679 |
+
} catch (err) {
|
| 680 |
+
log(reqId, 'fetch error:', err.message || err);
|
| 681 |
+
throw err;
|
| 682 |
+
}
|
| 683 |
+
return;
|
| 684 |
+
}
|
| 685 |
+
|
| 686 |
+
// ── POST /v1/fetch ──
|
| 687 |
+
if (req.method === 'POST' && url.pathname === '/v1/fetch') {
|
| 688 |
+
const raw = await readBody(req, 8 * 1024 * 1024);
|
| 689 |
+
let body = {};
|
| 690 |
+
try {
|
| 691 |
+
body = JSON.parse(raw.toString('utf8') || '{}');
|
| 692 |
+
} catch {
|
| 693 |
+
return badRequest(res, 'invalid json');
|
| 694 |
+
}
|
| 695 |
+
if (!body.url) return badRequest(res, 'missing url');
|
| 696 |
+
const method = (body.method || 'GET').toUpperCase();
|
| 697 |
+
const headers = body.headers || {};
|
| 698 |
+
let upstreamBody = body.body;
|
| 699 |
+
if (body.bodyBase64) {
|
| 700 |
+
upstreamBody = Buffer.from(body.bodyBase64, 'base64');
|
| 701 |
+
}
|
| 702 |
+
// Response mode: stream (default for media) or json envelope
|
| 703 |
+
const mode = body.mode || 'stream';
|
| 704 |
+
log(reqId, 'fetch POST', method, String(body.url).slice(0, 80));
|
| 705 |
+
const upstream = await proxyFetch(body.url, {
|
| 706 |
+
method,
|
| 707 |
+
headers,
|
| 708 |
+
body: upstreamBody,
|
| 709 |
+
});
|
| 710 |
+
|
| 711 |
+
if (mode === 'json') {
|
| 712 |
+
const buf = Buffer.from(await upstream.arrayBuffer());
|
| 713 |
+
const ct = upstream.headers.get('content-type') || '';
|
| 714 |
+
let parsed = null;
|
| 715 |
+
if (ct.includes('json') || buf[0] === 0x7b) {
|
| 716 |
+
try {
|
| 717 |
+
parsed = JSON.parse(buf.toString('utf8'));
|
| 718 |
+
} catch {
|
| 719 |
+
/* keep null */
|
| 720 |
+
}
|
| 721 |
+
}
|
| 722 |
+
return json(res, 200, {
|
| 723 |
+
ok: upstream.ok,
|
| 724 |
+
status: upstream.status,
|
| 725 |
+
headers: Object.fromEntries(upstream.headers.entries()),
|
| 726 |
+
bodyBase64: parsed ? undefined : buf.toString('base64'),
|
| 727 |
+
json: parsed,
|
| 728 |
+
});
|
| 729 |
+
}
|
| 730 |
+
return pipeWebToNode(upstream, res);
|
| 731 |
+
}
|
| 732 |
+
|
| 733 |
+
json(res, 404, { error: 'not found', path: url.pathname });
|
| 734 |
+
} catch (e) {
|
| 735 |
+
log(reqId, 'error', e.message || e);
|
| 736 |
+
json(res, 502, { error: String(e && e.message ? e.message : e) });
|
| 737 |
+
}
|
| 738 |
+
});
|
| 739 |
+
|
| 740 |
+
if (!RELAY_SECRET) {
|
| 741 |
+
console.error('');
|
| 742 |
+
console.error('ERROR: set RELAY_SECRET to a long random string before starting.');
|
| 743 |
+
console.error(' Windows: set RELAY_SECRET=your-secret-here');
|
| 744 |
+
console.error(' PowerShell: $env:RELAY_SECRET="your-secret-here"');
|
| 745 |
+
console.error(' Linux/mac: export RELAY_SECRET=your-secret-here');
|
| 746 |
+
console.error('');
|
| 747 |
+
process.exit(1);
|
| 748 |
+
}
|
| 749 |
+
|
| 750 |
+
let firefoxCookiesStr = '';
|
| 751 |
+
|
| 752 |
+
function extractFirefoxCookies() {
|
| 753 |
+
return new Promise((resolve) => {
|
| 754 |
+
const appData = process.env.APPDATA;
|
| 755 |
+
if (!appData) {
|
| 756 |
+
log('[Firefox Cookies] APPDATA env var not found.');
|
| 757 |
+
return resolve();
|
| 758 |
+
}
|
| 759 |
+
|
| 760 |
+
const profilesDir = path.join(appData, 'Mozilla', 'Firefox', 'Profiles');
|
| 761 |
+
if (!fs.existsSync(profilesDir)) {
|
| 762 |
+
log('[Firefox Cookies] Firefox profiles directory not found.');
|
| 763 |
+
return resolve();
|
| 764 |
+
}
|
| 765 |
+
|
| 766 |
+
try {
|
| 767 |
+
const profiles = fs.readdirSync(profilesDir);
|
| 768 |
+
let cookiesPath = null;
|
| 769 |
+
|
| 770 |
+
for (const profile of profiles) {
|
| 771 |
+
const p = path.join(profilesDir, profile, 'cookies.sqlite');
|
| 772 |
+
if (fs.existsSync(p)) {
|
| 773 |
+
cookiesPath = p;
|
| 774 |
+
if (profile.includes('default-release')) break;
|
| 775 |
+
}
|
| 776 |
+
}
|
| 777 |
+
|
| 778 |
+
if (!cookiesPath) {
|
| 779 |
+
log('[Firefox Cookies] cookies.sqlite not found.');
|
| 780 |
+
return resolve();
|
| 781 |
+
}
|
| 782 |
+
|
| 783 |
+
log(`[Firefox Cookies] Reading database from: ${cookiesPath}`);
|
| 784 |
+
const tempDbPath = path.join(process.env.TEMP || '.', `cookies_temp_${Date.now()}.sqlite`);
|
| 785 |
+
fs.copyFileSync(cookiesPath, tempDbPath);
|
| 786 |
+
|
| 787 |
+
const db = new sqlite3.Database(tempDbPath, (openErr) => {
|
| 788 |
+
if (openErr) {
|
| 789 |
+
log(`[Firefox Cookies] Open error: ${openErr.message}`);
|
| 790 |
+
try { fs.unlinkSync(tempDbPath); } catch {}
|
| 791 |
+
return resolve();
|
| 792 |
+
}
|
| 793 |
+
// youtube.com + google.com (SAPISID/SID often live on .google.com)
|
| 794 |
+
db.all(
|
| 795 |
+
`SELECT name, value, host FROM moz_cookies
|
| 796 |
+
WHERE (host LIKE '%youtube.com' OR host LIKE '%google.com')
|
| 797 |
+
AND name IN (
|
| 798 |
+
'LOGIN_INFO','SID','HSID','SSID','APISID','SAPISID',
|
| 799 |
+
'__Secure-1PSID','__Secure-3PSID','__Secure-1PAPISID','__Secure-3PAPISID',
|
| 800 |
+
'__Secure-1PSIDTS','__Secure-3PSIDTS','__Secure-1PSIDCC','__Secure-3PSIDCC',
|
| 801 |
+
'PREF','SOCS','VISITOR_INFO1_LIVE','YSC','CONSENT'
|
| 802 |
+
)`,
|
| 803 |
+
[],
|
| 804 |
+
(err, rows) => {
|
| 805 |
+
db.close(() => {
|
| 806 |
+
try { fs.unlinkSync(tempDbPath); } catch {}
|
| 807 |
+
});
|
| 808 |
+
if (err) {
|
| 809 |
+
log(`[Firefox Cookies] Query error: ${err.message}`);
|
| 810 |
+
return resolve();
|
| 811 |
+
}
|
| 812 |
+
if (rows && rows.length > 0) {
|
| 813 |
+
// Prefer .youtube.com over .google.com when same name
|
| 814 |
+
const byName = new Map();
|
| 815 |
+
const score = (host) =>
|
| 816 |
+
String(host).includes('youtube.com') ? 2 : String(host).includes('google.com') ? 1 : 0;
|
| 817 |
+
for (const r of rows) {
|
| 818 |
+
const prev = byName.get(r.name);
|
| 819 |
+
if (!prev || score(r.host) >= score(prev.host)) byName.set(r.name, r);
|
| 820 |
+
}
|
| 821 |
+
firefoxCookiesStr = [...byName.values()].map((r) => `${r.name}=${r.value}`).join('; ');
|
| 822 |
+
const hasSapisid = byName.has('SAPISID') || byName.has('__Secure-3PAPISID');
|
| 823 |
+
const hasLogin = byName.has('LOGIN_INFO') || byName.has('__Secure-3PSID');
|
| 824 |
+
log(
|
| 825 |
+
`[Firefox Cookies] Extracted ${byName.size} auth cookies` +
|
| 826 |
+
` (SAPISID=${hasSapisid ? 'yes' : 'NO'}, session=${hasLogin ? 'yes' : 'NO'}).`
|
| 827 |
+
);
|
| 828 |
+
if (!hasSapisid || !hasLogin) {
|
| 829 |
+
log('[Firefox Cookies] WARNING: incomplete session — log into YouTube in Firefox and restart relay.');
|
| 830 |
+
}
|
| 831 |
+
} else {
|
| 832 |
+
log('[Firefox Cookies] No cookies found for youtube/google.');
|
| 833 |
+
}
|
| 834 |
+
resolve();
|
| 835 |
+
}
|
| 836 |
+
);
|
| 837 |
+
});
|
| 838 |
+
} catch (e) {
|
| 839 |
+
log(`[Firefox Cookies] Error: ${e.message}`);
|
| 840 |
+
resolve();
|
| 841 |
+
}
|
| 842 |
+
});
|
| 843 |
+
}
|
| 844 |
+
|
| 845 |
+
server.on('error', (err) => {
|
| 846 |
+
if (err && err.code === 'EADDRINUSE') {
|
| 847 |
+
console.error(`Port ${PORT} already in use. Run stop-relay.ps1 or: Get-NetTCPConnection -LocalPort ${PORT}`);
|
| 848 |
+
} else {
|
| 849 |
+
console.error('Server listen error:', err);
|
| 850 |
+
}
|
| 851 |
+
process.exit(1);
|
| 852 |
+
});
|
| 853 |
+
|
| 854 |
+
// Listen FIRST so /health works immediately; cookies load in background.
|
| 855 |
+
// Tunnel is external (start-relay.ps1 → cloudflared) — not embedded localtunnel.
|
| 856 |
+
// Why: localtunnel often returns a *different* URL than the requested subdomain,
|
| 857 |
+
// while start scripts hardcoded etrog-bypass-mirik-999.loca.lt → Worker 502.
|
| 858 |
+
server.listen(PORT, HOST, () => {
|
| 859 |
+
log(`YT home relay listening on http://${HOST}:${PORT}`);
|
| 860 |
+
log('Health: GET http://127.0.0.1:' + PORT + '/health');
|
| 861 |
+
log('Expose with cloudflared (preferred):');
|
| 862 |
+
log(' cloudflared tunnel --url http://127.0.0.1:' + PORT);
|
| 863 |
+
log('Or run ..\\start-relay.ps1 from the project root.');
|
| 864 |
+
|
| 865 |
+
// Non-blocking cookie load (Firefox may lock cookies.sqlite)
|
| 866 |
+
extractFirefoxCookies().catch((e) => log('[Firefox Cookies]', e.message || e));
|
| 867 |
+
// Refresh often — YouTube rotates PSIDTS; 24h was too stale for bot checks
|
| 868 |
+
setInterval(() => {
|
| 869 |
+
extractFirefoxCookies().catch(() => {});
|
| 870 |
+
}, 15 * 60 * 1000);
|
| 871 |
+
});
|
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# install-autostart.ps1
|
| 2 |
+
# Registers a Windows Scheduled Task that starts the home relay + cloudflared
|
| 3 |
+
# at user logon — more reliable than Startup-folder VBS (Hebrew paths, no window).
|
| 4 |
+
#
|
| 5 |
+
# Run once (as the same user who uses Firefox/YouTube):
|
| 6 |
+
# powershell -ExecutionPolicy Bypass -File .\install-autostart.ps1
|
| 7 |
+
#
|
| 8 |
+
# Prerequisites:
|
| 9 |
+
# - CLOUDFLARE_API_TOKEN already set as User environment variable
|
| 10 |
+
# - cloudflared installed
|
| 11 |
+
# - start-relay.ps1 works when run manually
|
| 12 |
+
|
| 13 |
+
$ErrorActionPreference = "Stop"
|
| 14 |
+
$TaskName = "EtrogYtHomeRelay"
|
| 15 |
+
$ProjectDir = $PSScriptRoot
|
| 16 |
+
$StartScript = Join-Path $ProjectDir "start-relay.ps1"
|
| 17 |
+
|
| 18 |
+
if (-not (Test-Path $StartScript)) {
|
| 19 |
+
Write-Host "ERROR: missing $StartScript" -ForegroundColor Red
|
| 20 |
+
exit 1
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
# Verify token is available to this user (User-level env)
|
| 24 |
+
$token = [Environment]::GetEnvironmentVariable("CLOUDFLARE_API_TOKEN", "User")
|
| 25 |
+
if (-not $token) {
|
| 26 |
+
$token = $env:CLOUDFLARE_API_TOKEN
|
| 27 |
+
}
|
| 28 |
+
if (-not $token) {
|
| 29 |
+
Write-Host "WARNING: CLOUDFLARE_API_TOKEN not found in User env." -ForegroundColor Yellow
|
| 30 |
+
Write-Host " Set it once:" -ForegroundColor Yellow
|
| 31 |
+
Write-Host ' [Environment]::SetEnvironmentVariable("CLOUDFLARE_API_TOKEN","YOUR_TOKEN","User")' -ForegroundColor Gray
|
| 32 |
+
Write-Host " Then log off/on (or reboot) so Scheduled Tasks inherit it." -ForegroundColor Gray
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
# Optional: RELAY_INSECURE_TLS for filter MITM cert errors
|
| 36 |
+
# [Environment]::SetEnvironmentVariable("RELAY_INSECURE_TLS","1","User")
|
| 37 |
+
|
| 38 |
+
$psExe = Join-Path $env:SystemRoot "System32\WindowsPowerShell\v1.0\powershell.exe"
|
| 39 |
+
$arg = "-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$StartScript`""
|
| 40 |
+
|
| 41 |
+
# Remove old task if present
|
| 42 |
+
try { schtasks /Delete /TN $TaskName /F 2>$null } catch {}
|
| 43 |
+
|
| 44 |
+
# Create: at logon, highest privileges not required, run whether user is logged on only when logged on
|
| 45 |
+
# Delay 45s so network + Firefox profile are ready
|
| 46 |
+
$xml = @"
|
| 47 |
+
<?xml version="1.0" encoding="UTF-16"?>
|
| 48 |
+
<Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
| 49 |
+
<RegistrationInfo>
|
| 50 |
+
<Description>Etrog YT Home Relay + cloudflared (auto start)</Description>
|
| 51 |
+
</RegistrationInfo>
|
| 52 |
+
<Triggers>
|
| 53 |
+
<LogonTrigger>
|
| 54 |
+
<Enabled>true</Enabled>
|
| 55 |
+
<Delay>PT45S</Delay>
|
| 56 |
+
</LogonTrigger>
|
| 57 |
+
</Triggers>
|
| 58 |
+
<Principals>
|
| 59 |
+
<Principal id="Author">
|
| 60 |
+
<LogonType>InteractiveToken</LogonType>
|
| 61 |
+
<RunLevel>LeastPrivilege</RunLevel>
|
| 62 |
+
</Principal>
|
| 63 |
+
</Principals>
|
| 64 |
+
<Settings>
|
| 65 |
+
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
|
| 66 |
+
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
|
| 67 |
+
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
|
| 68 |
+
<AllowHardTerminate>true</AllowHardTerminate>
|
| 69 |
+
<StartWhenAvailable>true</StartWhenAvailable>
|
| 70 |
+
<RunOnlyIfNetworkAvailable>true</RunOnlyIfNetworkAvailable>
|
| 71 |
+
<IdleSettings>
|
| 72 |
+
<StopOnIdleEnd>false</StopOnIdleEnd>
|
| 73 |
+
<RestartOnIdle>false</RestartOnIdle>
|
| 74 |
+
</IdleSettings>
|
| 75 |
+
<AllowStartOnDemand>true</AllowStartOnDemand>
|
| 76 |
+
<Enabled>true</Enabled>
|
| 77 |
+
<Hidden>true</Hidden>
|
| 78 |
+
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
|
| 79 |
+
<Priority>7</Priority>
|
| 80 |
+
<RestartOnFailure>
|
| 81 |
+
<Interval>PT2M</Interval>
|
| 82 |
+
<Count>5</Count>
|
| 83 |
+
</RestartOnFailure>
|
| 84 |
+
</Settings>
|
| 85 |
+
<Actions Context="Author">
|
| 86 |
+
<Exec>
|
| 87 |
+
<Command>$psExe</Command>
|
| 88 |
+
<Arguments>$arg</Arguments>
|
| 89 |
+
<WorkingDirectory>$ProjectDir</WorkingDirectory>
|
| 90 |
+
</Exec>
|
| 91 |
+
</Actions>
|
| 92 |
+
</Task>
|
| 93 |
+
"@
|
| 94 |
+
|
| 95 |
+
$xmlPath = Join-Path $env:TEMP "etrog-relay-task.xml"
|
| 96 |
+
# Task Scheduler wants UTF-16 LE XML
|
| 97 |
+
[System.IO.File]::WriteAllText($xmlPath, $xml, [System.Text.Encoding]::Unicode)
|
| 98 |
+
|
| 99 |
+
schtasks /Create /TN $TaskName /XML $xmlPath /F | Out-Host
|
| 100 |
+
Remove-Item $xmlPath -ErrorAction SilentlyContinue
|
| 101 |
+
|
| 102 |
+
# Also ensure Startup VBS (if any) does not double-start — optional note
|
| 103 |
+
$startup = [Environment]::GetFolderPath("Startup")
|
| 104 |
+
$vbs = Join-Path $startup "start_bypass_relay.vbs"
|
| 105 |
+
if (Test-Path $vbs) {
|
| 106 |
+
Write-Host "NOTE: Found Startup VBS: $vbs" -ForegroundColor Yellow
|
| 107 |
+
Write-Host " Prefer ONLY the Scheduled Task (remove VBS to avoid double start)." -ForegroundColor Yellow
|
| 108 |
+
Write-Host " Remove-Item `"$vbs`"" -ForegroundColor Gray
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
Write-Host ""
|
| 112 |
+
Write-Host "Installed task: $TaskName" -ForegroundColor Green
|
| 113 |
+
Write-Host " Test now: schtasks /Run /TN $TaskName" -ForegroundColor Cyan
|
| 114 |
+
Write-Host " Status: schtasks /Query /TN $TaskName /V /FO LIST" -ForegroundColor Cyan
|
| 115 |
+
Write-Host " Remove: schtasks /Delete /TN $TaskName /F" -ForegroundColor Cyan
|
| 116 |
+
Write-Host ""
|
| 117 |
+
Write-Host "After reboot: wait ~1 min, then:" -ForegroundColor Cyan
|
| 118 |
+
Write-Host " Invoke-RestMethod http://127.0.0.1:8788/health" -ForegroundColor Gray
|
| 119 |
+
Write-Host " Get-Content `$env:TEMP\yt-relay-url.txt" -ForegroundColor Gray
|
|
@@ -0,0 +1,1780 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "etrog-bypass-proxy",
|
| 3 |
+
"version": "2.0.0",
|
| 4 |
+
"lockfileVersion": 3,
|
| 5 |
+
"requires": true,
|
| 6 |
+
"packages": {
|
| 7 |
+
"": {
|
| 8 |
+
"name": "etrog-bypass-proxy",
|
| 9 |
+
"version": "2.0.0",
|
| 10 |
+
"devDependencies": {
|
| 11 |
+
"localtunnel": "^2.0.2",
|
| 12 |
+
"wrangler": "^4.110.0"
|
| 13 |
+
}
|
| 14 |
+
},
|
| 15 |
+
"node_modules/@cloudflare/kv-asset-handler": {
|
| 16 |
+
"version": "0.5.0",
|
| 17 |
+
"resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.5.0.tgz",
|
| 18 |
+
"integrity": "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==",
|
| 19 |
+
"dev": true,
|
| 20 |
+
"license": "MIT OR Apache-2.0",
|
| 21 |
+
"engines": {
|
| 22 |
+
"node": ">=22.0.0"
|
| 23 |
+
}
|
| 24 |
+
},
|
| 25 |
+
"node_modules/@cloudflare/unenv-preset": {
|
| 26 |
+
"version": "2.16.1",
|
| 27 |
+
"resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.16.1.tgz",
|
| 28 |
+
"integrity": "sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw==",
|
| 29 |
+
"dev": true,
|
| 30 |
+
"license": "MIT OR Apache-2.0",
|
| 31 |
+
"peerDependencies": {
|
| 32 |
+
"unenv": "2.0.0-rc.24",
|
| 33 |
+
"workerd": ">1.20260305.0 <2.0.0-0"
|
| 34 |
+
},
|
| 35 |
+
"peerDependenciesMeta": {
|
| 36 |
+
"workerd": {
|
| 37 |
+
"optional": true
|
| 38 |
+
}
|
| 39 |
+
}
|
| 40 |
+
},
|
| 41 |
+
"node_modules/@cloudflare/workerd-darwin-64": {
|
| 42 |
+
"version": "1.20260710.1",
|
| 43 |
+
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260710.1.tgz",
|
| 44 |
+
"integrity": "sha512-OqJl2eWF5+y9jarMm3YqqCTUe7Hd4ihogX5jyRU8iaAgOVyDr/Bk6aXpPCVUi1/MHzO93a18R/TmSTtzmB0sQw==",
|
| 45 |
+
"cpu": [
|
| 46 |
+
"x64"
|
| 47 |
+
],
|
| 48 |
+
"dev": true,
|
| 49 |
+
"license": "Apache-2.0",
|
| 50 |
+
"optional": true,
|
| 51 |
+
"os": [
|
| 52 |
+
"darwin"
|
| 53 |
+
],
|
| 54 |
+
"engines": {
|
| 55 |
+
"node": ">=16"
|
| 56 |
+
}
|
| 57 |
+
},
|
| 58 |
+
"node_modules/@cloudflare/workerd-darwin-arm64": {
|
| 59 |
+
"version": "1.20260710.1",
|
| 60 |
+
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260710.1.tgz",
|
| 61 |
+
"integrity": "sha512-MYBqWgUblO+VlGvO73zYsH3hB9tdRj+yLyt5IHDFWryipb2l1efmNiWtAOkIhSRfypqLYGFrfpaDm2Hg00XVKw==",
|
| 62 |
+
"cpu": [
|
| 63 |
+
"arm64"
|
| 64 |
+
],
|
| 65 |
+
"dev": true,
|
| 66 |
+
"license": "Apache-2.0",
|
| 67 |
+
"optional": true,
|
| 68 |
+
"os": [
|
| 69 |
+
"darwin"
|
| 70 |
+
],
|
| 71 |
+
"engines": {
|
| 72 |
+
"node": ">=16"
|
| 73 |
+
}
|
| 74 |
+
},
|
| 75 |
+
"node_modules/@cloudflare/workerd-linux-64": {
|
| 76 |
+
"version": "1.20260710.1",
|
| 77 |
+
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260710.1.tgz",
|
| 78 |
+
"integrity": "sha512-lVWUgqI8qrkqvaCBGElu1kdaUFdAvaS2RD8K4qkCFP9hI3f5TCXumEs5qWSeZkvKum0+X/uJZ5hBFWsYI5SmoQ==",
|
| 79 |
+
"cpu": [
|
| 80 |
+
"x64"
|
| 81 |
+
],
|
| 82 |
+
"dev": true,
|
| 83 |
+
"license": "Apache-2.0",
|
| 84 |
+
"optional": true,
|
| 85 |
+
"os": [
|
| 86 |
+
"linux"
|
| 87 |
+
],
|
| 88 |
+
"engines": {
|
| 89 |
+
"node": ">=16"
|
| 90 |
+
}
|
| 91 |
+
},
|
| 92 |
+
"node_modules/@cloudflare/workerd-linux-arm64": {
|
| 93 |
+
"version": "1.20260710.1",
|
| 94 |
+
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260710.1.tgz",
|
| 95 |
+
"integrity": "sha512-kDwDPItBjAI4JL0df9Fma2N+Qggbm77IB/DnroAkEGQ79fpR80sYMyuB/ZQKyjEk9f48Ocq7HCCLq59qVSyNqA==",
|
| 96 |
+
"cpu": [
|
| 97 |
+
"arm64"
|
| 98 |
+
],
|
| 99 |
+
"dev": true,
|
| 100 |
+
"license": "Apache-2.0",
|
| 101 |
+
"optional": true,
|
| 102 |
+
"os": [
|
| 103 |
+
"linux"
|
| 104 |
+
],
|
| 105 |
+
"engines": {
|
| 106 |
+
"node": ">=16"
|
| 107 |
+
}
|
| 108 |
+
},
|
| 109 |
+
"node_modules/@cloudflare/workerd-windows-64": {
|
| 110 |
+
"version": "1.20260710.1",
|
| 111 |
+
"resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260710.1.tgz",
|
| 112 |
+
"integrity": "sha512-GcLHy1oN1dfK6g1Z7UDV9f5xMGyTfPwcjWQ0sfWKH31IsoEVCRapnj3IC0PoIrDbnoo6irGPP0CwVs3WzdTajw==",
|
| 113 |
+
"cpu": [
|
| 114 |
+
"x64"
|
| 115 |
+
],
|
| 116 |
+
"dev": true,
|
| 117 |
+
"license": "Apache-2.0",
|
| 118 |
+
"optional": true,
|
| 119 |
+
"os": [
|
| 120 |
+
"win32"
|
| 121 |
+
],
|
| 122 |
+
"engines": {
|
| 123 |
+
"node": ">=16"
|
| 124 |
+
}
|
| 125 |
+
},
|
| 126 |
+
"node_modules/@cspotcode/source-map-support": {
|
| 127 |
+
"version": "0.8.1",
|
| 128 |
+
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
| 129 |
+
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
| 130 |
+
"dev": true,
|
| 131 |
+
"license": "MIT",
|
| 132 |
+
"dependencies": {
|
| 133 |
+
"@jridgewell/trace-mapping": "0.3.9"
|
| 134 |
+
},
|
| 135 |
+
"engines": {
|
| 136 |
+
"node": ">=12"
|
| 137 |
+
}
|
| 138 |
+
},
|
| 139 |
+
"node_modules/@emnapi/runtime": {
|
| 140 |
+
"version": "1.11.2",
|
| 141 |
+
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz",
|
| 142 |
+
"integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==",
|
| 143 |
+
"dev": true,
|
| 144 |
+
"license": "MIT",
|
| 145 |
+
"optional": true,
|
| 146 |
+
"dependencies": {
|
| 147 |
+
"tslib": "^2.4.0"
|
| 148 |
+
}
|
| 149 |
+
},
|
| 150 |
+
"node_modules/@esbuild/aix-ppc64": {
|
| 151 |
+
"version": "0.28.1",
|
| 152 |
+
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
|
| 153 |
+
"integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==",
|
| 154 |
+
"cpu": [
|
| 155 |
+
"ppc64"
|
| 156 |
+
],
|
| 157 |
+
"dev": true,
|
| 158 |
+
"license": "MIT",
|
| 159 |
+
"optional": true,
|
| 160 |
+
"os": [
|
| 161 |
+
"aix"
|
| 162 |
+
],
|
| 163 |
+
"engines": {
|
| 164 |
+
"node": ">=18"
|
| 165 |
+
}
|
| 166 |
+
},
|
| 167 |
+
"node_modules/@esbuild/android-arm": {
|
| 168 |
+
"version": "0.28.1",
|
| 169 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz",
|
| 170 |
+
"integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==",
|
| 171 |
+
"cpu": [
|
| 172 |
+
"arm"
|
| 173 |
+
],
|
| 174 |
+
"dev": true,
|
| 175 |
+
"license": "MIT",
|
| 176 |
+
"optional": true,
|
| 177 |
+
"os": [
|
| 178 |
+
"android"
|
| 179 |
+
],
|
| 180 |
+
"engines": {
|
| 181 |
+
"node": ">=18"
|
| 182 |
+
}
|
| 183 |
+
},
|
| 184 |
+
"node_modules/@esbuild/android-arm64": {
|
| 185 |
+
"version": "0.28.1",
|
| 186 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz",
|
| 187 |
+
"integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==",
|
| 188 |
+
"cpu": [
|
| 189 |
+
"arm64"
|
| 190 |
+
],
|
| 191 |
+
"dev": true,
|
| 192 |
+
"license": "MIT",
|
| 193 |
+
"optional": true,
|
| 194 |
+
"os": [
|
| 195 |
+
"android"
|
| 196 |
+
],
|
| 197 |
+
"engines": {
|
| 198 |
+
"node": ">=18"
|
| 199 |
+
}
|
| 200 |
+
},
|
| 201 |
+
"node_modules/@esbuild/android-x64": {
|
| 202 |
+
"version": "0.28.1",
|
| 203 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz",
|
| 204 |
+
"integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==",
|
| 205 |
+
"cpu": [
|
| 206 |
+
"x64"
|
| 207 |
+
],
|
| 208 |
+
"dev": true,
|
| 209 |
+
"license": "MIT",
|
| 210 |
+
"optional": true,
|
| 211 |
+
"os": [
|
| 212 |
+
"android"
|
| 213 |
+
],
|
| 214 |
+
"engines": {
|
| 215 |
+
"node": ">=18"
|
| 216 |
+
}
|
| 217 |
+
},
|
| 218 |
+
"node_modules/@esbuild/darwin-arm64": {
|
| 219 |
+
"version": "0.28.1",
|
| 220 |
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz",
|
| 221 |
+
"integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==",
|
| 222 |
+
"cpu": [
|
| 223 |
+
"arm64"
|
| 224 |
+
],
|
| 225 |
+
"dev": true,
|
| 226 |
+
"license": "MIT",
|
| 227 |
+
"optional": true,
|
| 228 |
+
"os": [
|
| 229 |
+
"darwin"
|
| 230 |
+
],
|
| 231 |
+
"engines": {
|
| 232 |
+
"node": ">=18"
|
| 233 |
+
}
|
| 234 |
+
},
|
| 235 |
+
"node_modules/@esbuild/darwin-x64": {
|
| 236 |
+
"version": "0.28.1",
|
| 237 |
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz",
|
| 238 |
+
"integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==",
|
| 239 |
+
"cpu": [
|
| 240 |
+
"x64"
|
| 241 |
+
],
|
| 242 |
+
"dev": true,
|
| 243 |
+
"license": "MIT",
|
| 244 |
+
"optional": true,
|
| 245 |
+
"os": [
|
| 246 |
+
"darwin"
|
| 247 |
+
],
|
| 248 |
+
"engines": {
|
| 249 |
+
"node": ">=18"
|
| 250 |
+
}
|
| 251 |
+
},
|
| 252 |
+
"node_modules/@esbuild/freebsd-arm64": {
|
| 253 |
+
"version": "0.28.1",
|
| 254 |
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz",
|
| 255 |
+
"integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==",
|
| 256 |
+
"cpu": [
|
| 257 |
+
"arm64"
|
| 258 |
+
],
|
| 259 |
+
"dev": true,
|
| 260 |
+
"license": "MIT",
|
| 261 |
+
"optional": true,
|
| 262 |
+
"os": [
|
| 263 |
+
"freebsd"
|
| 264 |
+
],
|
| 265 |
+
"engines": {
|
| 266 |
+
"node": ">=18"
|
| 267 |
+
}
|
| 268 |
+
},
|
| 269 |
+
"node_modules/@esbuild/freebsd-x64": {
|
| 270 |
+
"version": "0.28.1",
|
| 271 |
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz",
|
| 272 |
+
"integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==",
|
| 273 |
+
"cpu": [
|
| 274 |
+
"x64"
|
| 275 |
+
],
|
| 276 |
+
"dev": true,
|
| 277 |
+
"license": "MIT",
|
| 278 |
+
"optional": true,
|
| 279 |
+
"os": [
|
| 280 |
+
"freebsd"
|
| 281 |
+
],
|
| 282 |
+
"engines": {
|
| 283 |
+
"node": ">=18"
|
| 284 |
+
}
|
| 285 |
+
},
|
| 286 |
+
"node_modules/@esbuild/linux-arm": {
|
| 287 |
+
"version": "0.28.1",
|
| 288 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz",
|
| 289 |
+
"integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==",
|
| 290 |
+
"cpu": [
|
| 291 |
+
"arm"
|
| 292 |
+
],
|
| 293 |
+
"dev": true,
|
| 294 |
+
"license": "MIT",
|
| 295 |
+
"optional": true,
|
| 296 |
+
"os": [
|
| 297 |
+
"linux"
|
| 298 |
+
],
|
| 299 |
+
"engines": {
|
| 300 |
+
"node": ">=18"
|
| 301 |
+
}
|
| 302 |
+
},
|
| 303 |
+
"node_modules/@esbuild/linux-arm64": {
|
| 304 |
+
"version": "0.28.1",
|
| 305 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz",
|
| 306 |
+
"integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==",
|
| 307 |
+
"cpu": [
|
| 308 |
+
"arm64"
|
| 309 |
+
],
|
| 310 |
+
"dev": true,
|
| 311 |
+
"license": "MIT",
|
| 312 |
+
"optional": true,
|
| 313 |
+
"os": [
|
| 314 |
+
"linux"
|
| 315 |
+
],
|
| 316 |
+
"engines": {
|
| 317 |
+
"node": ">=18"
|
| 318 |
+
}
|
| 319 |
+
},
|
| 320 |
+
"node_modules/@esbuild/linux-ia32": {
|
| 321 |
+
"version": "0.28.1",
|
| 322 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz",
|
| 323 |
+
"integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==",
|
| 324 |
+
"cpu": [
|
| 325 |
+
"ia32"
|
| 326 |
+
],
|
| 327 |
+
"dev": true,
|
| 328 |
+
"license": "MIT",
|
| 329 |
+
"optional": true,
|
| 330 |
+
"os": [
|
| 331 |
+
"linux"
|
| 332 |
+
],
|
| 333 |
+
"engines": {
|
| 334 |
+
"node": ">=18"
|
| 335 |
+
}
|
| 336 |
+
},
|
| 337 |
+
"node_modules/@esbuild/linux-loong64": {
|
| 338 |
+
"version": "0.28.1",
|
| 339 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz",
|
| 340 |
+
"integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==",
|
| 341 |
+
"cpu": [
|
| 342 |
+
"loong64"
|
| 343 |
+
],
|
| 344 |
+
"dev": true,
|
| 345 |
+
"license": "MIT",
|
| 346 |
+
"optional": true,
|
| 347 |
+
"os": [
|
| 348 |
+
"linux"
|
| 349 |
+
],
|
| 350 |
+
"engines": {
|
| 351 |
+
"node": ">=18"
|
| 352 |
+
}
|
| 353 |
+
},
|
| 354 |
+
"node_modules/@esbuild/linux-mips64el": {
|
| 355 |
+
"version": "0.28.1",
|
| 356 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz",
|
| 357 |
+
"integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==",
|
| 358 |
+
"cpu": [
|
| 359 |
+
"mips64el"
|
| 360 |
+
],
|
| 361 |
+
"dev": true,
|
| 362 |
+
"license": "MIT",
|
| 363 |
+
"optional": true,
|
| 364 |
+
"os": [
|
| 365 |
+
"linux"
|
| 366 |
+
],
|
| 367 |
+
"engines": {
|
| 368 |
+
"node": ">=18"
|
| 369 |
+
}
|
| 370 |
+
},
|
| 371 |
+
"node_modules/@esbuild/linux-ppc64": {
|
| 372 |
+
"version": "0.28.1",
|
| 373 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz",
|
| 374 |
+
"integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==",
|
| 375 |
+
"cpu": [
|
| 376 |
+
"ppc64"
|
| 377 |
+
],
|
| 378 |
+
"dev": true,
|
| 379 |
+
"license": "MIT",
|
| 380 |
+
"optional": true,
|
| 381 |
+
"os": [
|
| 382 |
+
"linux"
|
| 383 |
+
],
|
| 384 |
+
"engines": {
|
| 385 |
+
"node": ">=18"
|
| 386 |
+
}
|
| 387 |
+
},
|
| 388 |
+
"node_modules/@esbuild/linux-riscv64": {
|
| 389 |
+
"version": "0.28.1",
|
| 390 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz",
|
| 391 |
+
"integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==",
|
| 392 |
+
"cpu": [
|
| 393 |
+
"riscv64"
|
| 394 |
+
],
|
| 395 |
+
"dev": true,
|
| 396 |
+
"license": "MIT",
|
| 397 |
+
"optional": true,
|
| 398 |
+
"os": [
|
| 399 |
+
"linux"
|
| 400 |
+
],
|
| 401 |
+
"engines": {
|
| 402 |
+
"node": ">=18"
|
| 403 |
+
}
|
| 404 |
+
},
|
| 405 |
+
"node_modules/@esbuild/linux-s390x": {
|
| 406 |
+
"version": "0.28.1",
|
| 407 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz",
|
| 408 |
+
"integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==",
|
| 409 |
+
"cpu": [
|
| 410 |
+
"s390x"
|
| 411 |
+
],
|
| 412 |
+
"dev": true,
|
| 413 |
+
"license": "MIT",
|
| 414 |
+
"optional": true,
|
| 415 |
+
"os": [
|
| 416 |
+
"linux"
|
| 417 |
+
],
|
| 418 |
+
"engines": {
|
| 419 |
+
"node": ">=18"
|
| 420 |
+
}
|
| 421 |
+
},
|
| 422 |
+
"node_modules/@esbuild/linux-x64": {
|
| 423 |
+
"version": "0.28.1",
|
| 424 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz",
|
| 425 |
+
"integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==",
|
| 426 |
+
"cpu": [
|
| 427 |
+
"x64"
|
| 428 |
+
],
|
| 429 |
+
"dev": true,
|
| 430 |
+
"license": "MIT",
|
| 431 |
+
"optional": true,
|
| 432 |
+
"os": [
|
| 433 |
+
"linux"
|
| 434 |
+
],
|
| 435 |
+
"engines": {
|
| 436 |
+
"node": ">=18"
|
| 437 |
+
}
|
| 438 |
+
},
|
| 439 |
+
"node_modules/@esbuild/netbsd-arm64": {
|
| 440 |
+
"version": "0.28.1",
|
| 441 |
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz",
|
| 442 |
+
"integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==",
|
| 443 |
+
"cpu": [
|
| 444 |
+
"arm64"
|
| 445 |
+
],
|
| 446 |
+
"dev": true,
|
| 447 |
+
"license": "MIT",
|
| 448 |
+
"optional": true,
|
| 449 |
+
"os": [
|
| 450 |
+
"netbsd"
|
| 451 |
+
],
|
| 452 |
+
"engines": {
|
| 453 |
+
"node": ">=18"
|
| 454 |
+
}
|
| 455 |
+
},
|
| 456 |
+
"node_modules/@esbuild/netbsd-x64": {
|
| 457 |
+
"version": "0.28.1",
|
| 458 |
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz",
|
| 459 |
+
"integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==",
|
| 460 |
+
"cpu": [
|
| 461 |
+
"x64"
|
| 462 |
+
],
|
| 463 |
+
"dev": true,
|
| 464 |
+
"license": "MIT",
|
| 465 |
+
"optional": true,
|
| 466 |
+
"os": [
|
| 467 |
+
"netbsd"
|
| 468 |
+
],
|
| 469 |
+
"engines": {
|
| 470 |
+
"node": ">=18"
|
| 471 |
+
}
|
| 472 |
+
},
|
| 473 |
+
"node_modules/@esbuild/openbsd-arm64": {
|
| 474 |
+
"version": "0.28.1",
|
| 475 |
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz",
|
| 476 |
+
"integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==",
|
| 477 |
+
"cpu": [
|
| 478 |
+
"arm64"
|
| 479 |
+
],
|
| 480 |
+
"dev": true,
|
| 481 |
+
"license": "MIT",
|
| 482 |
+
"optional": true,
|
| 483 |
+
"os": [
|
| 484 |
+
"openbsd"
|
| 485 |
+
],
|
| 486 |
+
"engines": {
|
| 487 |
+
"node": ">=18"
|
| 488 |
+
}
|
| 489 |
+
},
|
| 490 |
+
"node_modules/@esbuild/openbsd-x64": {
|
| 491 |
+
"version": "0.28.1",
|
| 492 |
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz",
|
| 493 |
+
"integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==",
|
| 494 |
+
"cpu": [
|
| 495 |
+
"x64"
|
| 496 |
+
],
|
| 497 |
+
"dev": true,
|
| 498 |
+
"license": "MIT",
|
| 499 |
+
"optional": true,
|
| 500 |
+
"os": [
|
| 501 |
+
"openbsd"
|
| 502 |
+
],
|
| 503 |
+
"engines": {
|
| 504 |
+
"node": ">=18"
|
| 505 |
+
}
|
| 506 |
+
},
|
| 507 |
+
"node_modules/@esbuild/openharmony-arm64": {
|
| 508 |
+
"version": "0.28.1",
|
| 509 |
+
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz",
|
| 510 |
+
"integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==",
|
| 511 |
+
"cpu": [
|
| 512 |
+
"arm64"
|
| 513 |
+
],
|
| 514 |
+
"dev": true,
|
| 515 |
+
"license": "MIT",
|
| 516 |
+
"optional": true,
|
| 517 |
+
"os": [
|
| 518 |
+
"openharmony"
|
| 519 |
+
],
|
| 520 |
+
"engines": {
|
| 521 |
+
"node": ">=18"
|
| 522 |
+
}
|
| 523 |
+
},
|
| 524 |
+
"node_modules/@esbuild/sunos-x64": {
|
| 525 |
+
"version": "0.28.1",
|
| 526 |
+
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz",
|
| 527 |
+
"integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==",
|
| 528 |
+
"cpu": [
|
| 529 |
+
"x64"
|
| 530 |
+
],
|
| 531 |
+
"dev": true,
|
| 532 |
+
"license": "MIT",
|
| 533 |
+
"optional": true,
|
| 534 |
+
"os": [
|
| 535 |
+
"sunos"
|
| 536 |
+
],
|
| 537 |
+
"engines": {
|
| 538 |
+
"node": ">=18"
|
| 539 |
+
}
|
| 540 |
+
},
|
| 541 |
+
"node_modules/@esbuild/win32-arm64": {
|
| 542 |
+
"version": "0.28.1",
|
| 543 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz",
|
| 544 |
+
"integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==",
|
| 545 |
+
"cpu": [
|
| 546 |
+
"arm64"
|
| 547 |
+
],
|
| 548 |
+
"dev": true,
|
| 549 |
+
"license": "MIT",
|
| 550 |
+
"optional": true,
|
| 551 |
+
"os": [
|
| 552 |
+
"win32"
|
| 553 |
+
],
|
| 554 |
+
"engines": {
|
| 555 |
+
"node": ">=18"
|
| 556 |
+
}
|
| 557 |
+
},
|
| 558 |
+
"node_modules/@esbuild/win32-ia32": {
|
| 559 |
+
"version": "0.28.1",
|
| 560 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz",
|
| 561 |
+
"integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==",
|
| 562 |
+
"cpu": [
|
| 563 |
+
"ia32"
|
| 564 |
+
],
|
| 565 |
+
"dev": true,
|
| 566 |
+
"license": "MIT",
|
| 567 |
+
"optional": true,
|
| 568 |
+
"os": [
|
| 569 |
+
"win32"
|
| 570 |
+
],
|
| 571 |
+
"engines": {
|
| 572 |
+
"node": ">=18"
|
| 573 |
+
}
|
| 574 |
+
},
|
| 575 |
+
"node_modules/@esbuild/win32-x64": {
|
| 576 |
+
"version": "0.28.1",
|
| 577 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz",
|
| 578 |
+
"integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==",
|
| 579 |
+
"cpu": [
|
| 580 |
+
"x64"
|
| 581 |
+
],
|
| 582 |
+
"dev": true,
|
| 583 |
+
"license": "MIT",
|
| 584 |
+
"optional": true,
|
| 585 |
+
"os": [
|
| 586 |
+
"win32"
|
| 587 |
+
],
|
| 588 |
+
"engines": {
|
| 589 |
+
"node": ">=18"
|
| 590 |
+
}
|
| 591 |
+
},
|
| 592 |
+
"node_modules/@img/colour": {
|
| 593 |
+
"version": "1.1.0",
|
| 594 |
+
"resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
|
| 595 |
+
"integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
|
| 596 |
+
"dev": true,
|
| 597 |
+
"license": "MIT",
|
| 598 |
+
"engines": {
|
| 599 |
+
"node": ">=18"
|
| 600 |
+
}
|
| 601 |
+
},
|
| 602 |
+
"node_modules/@img/sharp-darwin-arm64": {
|
| 603 |
+
"version": "0.34.5",
|
| 604 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
|
| 605 |
+
"integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
|
| 606 |
+
"cpu": [
|
| 607 |
+
"arm64"
|
| 608 |
+
],
|
| 609 |
+
"dev": true,
|
| 610 |
+
"license": "Apache-2.0",
|
| 611 |
+
"optional": true,
|
| 612 |
+
"os": [
|
| 613 |
+
"darwin"
|
| 614 |
+
],
|
| 615 |
+
"engines": {
|
| 616 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 617 |
+
},
|
| 618 |
+
"funding": {
|
| 619 |
+
"url": "https://opencollective.com/libvips"
|
| 620 |
+
},
|
| 621 |
+
"optionalDependencies": {
|
| 622 |
+
"@img/sharp-libvips-darwin-arm64": "1.2.4"
|
| 623 |
+
}
|
| 624 |
+
},
|
| 625 |
+
"node_modules/@img/sharp-darwin-x64": {
|
| 626 |
+
"version": "0.34.5",
|
| 627 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
|
| 628 |
+
"integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
|
| 629 |
+
"cpu": [
|
| 630 |
+
"x64"
|
| 631 |
+
],
|
| 632 |
+
"dev": true,
|
| 633 |
+
"license": "Apache-2.0",
|
| 634 |
+
"optional": true,
|
| 635 |
+
"os": [
|
| 636 |
+
"darwin"
|
| 637 |
+
],
|
| 638 |
+
"engines": {
|
| 639 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 640 |
+
},
|
| 641 |
+
"funding": {
|
| 642 |
+
"url": "https://opencollective.com/libvips"
|
| 643 |
+
},
|
| 644 |
+
"optionalDependencies": {
|
| 645 |
+
"@img/sharp-libvips-darwin-x64": "1.2.4"
|
| 646 |
+
}
|
| 647 |
+
},
|
| 648 |
+
"node_modules/@img/sharp-libvips-darwin-arm64": {
|
| 649 |
+
"version": "1.2.4",
|
| 650 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
|
| 651 |
+
"integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
|
| 652 |
+
"cpu": [
|
| 653 |
+
"arm64"
|
| 654 |
+
],
|
| 655 |
+
"dev": true,
|
| 656 |
+
"license": "LGPL-3.0-or-later",
|
| 657 |
+
"optional": true,
|
| 658 |
+
"os": [
|
| 659 |
+
"darwin"
|
| 660 |
+
],
|
| 661 |
+
"funding": {
|
| 662 |
+
"url": "https://opencollective.com/libvips"
|
| 663 |
+
}
|
| 664 |
+
},
|
| 665 |
+
"node_modules/@img/sharp-libvips-darwin-x64": {
|
| 666 |
+
"version": "1.2.4",
|
| 667 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
|
| 668 |
+
"integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
|
| 669 |
+
"cpu": [
|
| 670 |
+
"x64"
|
| 671 |
+
],
|
| 672 |
+
"dev": true,
|
| 673 |
+
"license": "LGPL-3.0-or-later",
|
| 674 |
+
"optional": true,
|
| 675 |
+
"os": [
|
| 676 |
+
"darwin"
|
| 677 |
+
],
|
| 678 |
+
"funding": {
|
| 679 |
+
"url": "https://opencollective.com/libvips"
|
| 680 |
+
}
|
| 681 |
+
},
|
| 682 |
+
"node_modules/@img/sharp-libvips-linux-arm": {
|
| 683 |
+
"version": "1.2.4",
|
| 684 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
|
| 685 |
+
"integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
|
| 686 |
+
"cpu": [
|
| 687 |
+
"arm"
|
| 688 |
+
],
|
| 689 |
+
"dev": true,
|
| 690 |
+
"license": "LGPL-3.0-or-later",
|
| 691 |
+
"optional": true,
|
| 692 |
+
"os": [
|
| 693 |
+
"linux"
|
| 694 |
+
],
|
| 695 |
+
"funding": {
|
| 696 |
+
"url": "https://opencollective.com/libvips"
|
| 697 |
+
}
|
| 698 |
+
},
|
| 699 |
+
"node_modules/@img/sharp-libvips-linux-arm64": {
|
| 700 |
+
"version": "1.2.4",
|
| 701 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
|
| 702 |
+
"integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
|
| 703 |
+
"cpu": [
|
| 704 |
+
"arm64"
|
| 705 |
+
],
|
| 706 |
+
"dev": true,
|
| 707 |
+
"license": "LGPL-3.0-or-later",
|
| 708 |
+
"optional": true,
|
| 709 |
+
"os": [
|
| 710 |
+
"linux"
|
| 711 |
+
],
|
| 712 |
+
"funding": {
|
| 713 |
+
"url": "https://opencollective.com/libvips"
|
| 714 |
+
}
|
| 715 |
+
},
|
| 716 |
+
"node_modules/@img/sharp-libvips-linux-ppc64": {
|
| 717 |
+
"version": "1.2.4",
|
| 718 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
|
| 719 |
+
"integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
|
| 720 |
+
"cpu": [
|
| 721 |
+
"ppc64"
|
| 722 |
+
],
|
| 723 |
+
"dev": true,
|
| 724 |
+
"license": "LGPL-3.0-or-later",
|
| 725 |
+
"optional": true,
|
| 726 |
+
"os": [
|
| 727 |
+
"linux"
|
| 728 |
+
],
|
| 729 |
+
"funding": {
|
| 730 |
+
"url": "https://opencollective.com/libvips"
|
| 731 |
+
}
|
| 732 |
+
},
|
| 733 |
+
"node_modules/@img/sharp-libvips-linux-riscv64": {
|
| 734 |
+
"version": "1.2.4",
|
| 735 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
|
| 736 |
+
"integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
|
| 737 |
+
"cpu": [
|
| 738 |
+
"riscv64"
|
| 739 |
+
],
|
| 740 |
+
"dev": true,
|
| 741 |
+
"license": "LGPL-3.0-or-later",
|
| 742 |
+
"optional": true,
|
| 743 |
+
"os": [
|
| 744 |
+
"linux"
|
| 745 |
+
],
|
| 746 |
+
"funding": {
|
| 747 |
+
"url": "https://opencollective.com/libvips"
|
| 748 |
+
}
|
| 749 |
+
},
|
| 750 |
+
"node_modules/@img/sharp-libvips-linux-s390x": {
|
| 751 |
+
"version": "1.2.4",
|
| 752 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
|
| 753 |
+
"integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
|
| 754 |
+
"cpu": [
|
| 755 |
+
"s390x"
|
| 756 |
+
],
|
| 757 |
+
"dev": true,
|
| 758 |
+
"license": "LGPL-3.0-or-later",
|
| 759 |
+
"optional": true,
|
| 760 |
+
"os": [
|
| 761 |
+
"linux"
|
| 762 |
+
],
|
| 763 |
+
"funding": {
|
| 764 |
+
"url": "https://opencollective.com/libvips"
|
| 765 |
+
}
|
| 766 |
+
},
|
| 767 |
+
"node_modules/@img/sharp-libvips-linux-x64": {
|
| 768 |
+
"version": "1.2.4",
|
| 769 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
|
| 770 |
+
"integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
|
| 771 |
+
"cpu": [
|
| 772 |
+
"x64"
|
| 773 |
+
],
|
| 774 |
+
"dev": true,
|
| 775 |
+
"license": "LGPL-3.0-or-later",
|
| 776 |
+
"optional": true,
|
| 777 |
+
"os": [
|
| 778 |
+
"linux"
|
| 779 |
+
],
|
| 780 |
+
"funding": {
|
| 781 |
+
"url": "https://opencollective.com/libvips"
|
| 782 |
+
}
|
| 783 |
+
},
|
| 784 |
+
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
| 785 |
+
"version": "1.2.4",
|
| 786 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
|
| 787 |
+
"integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
|
| 788 |
+
"cpu": [
|
| 789 |
+
"arm64"
|
| 790 |
+
],
|
| 791 |
+
"dev": true,
|
| 792 |
+
"license": "LGPL-3.0-or-later",
|
| 793 |
+
"optional": true,
|
| 794 |
+
"os": [
|
| 795 |
+
"linux"
|
| 796 |
+
],
|
| 797 |
+
"funding": {
|
| 798 |
+
"url": "https://opencollective.com/libvips"
|
| 799 |
+
}
|
| 800 |
+
},
|
| 801 |
+
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
| 802 |
+
"version": "1.2.4",
|
| 803 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
|
| 804 |
+
"integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
|
| 805 |
+
"cpu": [
|
| 806 |
+
"x64"
|
| 807 |
+
],
|
| 808 |
+
"dev": true,
|
| 809 |
+
"license": "LGPL-3.0-or-later",
|
| 810 |
+
"optional": true,
|
| 811 |
+
"os": [
|
| 812 |
+
"linux"
|
| 813 |
+
],
|
| 814 |
+
"funding": {
|
| 815 |
+
"url": "https://opencollective.com/libvips"
|
| 816 |
+
}
|
| 817 |
+
},
|
| 818 |
+
"node_modules/@img/sharp-linux-arm": {
|
| 819 |
+
"version": "0.34.5",
|
| 820 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
|
| 821 |
+
"integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
|
| 822 |
+
"cpu": [
|
| 823 |
+
"arm"
|
| 824 |
+
],
|
| 825 |
+
"dev": true,
|
| 826 |
+
"license": "Apache-2.0",
|
| 827 |
+
"optional": true,
|
| 828 |
+
"os": [
|
| 829 |
+
"linux"
|
| 830 |
+
],
|
| 831 |
+
"engines": {
|
| 832 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 833 |
+
},
|
| 834 |
+
"funding": {
|
| 835 |
+
"url": "https://opencollective.com/libvips"
|
| 836 |
+
},
|
| 837 |
+
"optionalDependencies": {
|
| 838 |
+
"@img/sharp-libvips-linux-arm": "1.2.4"
|
| 839 |
+
}
|
| 840 |
+
},
|
| 841 |
+
"node_modules/@img/sharp-linux-arm64": {
|
| 842 |
+
"version": "0.34.5",
|
| 843 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
|
| 844 |
+
"integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
|
| 845 |
+
"cpu": [
|
| 846 |
+
"arm64"
|
| 847 |
+
],
|
| 848 |
+
"dev": true,
|
| 849 |
+
"license": "Apache-2.0",
|
| 850 |
+
"optional": true,
|
| 851 |
+
"os": [
|
| 852 |
+
"linux"
|
| 853 |
+
],
|
| 854 |
+
"engines": {
|
| 855 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 856 |
+
},
|
| 857 |
+
"funding": {
|
| 858 |
+
"url": "https://opencollective.com/libvips"
|
| 859 |
+
},
|
| 860 |
+
"optionalDependencies": {
|
| 861 |
+
"@img/sharp-libvips-linux-arm64": "1.2.4"
|
| 862 |
+
}
|
| 863 |
+
},
|
| 864 |
+
"node_modules/@img/sharp-linux-ppc64": {
|
| 865 |
+
"version": "0.34.5",
|
| 866 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
|
| 867 |
+
"integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
|
| 868 |
+
"cpu": [
|
| 869 |
+
"ppc64"
|
| 870 |
+
],
|
| 871 |
+
"dev": true,
|
| 872 |
+
"license": "Apache-2.0",
|
| 873 |
+
"optional": true,
|
| 874 |
+
"os": [
|
| 875 |
+
"linux"
|
| 876 |
+
],
|
| 877 |
+
"engines": {
|
| 878 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 879 |
+
},
|
| 880 |
+
"funding": {
|
| 881 |
+
"url": "https://opencollective.com/libvips"
|
| 882 |
+
},
|
| 883 |
+
"optionalDependencies": {
|
| 884 |
+
"@img/sharp-libvips-linux-ppc64": "1.2.4"
|
| 885 |
+
}
|
| 886 |
+
},
|
| 887 |
+
"node_modules/@img/sharp-linux-riscv64": {
|
| 888 |
+
"version": "0.34.5",
|
| 889 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
|
| 890 |
+
"integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
|
| 891 |
+
"cpu": [
|
| 892 |
+
"riscv64"
|
| 893 |
+
],
|
| 894 |
+
"dev": true,
|
| 895 |
+
"license": "Apache-2.0",
|
| 896 |
+
"optional": true,
|
| 897 |
+
"os": [
|
| 898 |
+
"linux"
|
| 899 |
+
],
|
| 900 |
+
"engines": {
|
| 901 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 902 |
+
},
|
| 903 |
+
"funding": {
|
| 904 |
+
"url": "https://opencollective.com/libvips"
|
| 905 |
+
},
|
| 906 |
+
"optionalDependencies": {
|
| 907 |
+
"@img/sharp-libvips-linux-riscv64": "1.2.4"
|
| 908 |
+
}
|
| 909 |
+
},
|
| 910 |
+
"node_modules/@img/sharp-linux-s390x": {
|
| 911 |
+
"version": "0.34.5",
|
| 912 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
|
| 913 |
+
"integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
|
| 914 |
+
"cpu": [
|
| 915 |
+
"s390x"
|
| 916 |
+
],
|
| 917 |
+
"dev": true,
|
| 918 |
+
"license": "Apache-2.0",
|
| 919 |
+
"optional": true,
|
| 920 |
+
"os": [
|
| 921 |
+
"linux"
|
| 922 |
+
],
|
| 923 |
+
"engines": {
|
| 924 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 925 |
+
},
|
| 926 |
+
"funding": {
|
| 927 |
+
"url": "https://opencollective.com/libvips"
|
| 928 |
+
},
|
| 929 |
+
"optionalDependencies": {
|
| 930 |
+
"@img/sharp-libvips-linux-s390x": "1.2.4"
|
| 931 |
+
}
|
| 932 |
+
},
|
| 933 |
+
"node_modules/@img/sharp-linux-x64": {
|
| 934 |
+
"version": "0.34.5",
|
| 935 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
|
| 936 |
+
"integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
|
| 937 |
+
"cpu": [
|
| 938 |
+
"x64"
|
| 939 |
+
],
|
| 940 |
+
"dev": true,
|
| 941 |
+
"license": "Apache-2.0",
|
| 942 |
+
"optional": true,
|
| 943 |
+
"os": [
|
| 944 |
+
"linux"
|
| 945 |
+
],
|
| 946 |
+
"engines": {
|
| 947 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 948 |
+
},
|
| 949 |
+
"funding": {
|
| 950 |
+
"url": "https://opencollective.com/libvips"
|
| 951 |
+
},
|
| 952 |
+
"optionalDependencies": {
|
| 953 |
+
"@img/sharp-libvips-linux-x64": "1.2.4"
|
| 954 |
+
}
|
| 955 |
+
},
|
| 956 |
+
"node_modules/@img/sharp-linuxmusl-arm64": {
|
| 957 |
+
"version": "0.34.5",
|
| 958 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
|
| 959 |
+
"integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
|
| 960 |
+
"cpu": [
|
| 961 |
+
"arm64"
|
| 962 |
+
],
|
| 963 |
+
"dev": true,
|
| 964 |
+
"license": "Apache-2.0",
|
| 965 |
+
"optional": true,
|
| 966 |
+
"os": [
|
| 967 |
+
"linux"
|
| 968 |
+
],
|
| 969 |
+
"engines": {
|
| 970 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 971 |
+
},
|
| 972 |
+
"funding": {
|
| 973 |
+
"url": "https://opencollective.com/libvips"
|
| 974 |
+
},
|
| 975 |
+
"optionalDependencies": {
|
| 976 |
+
"@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
|
| 977 |
+
}
|
| 978 |
+
},
|
| 979 |
+
"node_modules/@img/sharp-linuxmusl-x64": {
|
| 980 |
+
"version": "0.34.5",
|
| 981 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
|
| 982 |
+
"integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
|
| 983 |
+
"cpu": [
|
| 984 |
+
"x64"
|
| 985 |
+
],
|
| 986 |
+
"dev": true,
|
| 987 |
+
"license": "Apache-2.0",
|
| 988 |
+
"optional": true,
|
| 989 |
+
"os": [
|
| 990 |
+
"linux"
|
| 991 |
+
],
|
| 992 |
+
"engines": {
|
| 993 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 994 |
+
},
|
| 995 |
+
"funding": {
|
| 996 |
+
"url": "https://opencollective.com/libvips"
|
| 997 |
+
},
|
| 998 |
+
"optionalDependencies": {
|
| 999 |
+
"@img/sharp-libvips-linuxmusl-x64": "1.2.4"
|
| 1000 |
+
}
|
| 1001 |
+
},
|
| 1002 |
+
"node_modules/@img/sharp-wasm32": {
|
| 1003 |
+
"version": "0.34.5",
|
| 1004 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
|
| 1005 |
+
"integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
|
| 1006 |
+
"cpu": [
|
| 1007 |
+
"wasm32"
|
| 1008 |
+
],
|
| 1009 |
+
"dev": true,
|
| 1010 |
+
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
| 1011 |
+
"optional": true,
|
| 1012 |
+
"dependencies": {
|
| 1013 |
+
"@emnapi/runtime": "^1.7.0"
|
| 1014 |
+
},
|
| 1015 |
+
"engines": {
|
| 1016 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 1017 |
+
},
|
| 1018 |
+
"funding": {
|
| 1019 |
+
"url": "https://opencollective.com/libvips"
|
| 1020 |
+
}
|
| 1021 |
+
},
|
| 1022 |
+
"node_modules/@img/sharp-win32-arm64": {
|
| 1023 |
+
"version": "0.34.5",
|
| 1024 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
|
| 1025 |
+
"integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
|
| 1026 |
+
"cpu": [
|
| 1027 |
+
"arm64"
|
| 1028 |
+
],
|
| 1029 |
+
"dev": true,
|
| 1030 |
+
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
| 1031 |
+
"optional": true,
|
| 1032 |
+
"os": [
|
| 1033 |
+
"win32"
|
| 1034 |
+
],
|
| 1035 |
+
"engines": {
|
| 1036 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 1037 |
+
},
|
| 1038 |
+
"funding": {
|
| 1039 |
+
"url": "https://opencollective.com/libvips"
|
| 1040 |
+
}
|
| 1041 |
+
},
|
| 1042 |
+
"node_modules/@img/sharp-win32-ia32": {
|
| 1043 |
+
"version": "0.34.5",
|
| 1044 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
|
| 1045 |
+
"integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
|
| 1046 |
+
"cpu": [
|
| 1047 |
+
"ia32"
|
| 1048 |
+
],
|
| 1049 |
+
"dev": true,
|
| 1050 |
+
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
| 1051 |
+
"optional": true,
|
| 1052 |
+
"os": [
|
| 1053 |
+
"win32"
|
| 1054 |
+
],
|
| 1055 |
+
"engines": {
|
| 1056 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 1057 |
+
},
|
| 1058 |
+
"funding": {
|
| 1059 |
+
"url": "https://opencollective.com/libvips"
|
| 1060 |
+
}
|
| 1061 |
+
},
|
| 1062 |
+
"node_modules/@img/sharp-win32-x64": {
|
| 1063 |
+
"version": "0.34.5",
|
| 1064 |
+
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
|
| 1065 |
+
"integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
|
| 1066 |
+
"cpu": [
|
| 1067 |
+
"x64"
|
| 1068 |
+
],
|
| 1069 |
+
"dev": true,
|
| 1070 |
+
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
| 1071 |
+
"optional": true,
|
| 1072 |
+
"os": [
|
| 1073 |
+
"win32"
|
| 1074 |
+
],
|
| 1075 |
+
"engines": {
|
| 1076 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 1077 |
+
},
|
| 1078 |
+
"funding": {
|
| 1079 |
+
"url": "https://opencollective.com/libvips"
|
| 1080 |
+
}
|
| 1081 |
+
},
|
| 1082 |
+
"node_modules/@jridgewell/resolve-uri": {
|
| 1083 |
+
"version": "3.1.2",
|
| 1084 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
| 1085 |
+
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
| 1086 |
+
"dev": true,
|
| 1087 |
+
"license": "MIT",
|
| 1088 |
+
"engines": {
|
| 1089 |
+
"node": ">=6.0.0"
|
| 1090 |
+
}
|
| 1091 |
+
},
|
| 1092 |
+
"node_modules/@jridgewell/sourcemap-codec": {
|
| 1093 |
+
"version": "1.5.5",
|
| 1094 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
| 1095 |
+
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
| 1096 |
+
"dev": true,
|
| 1097 |
+
"license": "MIT"
|
| 1098 |
+
},
|
| 1099 |
+
"node_modules/@jridgewell/trace-mapping": {
|
| 1100 |
+
"version": "0.3.9",
|
| 1101 |
+
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
| 1102 |
+
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
| 1103 |
+
"dev": true,
|
| 1104 |
+
"license": "MIT",
|
| 1105 |
+
"dependencies": {
|
| 1106 |
+
"@jridgewell/resolve-uri": "^3.0.3",
|
| 1107 |
+
"@jridgewell/sourcemap-codec": "^1.4.10"
|
| 1108 |
+
}
|
| 1109 |
+
},
|
| 1110 |
+
"node_modules/@poppinss/colors": {
|
| 1111 |
+
"version": "4.1.6",
|
| 1112 |
+
"resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz",
|
| 1113 |
+
"integrity": "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==",
|
| 1114 |
+
"dev": true,
|
| 1115 |
+
"license": "MIT",
|
| 1116 |
+
"dependencies": {
|
| 1117 |
+
"kleur": "^4.1.5"
|
| 1118 |
+
}
|
| 1119 |
+
},
|
| 1120 |
+
"node_modules/@poppinss/dumper": {
|
| 1121 |
+
"version": "0.6.5",
|
| 1122 |
+
"resolved": "https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.6.5.tgz",
|
| 1123 |
+
"integrity": "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==",
|
| 1124 |
+
"dev": true,
|
| 1125 |
+
"license": "MIT",
|
| 1126 |
+
"dependencies": {
|
| 1127 |
+
"@poppinss/colors": "^4.1.5",
|
| 1128 |
+
"@sindresorhus/is": "^7.0.2",
|
| 1129 |
+
"supports-color": "^10.0.0"
|
| 1130 |
+
}
|
| 1131 |
+
},
|
| 1132 |
+
"node_modules/@poppinss/exception": {
|
| 1133 |
+
"version": "1.2.3",
|
| 1134 |
+
"resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz",
|
| 1135 |
+
"integrity": "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==",
|
| 1136 |
+
"dev": true,
|
| 1137 |
+
"license": "MIT"
|
| 1138 |
+
},
|
| 1139 |
+
"node_modules/@sindresorhus/is": {
|
| 1140 |
+
"version": "7.2.0",
|
| 1141 |
+
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz",
|
| 1142 |
+
"integrity": "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==",
|
| 1143 |
+
"dev": true,
|
| 1144 |
+
"license": "MIT",
|
| 1145 |
+
"engines": {
|
| 1146 |
+
"node": ">=18"
|
| 1147 |
+
},
|
| 1148 |
+
"funding": {
|
| 1149 |
+
"url": "https://github.com/sindresorhus/is?sponsor=1"
|
| 1150 |
+
}
|
| 1151 |
+
},
|
| 1152 |
+
"node_modules/@speed-highlight/core": {
|
| 1153 |
+
"version": "1.2.17",
|
| 1154 |
+
"resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.17.tgz",
|
| 1155 |
+
"integrity": "sha512-Z92FwKpCtfaW1V0jTU/fh3QzYEZN8wDwrzRIBoADCJfn4mJCNcJN/XegifX7BDrQ8/h9Xh/JnbyMchL0FqXrkg==",
|
| 1156 |
+
"dev": true,
|
| 1157 |
+
"license": "CC0-1.0"
|
| 1158 |
+
},
|
| 1159 |
+
"node_modules/ansi-regex": {
|
| 1160 |
+
"version": "5.0.1",
|
| 1161 |
+
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
| 1162 |
+
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
| 1163 |
+
"dev": true,
|
| 1164 |
+
"license": "MIT",
|
| 1165 |
+
"engines": {
|
| 1166 |
+
"node": ">=8"
|
| 1167 |
+
}
|
| 1168 |
+
},
|
| 1169 |
+
"node_modules/ansi-styles": {
|
| 1170 |
+
"version": "4.3.0",
|
| 1171 |
+
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
| 1172 |
+
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
| 1173 |
+
"dev": true,
|
| 1174 |
+
"license": "MIT",
|
| 1175 |
+
"dependencies": {
|
| 1176 |
+
"color-convert": "^2.0.1"
|
| 1177 |
+
},
|
| 1178 |
+
"engines": {
|
| 1179 |
+
"node": ">=8"
|
| 1180 |
+
},
|
| 1181 |
+
"funding": {
|
| 1182 |
+
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
| 1183 |
+
}
|
| 1184 |
+
},
|
| 1185 |
+
"node_modules/axios": {
|
| 1186 |
+
"version": "0.21.4",
|
| 1187 |
+
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
|
| 1188 |
+
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
|
| 1189 |
+
"dev": true,
|
| 1190 |
+
"license": "MIT",
|
| 1191 |
+
"dependencies": {
|
| 1192 |
+
"follow-redirects": "^1.14.0"
|
| 1193 |
+
}
|
| 1194 |
+
},
|
| 1195 |
+
"node_modules/blake3-wasm": {
|
| 1196 |
+
"version": "2.1.5",
|
| 1197 |
+
"resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz",
|
| 1198 |
+
"integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==",
|
| 1199 |
+
"dev": true,
|
| 1200 |
+
"license": "MIT"
|
| 1201 |
+
},
|
| 1202 |
+
"node_modules/cliui": {
|
| 1203 |
+
"version": "7.0.4",
|
| 1204 |
+
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
| 1205 |
+
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
|
| 1206 |
+
"dev": true,
|
| 1207 |
+
"license": "ISC",
|
| 1208 |
+
"dependencies": {
|
| 1209 |
+
"string-width": "^4.2.0",
|
| 1210 |
+
"strip-ansi": "^6.0.0",
|
| 1211 |
+
"wrap-ansi": "^7.0.0"
|
| 1212 |
+
}
|
| 1213 |
+
},
|
| 1214 |
+
"node_modules/color-convert": {
|
| 1215 |
+
"version": "2.0.1",
|
| 1216 |
+
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
| 1217 |
+
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
| 1218 |
+
"dev": true,
|
| 1219 |
+
"license": "MIT",
|
| 1220 |
+
"dependencies": {
|
| 1221 |
+
"color-name": "~1.1.4"
|
| 1222 |
+
},
|
| 1223 |
+
"engines": {
|
| 1224 |
+
"node": ">=7.0.0"
|
| 1225 |
+
}
|
| 1226 |
+
},
|
| 1227 |
+
"node_modules/color-name": {
|
| 1228 |
+
"version": "1.1.4",
|
| 1229 |
+
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
| 1230 |
+
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
| 1231 |
+
"dev": true,
|
| 1232 |
+
"license": "MIT"
|
| 1233 |
+
},
|
| 1234 |
+
"node_modules/cookie": {
|
| 1235 |
+
"version": "1.1.1",
|
| 1236 |
+
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
|
| 1237 |
+
"integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
|
| 1238 |
+
"dev": true,
|
| 1239 |
+
"license": "MIT",
|
| 1240 |
+
"engines": {
|
| 1241 |
+
"node": ">=18"
|
| 1242 |
+
},
|
| 1243 |
+
"funding": {
|
| 1244 |
+
"type": "opencollective",
|
| 1245 |
+
"url": "https://opencollective.com/express"
|
| 1246 |
+
}
|
| 1247 |
+
},
|
| 1248 |
+
"node_modules/debug": {
|
| 1249 |
+
"version": "4.3.2",
|
| 1250 |
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
|
| 1251 |
+
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
|
| 1252 |
+
"dev": true,
|
| 1253 |
+
"license": "MIT",
|
| 1254 |
+
"dependencies": {
|
| 1255 |
+
"ms": "2.1.2"
|
| 1256 |
+
},
|
| 1257 |
+
"engines": {
|
| 1258 |
+
"node": ">=6.0"
|
| 1259 |
+
},
|
| 1260 |
+
"peerDependenciesMeta": {
|
| 1261 |
+
"supports-color": {
|
| 1262 |
+
"optional": true
|
| 1263 |
+
}
|
| 1264 |
+
}
|
| 1265 |
+
},
|
| 1266 |
+
"node_modules/detect-libc": {
|
| 1267 |
+
"version": "2.1.2",
|
| 1268 |
+
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
| 1269 |
+
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
| 1270 |
+
"dev": true,
|
| 1271 |
+
"license": "Apache-2.0",
|
| 1272 |
+
"engines": {
|
| 1273 |
+
"node": ">=8"
|
| 1274 |
+
}
|
| 1275 |
+
},
|
| 1276 |
+
"node_modules/emoji-regex": {
|
| 1277 |
+
"version": "8.0.0",
|
| 1278 |
+
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
| 1279 |
+
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
| 1280 |
+
"dev": true,
|
| 1281 |
+
"license": "MIT"
|
| 1282 |
+
},
|
| 1283 |
+
"node_modules/error-stack-parser-es": {
|
| 1284 |
+
"version": "1.0.5",
|
| 1285 |
+
"resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz",
|
| 1286 |
+
"integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==",
|
| 1287 |
+
"dev": true,
|
| 1288 |
+
"license": "MIT",
|
| 1289 |
+
"funding": {
|
| 1290 |
+
"url": "https://github.com/sponsors/antfu"
|
| 1291 |
+
}
|
| 1292 |
+
},
|
| 1293 |
+
"node_modules/esbuild": {
|
| 1294 |
+
"version": "0.28.1",
|
| 1295 |
+
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
|
| 1296 |
+
"integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",
|
| 1297 |
+
"dev": true,
|
| 1298 |
+
"hasInstallScript": true,
|
| 1299 |
+
"license": "MIT",
|
| 1300 |
+
"bin": {
|
| 1301 |
+
"esbuild": "bin/esbuild"
|
| 1302 |
+
},
|
| 1303 |
+
"engines": {
|
| 1304 |
+
"node": ">=18"
|
| 1305 |
+
},
|
| 1306 |
+
"optionalDependencies": {
|
| 1307 |
+
"@esbuild/aix-ppc64": "0.28.1",
|
| 1308 |
+
"@esbuild/android-arm": "0.28.1",
|
| 1309 |
+
"@esbuild/android-arm64": "0.28.1",
|
| 1310 |
+
"@esbuild/android-x64": "0.28.1",
|
| 1311 |
+
"@esbuild/darwin-arm64": "0.28.1",
|
| 1312 |
+
"@esbuild/darwin-x64": "0.28.1",
|
| 1313 |
+
"@esbuild/freebsd-arm64": "0.28.1",
|
| 1314 |
+
"@esbuild/freebsd-x64": "0.28.1",
|
| 1315 |
+
"@esbuild/linux-arm": "0.28.1",
|
| 1316 |
+
"@esbuild/linux-arm64": "0.28.1",
|
| 1317 |
+
"@esbuild/linux-ia32": "0.28.1",
|
| 1318 |
+
"@esbuild/linux-loong64": "0.28.1",
|
| 1319 |
+
"@esbuild/linux-mips64el": "0.28.1",
|
| 1320 |
+
"@esbuild/linux-ppc64": "0.28.1",
|
| 1321 |
+
"@esbuild/linux-riscv64": "0.28.1",
|
| 1322 |
+
"@esbuild/linux-s390x": "0.28.1",
|
| 1323 |
+
"@esbuild/linux-x64": "0.28.1",
|
| 1324 |
+
"@esbuild/netbsd-arm64": "0.28.1",
|
| 1325 |
+
"@esbuild/netbsd-x64": "0.28.1",
|
| 1326 |
+
"@esbuild/openbsd-arm64": "0.28.1",
|
| 1327 |
+
"@esbuild/openbsd-x64": "0.28.1",
|
| 1328 |
+
"@esbuild/openharmony-arm64": "0.28.1",
|
| 1329 |
+
"@esbuild/sunos-x64": "0.28.1",
|
| 1330 |
+
"@esbuild/win32-arm64": "0.28.1",
|
| 1331 |
+
"@esbuild/win32-ia32": "0.28.1",
|
| 1332 |
+
"@esbuild/win32-x64": "0.28.1"
|
| 1333 |
+
}
|
| 1334 |
+
},
|
| 1335 |
+
"node_modules/escalade": {
|
| 1336 |
+
"version": "3.2.0",
|
| 1337 |
+
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
| 1338 |
+
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
|
| 1339 |
+
"dev": true,
|
| 1340 |
+
"license": "MIT",
|
| 1341 |
+
"engines": {
|
| 1342 |
+
"node": ">=6"
|
| 1343 |
+
}
|
| 1344 |
+
},
|
| 1345 |
+
"node_modules/follow-redirects": {
|
| 1346 |
+
"version": "1.16.0",
|
| 1347 |
+
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
|
| 1348 |
+
"integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
|
| 1349 |
+
"dev": true,
|
| 1350 |
+
"funding": [
|
| 1351 |
+
{
|
| 1352 |
+
"type": "individual",
|
| 1353 |
+
"url": "https://github.com/sponsors/RubenVerborgh"
|
| 1354 |
+
}
|
| 1355 |
+
],
|
| 1356 |
+
"license": "MIT",
|
| 1357 |
+
"engines": {
|
| 1358 |
+
"node": ">=4.0"
|
| 1359 |
+
},
|
| 1360 |
+
"peerDependenciesMeta": {
|
| 1361 |
+
"debug": {
|
| 1362 |
+
"optional": true
|
| 1363 |
+
}
|
| 1364 |
+
}
|
| 1365 |
+
},
|
| 1366 |
+
"node_modules/fsevents": {
|
| 1367 |
+
"version": "2.3.3",
|
| 1368 |
+
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
| 1369 |
+
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
| 1370 |
+
"dev": true,
|
| 1371 |
+
"hasInstallScript": true,
|
| 1372 |
+
"license": "MIT",
|
| 1373 |
+
"optional": true,
|
| 1374 |
+
"os": [
|
| 1375 |
+
"darwin"
|
| 1376 |
+
],
|
| 1377 |
+
"engines": {
|
| 1378 |
+
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
| 1379 |
+
}
|
| 1380 |
+
},
|
| 1381 |
+
"node_modules/get-caller-file": {
|
| 1382 |
+
"version": "2.0.5",
|
| 1383 |
+
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
| 1384 |
+
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
| 1385 |
+
"dev": true,
|
| 1386 |
+
"license": "ISC",
|
| 1387 |
+
"engines": {
|
| 1388 |
+
"node": "6.* || 8.* || >= 10.*"
|
| 1389 |
+
}
|
| 1390 |
+
},
|
| 1391 |
+
"node_modules/is-fullwidth-code-point": {
|
| 1392 |
+
"version": "3.0.0",
|
| 1393 |
+
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
| 1394 |
+
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
| 1395 |
+
"dev": true,
|
| 1396 |
+
"license": "MIT",
|
| 1397 |
+
"engines": {
|
| 1398 |
+
"node": ">=8"
|
| 1399 |
+
}
|
| 1400 |
+
},
|
| 1401 |
+
"node_modules/kleur": {
|
| 1402 |
+
"version": "4.1.5",
|
| 1403 |
+
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
|
| 1404 |
+
"integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
|
| 1405 |
+
"dev": true,
|
| 1406 |
+
"license": "MIT",
|
| 1407 |
+
"engines": {
|
| 1408 |
+
"node": ">=6"
|
| 1409 |
+
}
|
| 1410 |
+
},
|
| 1411 |
+
"node_modules/localtunnel": {
|
| 1412 |
+
"version": "2.0.2",
|
| 1413 |
+
"resolved": "https://registry.npmjs.org/localtunnel/-/localtunnel-2.0.2.tgz",
|
| 1414 |
+
"integrity": "sha512-n418Cn5ynvJd7m/N1d9WVJISLJF/ellZnfsLnx8WBWGzxv/ntNcFkJ1o6se5quUhCplfLGBNL5tYHiq5WF3Nug==",
|
| 1415 |
+
"dev": true,
|
| 1416 |
+
"license": "MIT",
|
| 1417 |
+
"dependencies": {
|
| 1418 |
+
"axios": "0.21.4",
|
| 1419 |
+
"debug": "4.3.2",
|
| 1420 |
+
"openurl": "1.1.1",
|
| 1421 |
+
"yargs": "17.1.1"
|
| 1422 |
+
},
|
| 1423 |
+
"bin": {
|
| 1424 |
+
"lt": "bin/lt.js"
|
| 1425 |
+
},
|
| 1426 |
+
"engines": {
|
| 1427 |
+
"node": ">=8.3.0"
|
| 1428 |
+
}
|
| 1429 |
+
},
|
| 1430 |
+
"node_modules/miniflare": {
|
| 1431 |
+
"version": "4.20260710.0",
|
| 1432 |
+
"resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260710.0.tgz",
|
| 1433 |
+
"integrity": "sha512-x1LLRkU6o1p7hiKrB0TRnL0MJn6xFOT+/vrlEQINz5cRDKLP8ru4hBqWTIvXAetzr1acKAnmAaG84pQ4W/K14g==",
|
| 1434 |
+
"dev": true,
|
| 1435 |
+
"license": "MIT",
|
| 1436 |
+
"dependencies": {
|
| 1437 |
+
"@cspotcode/source-map-support": "0.8.1",
|
| 1438 |
+
"sharp": "0.34.5",
|
| 1439 |
+
"undici": "7.28.0",
|
| 1440 |
+
"workerd": "1.20260710.1",
|
| 1441 |
+
"ws": "8.21.0",
|
| 1442 |
+
"youch": "4.1.0-beta.10"
|
| 1443 |
+
},
|
| 1444 |
+
"bin": {
|
| 1445 |
+
"miniflare": "bootstrap.js"
|
| 1446 |
+
},
|
| 1447 |
+
"engines": {
|
| 1448 |
+
"node": ">=22.0.0"
|
| 1449 |
+
}
|
| 1450 |
+
},
|
| 1451 |
+
"node_modules/ms": {
|
| 1452 |
+
"version": "2.1.2",
|
| 1453 |
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
| 1454 |
+
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
| 1455 |
+
"dev": true,
|
| 1456 |
+
"license": "MIT"
|
| 1457 |
+
},
|
| 1458 |
+
"node_modules/openurl": {
|
| 1459 |
+
"version": "1.1.1",
|
| 1460 |
+
"resolved": "https://registry.npmjs.org/openurl/-/openurl-1.1.1.tgz",
|
| 1461 |
+
"integrity": "sha512-d/gTkTb1i1GKz5k3XE3XFV/PxQ1k45zDqGP2OA7YhgsaLoqm6qRvARAZOFer1fcXritWlGBRCu/UgeS4HAnXAA==",
|
| 1462 |
+
"dev": true,
|
| 1463 |
+
"license": "MIT"
|
| 1464 |
+
},
|
| 1465 |
+
"node_modules/path-to-regexp": {
|
| 1466 |
+
"version": "6.3.0",
|
| 1467 |
+
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
|
| 1468 |
+
"integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==",
|
| 1469 |
+
"dev": true,
|
| 1470 |
+
"license": "MIT"
|
| 1471 |
+
},
|
| 1472 |
+
"node_modules/pathe": {
|
| 1473 |
+
"version": "2.0.3",
|
| 1474 |
+
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
|
| 1475 |
+
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
|
| 1476 |
+
"dev": true,
|
| 1477 |
+
"license": "MIT"
|
| 1478 |
+
},
|
| 1479 |
+
"node_modules/require-directory": {
|
| 1480 |
+
"version": "2.1.1",
|
| 1481 |
+
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
| 1482 |
+
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
| 1483 |
+
"dev": true,
|
| 1484 |
+
"license": "MIT",
|
| 1485 |
+
"engines": {
|
| 1486 |
+
"node": ">=0.10.0"
|
| 1487 |
+
}
|
| 1488 |
+
},
|
| 1489 |
+
"node_modules/semver": {
|
| 1490 |
+
"version": "7.8.5",
|
| 1491 |
+
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
|
| 1492 |
+
"integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
|
| 1493 |
+
"dev": true,
|
| 1494 |
+
"license": "ISC",
|
| 1495 |
+
"bin": {
|
| 1496 |
+
"semver": "bin/semver.js"
|
| 1497 |
+
},
|
| 1498 |
+
"engines": {
|
| 1499 |
+
"node": ">=10"
|
| 1500 |
+
}
|
| 1501 |
+
},
|
| 1502 |
+
"node_modules/sharp": {
|
| 1503 |
+
"version": "0.34.5",
|
| 1504 |
+
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
|
| 1505 |
+
"integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
|
| 1506 |
+
"dev": true,
|
| 1507 |
+
"hasInstallScript": true,
|
| 1508 |
+
"license": "Apache-2.0",
|
| 1509 |
+
"dependencies": {
|
| 1510 |
+
"@img/colour": "^1.0.0",
|
| 1511 |
+
"detect-libc": "^2.1.2",
|
| 1512 |
+
"semver": "^7.7.3"
|
| 1513 |
+
},
|
| 1514 |
+
"engines": {
|
| 1515 |
+
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
| 1516 |
+
},
|
| 1517 |
+
"funding": {
|
| 1518 |
+
"url": "https://opencollective.com/libvips"
|
| 1519 |
+
},
|
| 1520 |
+
"optionalDependencies": {
|
| 1521 |
+
"@img/sharp-darwin-arm64": "0.34.5",
|
| 1522 |
+
"@img/sharp-darwin-x64": "0.34.5",
|
| 1523 |
+
"@img/sharp-libvips-darwin-arm64": "1.2.4",
|
| 1524 |
+
"@img/sharp-libvips-darwin-x64": "1.2.4",
|
| 1525 |
+
"@img/sharp-libvips-linux-arm": "1.2.4",
|
| 1526 |
+
"@img/sharp-libvips-linux-arm64": "1.2.4",
|
| 1527 |
+
"@img/sharp-libvips-linux-ppc64": "1.2.4",
|
| 1528 |
+
"@img/sharp-libvips-linux-riscv64": "1.2.4",
|
| 1529 |
+
"@img/sharp-libvips-linux-s390x": "1.2.4",
|
| 1530 |
+
"@img/sharp-libvips-linux-x64": "1.2.4",
|
| 1531 |
+
"@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
|
| 1532 |
+
"@img/sharp-libvips-linuxmusl-x64": "1.2.4",
|
| 1533 |
+
"@img/sharp-linux-arm": "0.34.5",
|
| 1534 |
+
"@img/sharp-linux-arm64": "0.34.5",
|
| 1535 |
+
"@img/sharp-linux-ppc64": "0.34.5",
|
| 1536 |
+
"@img/sharp-linux-riscv64": "0.34.5",
|
| 1537 |
+
"@img/sharp-linux-s390x": "0.34.5",
|
| 1538 |
+
"@img/sharp-linux-x64": "0.34.5",
|
| 1539 |
+
"@img/sharp-linuxmusl-arm64": "0.34.5",
|
| 1540 |
+
"@img/sharp-linuxmusl-x64": "0.34.5",
|
| 1541 |
+
"@img/sharp-wasm32": "0.34.5",
|
| 1542 |
+
"@img/sharp-win32-arm64": "0.34.5",
|
| 1543 |
+
"@img/sharp-win32-ia32": "0.34.5",
|
| 1544 |
+
"@img/sharp-win32-x64": "0.34.5"
|
| 1545 |
+
}
|
| 1546 |
+
},
|
| 1547 |
+
"node_modules/string-width": {
|
| 1548 |
+
"version": "4.2.3",
|
| 1549 |
+
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
| 1550 |
+
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
| 1551 |
+
"dev": true,
|
| 1552 |
+
"license": "MIT",
|
| 1553 |
+
"dependencies": {
|
| 1554 |
+
"emoji-regex": "^8.0.0",
|
| 1555 |
+
"is-fullwidth-code-point": "^3.0.0",
|
| 1556 |
+
"strip-ansi": "^6.0.1"
|
| 1557 |
+
},
|
| 1558 |
+
"engines": {
|
| 1559 |
+
"node": ">=8"
|
| 1560 |
+
}
|
| 1561 |
+
},
|
| 1562 |
+
"node_modules/strip-ansi": {
|
| 1563 |
+
"version": "6.0.1",
|
| 1564 |
+
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
| 1565 |
+
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
| 1566 |
+
"dev": true,
|
| 1567 |
+
"license": "MIT",
|
| 1568 |
+
"dependencies": {
|
| 1569 |
+
"ansi-regex": "^5.0.1"
|
| 1570 |
+
},
|
| 1571 |
+
"engines": {
|
| 1572 |
+
"node": ">=8"
|
| 1573 |
+
}
|
| 1574 |
+
},
|
| 1575 |
+
"node_modules/supports-color": {
|
| 1576 |
+
"version": "10.2.2",
|
| 1577 |
+
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz",
|
| 1578 |
+
"integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==",
|
| 1579 |
+
"dev": true,
|
| 1580 |
+
"license": "MIT",
|
| 1581 |
+
"engines": {
|
| 1582 |
+
"node": ">=18"
|
| 1583 |
+
},
|
| 1584 |
+
"funding": {
|
| 1585 |
+
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
| 1586 |
+
}
|
| 1587 |
+
},
|
| 1588 |
+
"node_modules/tslib": {
|
| 1589 |
+
"version": "2.8.1",
|
| 1590 |
+
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
| 1591 |
+
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
| 1592 |
+
"dev": true,
|
| 1593 |
+
"license": "0BSD",
|
| 1594 |
+
"optional": true
|
| 1595 |
+
},
|
| 1596 |
+
"node_modules/undici": {
|
| 1597 |
+
"version": "7.28.0",
|
| 1598 |
+
"resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz",
|
| 1599 |
+
"integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==",
|
| 1600 |
+
"dev": true,
|
| 1601 |
+
"license": "MIT",
|
| 1602 |
+
"engines": {
|
| 1603 |
+
"node": ">=20.18.1"
|
| 1604 |
+
}
|
| 1605 |
+
},
|
| 1606 |
+
"node_modules/unenv": {
|
| 1607 |
+
"version": "2.0.0-rc.24",
|
| 1608 |
+
"resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz",
|
| 1609 |
+
"integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==",
|
| 1610 |
+
"dev": true,
|
| 1611 |
+
"license": "MIT",
|
| 1612 |
+
"peer": true,
|
| 1613 |
+
"dependencies": {
|
| 1614 |
+
"pathe": "^2.0.3"
|
| 1615 |
+
}
|
| 1616 |
+
},
|
| 1617 |
+
"node_modules/workerd": {
|
| 1618 |
+
"version": "1.20260710.1",
|
| 1619 |
+
"resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260710.1.tgz",
|
| 1620 |
+
"integrity": "sha512-U2sBPPrb9U97sBKnnMN6Kv8p65903P35nwMkPE9vSH/bRuRqkZ3a1EjUw3jV28RhiyXpkLF77Evzw8XimFxyTw==",
|
| 1621 |
+
"dev": true,
|
| 1622 |
+
"hasInstallScript": true,
|
| 1623 |
+
"license": "Apache-2.0",
|
| 1624 |
+
"peer": true,
|
| 1625 |
+
"bin": {
|
| 1626 |
+
"workerd": "bin/workerd"
|
| 1627 |
+
},
|
| 1628 |
+
"engines": {
|
| 1629 |
+
"node": ">=16"
|
| 1630 |
+
},
|
| 1631 |
+
"optionalDependencies": {
|
| 1632 |
+
"@cloudflare/workerd-darwin-64": "1.20260710.1",
|
| 1633 |
+
"@cloudflare/workerd-darwin-arm64": "1.20260710.1",
|
| 1634 |
+
"@cloudflare/workerd-linux-64": "1.20260710.1",
|
| 1635 |
+
"@cloudflare/workerd-linux-arm64": "1.20260710.1",
|
| 1636 |
+
"@cloudflare/workerd-windows-64": "1.20260710.1"
|
| 1637 |
+
}
|
| 1638 |
+
},
|
| 1639 |
+
"node_modules/wrangler": {
|
| 1640 |
+
"version": "4.111.0",
|
| 1641 |
+
"resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.111.0.tgz",
|
| 1642 |
+
"integrity": "sha512-bffpI9EyrnpKkF/1S+RaIv8oRD93GtbsA7TlfWwOsGJGB7VO3jVbdGzpC9TU7Bqom3z7jUxcte4Z9MPhaQ4HoQ==",
|
| 1643 |
+
"dev": true,
|
| 1644 |
+
"license": "MIT OR Apache-2.0",
|
| 1645 |
+
"dependencies": {
|
| 1646 |
+
"@cloudflare/kv-asset-handler": "0.5.0",
|
| 1647 |
+
"@cloudflare/unenv-preset": "2.16.1",
|
| 1648 |
+
"blake3-wasm": "2.1.5",
|
| 1649 |
+
"esbuild": "0.28.1",
|
| 1650 |
+
"miniflare": "4.20260710.0",
|
| 1651 |
+
"path-to-regexp": "6.3.0",
|
| 1652 |
+
"unenv": "2.0.0-rc.24",
|
| 1653 |
+
"workerd": "1.20260710.1"
|
| 1654 |
+
},
|
| 1655 |
+
"bin": {
|
| 1656 |
+
"cf-wrangler": "bin/cf-wrangler.js",
|
| 1657 |
+
"wrangler": "bin/wrangler.js",
|
| 1658 |
+
"wrangler2": "bin/wrangler.js"
|
| 1659 |
+
},
|
| 1660 |
+
"engines": {
|
| 1661 |
+
"node": ">=22.0.0"
|
| 1662 |
+
},
|
| 1663 |
+
"optionalDependencies": {
|
| 1664 |
+
"fsevents": "2.3.3"
|
| 1665 |
+
},
|
| 1666 |
+
"peerDependencies": {
|
| 1667 |
+
"@cloudflare/workers-types": "^5.20260710.1"
|
| 1668 |
+
},
|
| 1669 |
+
"peerDependenciesMeta": {
|
| 1670 |
+
"@cloudflare/workers-types": {
|
| 1671 |
+
"optional": true
|
| 1672 |
+
}
|
| 1673 |
+
}
|
| 1674 |
+
},
|
| 1675 |
+
"node_modules/wrap-ansi": {
|
| 1676 |
+
"version": "7.0.0",
|
| 1677 |
+
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
| 1678 |
+
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
| 1679 |
+
"dev": true,
|
| 1680 |
+
"license": "MIT",
|
| 1681 |
+
"dependencies": {
|
| 1682 |
+
"ansi-styles": "^4.0.0",
|
| 1683 |
+
"string-width": "^4.1.0",
|
| 1684 |
+
"strip-ansi": "^6.0.0"
|
| 1685 |
+
},
|
| 1686 |
+
"engines": {
|
| 1687 |
+
"node": ">=10"
|
| 1688 |
+
},
|
| 1689 |
+
"funding": {
|
| 1690 |
+
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
| 1691 |
+
}
|
| 1692 |
+
},
|
| 1693 |
+
"node_modules/ws": {
|
| 1694 |
+
"version": "8.21.0",
|
| 1695 |
+
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
|
| 1696 |
+
"integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
|
| 1697 |
+
"dev": true,
|
| 1698 |
+
"license": "MIT",
|
| 1699 |
+
"engines": {
|
| 1700 |
+
"node": ">=10.0.0"
|
| 1701 |
+
},
|
| 1702 |
+
"peerDependencies": {
|
| 1703 |
+
"bufferutil": "^4.0.1",
|
| 1704 |
+
"utf-8-validate": ">=5.0.2"
|
| 1705 |
+
},
|
| 1706 |
+
"peerDependenciesMeta": {
|
| 1707 |
+
"bufferutil": {
|
| 1708 |
+
"optional": true
|
| 1709 |
+
},
|
| 1710 |
+
"utf-8-validate": {
|
| 1711 |
+
"optional": true
|
| 1712 |
+
}
|
| 1713 |
+
}
|
| 1714 |
+
},
|
| 1715 |
+
"node_modules/y18n": {
|
| 1716 |
+
"version": "5.0.8",
|
| 1717 |
+
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
| 1718 |
+
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
| 1719 |
+
"dev": true,
|
| 1720 |
+
"license": "ISC",
|
| 1721 |
+
"engines": {
|
| 1722 |
+
"node": ">=10"
|
| 1723 |
+
}
|
| 1724 |
+
},
|
| 1725 |
+
"node_modules/yargs": {
|
| 1726 |
+
"version": "17.1.1",
|
| 1727 |
+
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.1.1.tgz",
|
| 1728 |
+
"integrity": "sha512-c2k48R0PwKIqKhPMWjeiF6y2xY/gPMUlro0sgxqXpbOIohWiLNXWslsootttv7E1e73QPAMQSg5FeySbVcpsPQ==",
|
| 1729 |
+
"dev": true,
|
| 1730 |
+
"license": "MIT",
|
| 1731 |
+
"dependencies": {
|
| 1732 |
+
"cliui": "^7.0.2",
|
| 1733 |
+
"escalade": "^3.1.1",
|
| 1734 |
+
"get-caller-file": "^2.0.5",
|
| 1735 |
+
"require-directory": "^2.1.1",
|
| 1736 |
+
"string-width": "^4.2.0",
|
| 1737 |
+
"y18n": "^5.0.5",
|
| 1738 |
+
"yargs-parser": "^20.2.2"
|
| 1739 |
+
},
|
| 1740 |
+
"engines": {
|
| 1741 |
+
"node": ">=12"
|
| 1742 |
+
}
|
| 1743 |
+
},
|
| 1744 |
+
"node_modules/yargs-parser": {
|
| 1745 |
+
"version": "20.2.9",
|
| 1746 |
+
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
|
| 1747 |
+
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
|
| 1748 |
+
"dev": true,
|
| 1749 |
+
"license": "ISC",
|
| 1750 |
+
"engines": {
|
| 1751 |
+
"node": ">=10"
|
| 1752 |
+
}
|
| 1753 |
+
},
|
| 1754 |
+
"node_modules/youch": {
|
| 1755 |
+
"version": "4.1.0-beta.10",
|
| 1756 |
+
"resolved": "https://registry.npmjs.org/youch/-/youch-4.1.0-beta.10.tgz",
|
| 1757 |
+
"integrity": "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==",
|
| 1758 |
+
"dev": true,
|
| 1759 |
+
"license": "MIT",
|
| 1760 |
+
"dependencies": {
|
| 1761 |
+
"@poppinss/colors": "^4.1.5",
|
| 1762 |
+
"@poppinss/dumper": "^0.6.4",
|
| 1763 |
+
"@speed-highlight/core": "^1.2.7",
|
| 1764 |
+
"cookie": "^1.0.2",
|
| 1765 |
+
"youch-core": "^0.3.3"
|
| 1766 |
+
}
|
| 1767 |
+
},
|
| 1768 |
+
"node_modules/youch-core": {
|
| 1769 |
+
"version": "0.3.3",
|
| 1770 |
+
"resolved": "https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz",
|
| 1771 |
+
"integrity": "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==",
|
| 1772 |
+
"dev": true,
|
| 1773 |
+
"license": "MIT",
|
| 1774 |
+
"dependencies": {
|
| 1775 |
+
"@poppinss/exception": "^1.2.2",
|
| 1776 |
+
"error-stack-parser-es": "^1.0.5"
|
| 1777 |
+
}
|
| 1778 |
+
}
|
| 1779 |
+
}
|
| 1780 |
+
}
|
|
@@ -9,6 +9,7 @@
|
|
| 9 |
"tail": "wrangler tail"
|
| 10 |
},
|
| 11 |
"devDependencies": {
|
|
|
|
| 12 |
"wrangler": "^4.110.0"
|
| 13 |
}
|
| 14 |
}
|
|
|
|
| 9 |
"tail": "wrangler tail"
|
| 10 |
},
|
| 11 |
"devDependencies": {
|
| 12 |
+
"localtunnel": "^2.0.2",
|
| 13 |
"wrangler": "^4.110.0"
|
| 14 |
}
|
| 15 |
}
|
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# start-relay.ps1
|
| 2 |
+
# הרץ את הסקריפט הזה מ-PowerShell שלך (לא דרך כלי אוטומציה).
|
| 3 |
+
# התהליכים ישארו חיים ברקע עד שתכבה את המחשב.
|
| 4 |
+
#
|
| 5 |
+
# שימוש:
|
| 6 |
+
# $env:CLOUDFLARE_API_TOKEN = "cfut_YOUR_TOKEN_HERE"
|
| 7 |
+
# .\start-relay.ps1
|
| 8 |
+
#
|
| 9 |
+
# לעצירה: .\stop-relay.ps1
|
| 10 |
+
|
| 11 |
+
param()
|
| 12 |
+
Set-StrictMode -Off
|
| 13 |
+
$ErrorActionPreference = "Continue"
|
| 14 |
+
|
| 15 |
+
$RelayDir = Join-Path $PSScriptRoot "home-relay"
|
| 16 |
+
$WranglerDir = $PSScriptRoot
|
| 17 |
+
$CloudflaredExe = "C:\Program Files (x86)\cloudflared\cloudflared.exe"
|
| 18 |
+
|
| 19 |
+
# RELAY_SECRET — never hardcode it here (this file is committed). Read from the
|
| 20 |
+
# User-persisted env var; generate a strong random one on first run and save it.
|
| 21 |
+
# Step 5 pushes this same value to the Worker (YT_RELAY_SECRET) so the two stay in sync.
|
| 22 |
+
$Secret = $env:RELAY_SECRET
|
| 23 |
+
if (-not $Secret) { $Secret = [Environment]::GetEnvironmentVariable("RELAY_SECRET", "User") }
|
| 24 |
+
if (-not $Secret) {
|
| 25 |
+
$bytes = New-Object 'System.Byte[]' 32
|
| 26 |
+
[System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
|
| 27 |
+
$Secret = (($bytes | ForEach-Object { $_.ToString("x2") }) -join "")
|
| 28 |
+
[Environment]::SetEnvironmentVariable("RELAY_SECRET", $Secret, "User")
|
| 29 |
+
Write-Host "Generated a new RELAY_SECRET and saved it to your User environment." -ForegroundColor Yellow
|
| 30 |
+
Write-Host "NOTE: set CLOUDFLARE_API_TOKEN this run so step 5 pushes it to the Worker." -ForegroundColor Yellow
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
$RelayLog = Join-Path $env:TEMP "relay.log"
|
| 34 |
+
$RelayErrLog = Join-Path $env:TEMP "relay-error.log"
|
| 35 |
+
$CfLog = Join-Path $env:TEMP "cloudflared-relay.log"
|
| 36 |
+
$UrlFile = Join-Path $env:TEMP "yt-relay-url.txt"
|
| 37 |
+
|
| 38 |
+
# ── 1. Kill old processes ─────────────────────────────────────────────────────
|
| 39 |
+
Write-Host "[1/5] Stopping old processes..." -ForegroundColor Yellow
|
| 40 |
+
Get-Process -Name "cloudflared" -ErrorAction SilentlyContinue | Stop-Process -Force
|
| 41 |
+
Get-CimInstance Win32_Process -Filter "Name='node.exe' AND CommandLine LIKE '%server.js%'" |
|
| 42 |
+
ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }
|
| 43 |
+
Start-Sleep -Seconds 2
|
| 44 |
+
Remove-Item $RelayLog,$RelayErrLog,$CfLog,$UrlFile -ErrorAction SilentlyContinue
|
| 45 |
+
|
| 46 |
+
# ── 2. Start Node relay (truly detached — no stdout redirect) ─────────────────
|
| 47 |
+
Write-Host "[2/5] Starting home relay (node)..." -ForegroundColor Cyan
|
| 48 |
+
$env:RELAY_SECRET = $Secret
|
| 49 |
+
$env:PORT = "8788"
|
| 50 |
+
$env:HOST = "127.0.0.1"
|
| 51 |
+
# If Node fails with CERT_HAS_EXPIRED / UNABLE_TO_VERIFY — filter MITM on YouTube TLS
|
| 52 |
+
if (-not $env:RELAY_INSECURE_TLS) {
|
| 53 |
+
$env:RELAY_INSECURE_TLS = [Environment]::GetEnvironmentVariable("RELAY_INSECURE_TLS", "User")
|
| 54 |
+
}
|
| 55 |
+
# Start-Process WITHOUT -RedirectStandardOutput = truly detached child process
|
| 56 |
+
# Pass env via cmd so child inherits RELAY_* cleanly
|
| 57 |
+
$nodeArgs = "server.js"
|
| 58 |
+
Start-Process -FilePath "node" `
|
| 59 |
+
-ArgumentList $nodeArgs `
|
| 60 |
+
-WorkingDirectory $RelayDir `
|
| 61 |
+
-WindowStyle Hidden
|
| 62 |
+
|
| 63 |
+
# ── 3. Wait for /health ───────────────────────────────────────────────────────
|
| 64 |
+
Write-Host "[3/5] Waiting for /health on :8788..." -ForegroundColor Cyan
|
| 65 |
+
$ok = $false
|
| 66 |
+
for ($i = 1; $i -le 20; $i++) {
|
| 67 |
+
Start-Sleep -Seconds 1
|
| 68 |
+
try {
|
| 69 |
+
$r = Invoke-RestMethod "http://127.0.0.1:8788/health" -TimeoutSec 2 -ErrorAction Stop
|
| 70 |
+
if ($r.ok) { $ok = $true; break }
|
| 71 |
+
} catch {}
|
| 72 |
+
Write-Host " ...($i/20)"
|
| 73 |
+
}
|
| 74 |
+
if (-not $ok) {
|
| 75 |
+
Write-Host "ERROR: relay did not come up on :8788" -ForegroundColor Red
|
| 76 |
+
exit 1
|
| 77 |
+
}
|
| 78 |
+
Write-Host "OK: relay is up." -ForegroundColor Green
|
| 79 |
+
|
| 80 |
+
# ── 4. Start cloudflared (truly detached) ────────────────────────────────────
|
| 81 |
+
Write-Host "[4/5] Starting cloudflared tunnel..." -ForegroundColor Cyan
|
| 82 |
+
Remove-Item $CfLog -ErrorAction SilentlyContinue
|
| 83 |
+
Start-Process -FilePath $CloudflaredExe `
|
| 84 |
+
-ArgumentList "tunnel","--protocol","http2","--url","http://127.0.0.1:8788","--logfile",$CfLog `
|
| 85 |
+
-WindowStyle Hidden
|
| 86 |
+
|
| 87 |
+
$tunnelUrl = $null
|
| 88 |
+
for ($i = 1; $i -le 25; $i++) {
|
| 89 |
+
Start-Sleep -Seconds 1
|
| 90 |
+
if (Test-Path $CfLog) {
|
| 91 |
+
$txt = Get-Content $CfLog -Raw -ErrorAction SilentlyContinue
|
| 92 |
+
if ($txt -match 'https://[a-z0-9-]+\.trycloudflare\.com') {
|
| 93 |
+
$tunnelUrl = $Matches[0]; break
|
| 94 |
+
}
|
| 95 |
+
}
|
| 96 |
+
Write-Host " ...waiting for tunnel URL ($i/25)"
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
if (-not $tunnelUrl) {
|
| 100 |
+
Write-Host "ERROR: no tunnel URL. Check: $CfLog" -ForegroundColor Red
|
| 101 |
+
Get-Content $CfLog -Tail 10 -ErrorAction SilentlyContinue
|
| 102 |
+
exit 1
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
$tunnelUrl | Set-Content $UrlFile
|
| 106 |
+
Write-Host "OK: tunnel = $tunnelUrl" -ForegroundColor Green
|
| 107 |
+
|
| 108 |
+
# ── 5. Update Worker secrets ─────────────────────────────────────────���────────
|
| 109 |
+
Write-Host "[5/5] Updating Cloudflare Worker secrets..." -ForegroundColor Cyan
|
| 110 |
+
if (-not $env:CLOUDFLARE_API_TOKEN) {
|
| 111 |
+
Write-Host "WARNING: CLOUDFLARE_API_TOKEN not set - skipping Worker update." -ForegroundColor Yellow
|
| 112 |
+
Write-Host " Run: echo '$tunnelUrl' | npx wrangler secret put YT_RELAY_URL" -ForegroundColor Gray
|
| 113 |
+
} else {
|
| 114 |
+
Push-Location $WranglerDir
|
| 115 |
+
$tunnelUrl | npx wrangler secret put YT_RELAY_URL
|
| 116 |
+
$Secret | npx wrangler secret put YT_RELAY_SECRET
|
| 117 |
+
Pop-Location
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
Write-Host ""
|
| 121 |
+
Write-Host "================================================" -ForegroundColor Green
|
| 122 |
+
Write-Host " SUCCESS - relay + tunnel running in background" -ForegroundColor Green
|
| 123 |
+
Write-Host " Tunnel : $tunnelUrl" -ForegroundColor Green
|
| 124 |
+
Write-Host " Relay : http://127.0.0.1:8788/health" -ForegroundColor Green
|
| 125 |
+
Write-Host " Stop : .\stop-relay.ps1" -ForegroundColor Green
|
| 126 |
+
Write-Host "================================================" -ForegroundColor Green
|
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Set WshShell = CreateObject("WScript.Shell")
|
| 2 |
+
WshShell.Run "powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -File ""C:\Users\user\Documents\מעקף פרוקסי 2.0\start-relay.ps1""", 0, False
|
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# stop-relay.ps1 - Stops Node relay and cloudflared Scheduled Tasks
|
| 2 |
+
|
| 3 |
+
Write-Host "Stopping EtrogRelay and EtrogTunnel tasks..." -ForegroundColor Yellow
|
| 4 |
+
schtasks /end /tn "EtrogRelay" 2>$null | Out-Null
|
| 5 |
+
schtasks /end /tn "EtrogTunnel" 2>$null | Out-Null
|
| 6 |
+
schtasks /delete /tn "EtrogRelay" /f 2>$null | Out-Null
|
| 7 |
+
schtasks /delete /tn "EtrogTunnel" /f 2>$null | Out-Null
|
| 8 |
+
Get-Process -Name "cloudflared" -ErrorAction SilentlyContinue | Stop-Process -Force
|
| 9 |
+
Get-CimInstance Win32_Process -Filter "Name='node.exe' AND CommandLine LIKE '%server.js%'" |
|
| 10 |
+
ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }
|
| 11 |
+
Write-Host "Done." -ForegroundColor Green
|
|
@@ -7,16 +7,78 @@
|
|
| 7 |
|
| 8 |
const PROXY_PATH = '/cdn/';
|
| 9 |
const KEY = [97, 110, 116, 105, 103, 114, 97, 118, 105, 116, 121]; // "antigravity"
|
| 10 |
-
const SW_VERSION =
|
| 11 |
|
| 12 |
// ── Access gate (password) ──────────────────────────────────────────────────
|
| 13 |
// Session cookie only (no Max-Age) → cleared when the browser is closed.
|
| 14 |
const GATE_PASSWORD = 'מוישי';
|
| 15 |
const GATE_COOKIE = '__proxy_gate';
|
| 16 |
const GATE_PEPPER = 'etrog-gate-v1-antigravity'; // used only to HMAC the session token
|
|
|
|
|
|
|
|
|
|
| 17 |
|
| 18 |
-
//
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
async function gateToken() {
|
| 22 |
const data = new TextEncoder().encode(GATE_PEPPER + '|' + GATE_PASSWORD);
|
|
@@ -45,7 +107,11 @@ function getCookieValue(cookieHeader, name) {
|
|
| 45 |
return '';
|
| 46 |
}
|
| 47 |
|
| 48 |
-
async function isGateOpen(request) {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
const got = getCookieValue(request.headers.get('cookie') || '', GATE_COOKIE);
|
| 50 |
if (!got) return false;
|
| 51 |
const expect = await gateToken();
|
|
@@ -149,7 +215,7 @@ async function handleGatePost(request, url) {
|
|
| 149 |
});
|
| 150 |
}
|
| 151 |
|
| 152 |
-
async function requireGate(request, url) {
|
| 153 |
// Login endpoint always reachable
|
| 154 |
if (url.pathname === '/__gate') {
|
| 155 |
if (request.method === 'POST') return handleGatePost(request, url);
|
|
@@ -166,7 +232,7 @@ async function requireGate(request, url) {
|
|
| 166 |
},
|
| 167 |
});
|
| 168 |
}
|
| 169 |
-
if (await isGateOpen(request)) return null; // allowed
|
| 170 |
|
| 171 |
// API / media / XHR: 401 JSON so clients don't get HTML as media
|
| 172 |
const dest = (request.headers.get('sec-fetch-dest') || '').toLowerCase();
|
|
@@ -226,10 +292,10 @@ function cacheSetFormats(videoId, pack) {
|
|
| 226 |
FORMAT_CACHE.delete(first);
|
| 227 |
}
|
| 228 |
}
|
| 229 |
-
async function getPlayerFormatsCached(videoId, request) {
|
| 230 |
const cached = cacheGetFormats(videoId);
|
| 231 |
if (cached) return { ...cached, fromCache: true };
|
| 232 |
-
const pack = await fetchPlayerFormats(videoId, request);
|
| 233 |
if (pack && pack.ok) cacheSetFormats(videoId, pack);
|
| 234 |
return pack;
|
| 235 |
}
|
|
@@ -452,7 +518,7 @@ function sealedStreamResponse(readable, status, origCt, extraHeaders = {}, range
|
|
| 452 |
h.set('cache-control', 'no-store');
|
| 453 |
h.set(
|
| 454 |
'Access-Control-Expose-Headers',
|
| 455 |
-
'Content-Length, Content-Range, Accept-Ranges, Content-Type, X-Proxy-Obfuscated, X-Original-Content-Type, X-Seal-Offset, X-Etrog-Mode, X-Etrog-Itag, X-Etrog-Bytes'
|
| 456 |
);
|
| 457 |
if (contentLength != null && contentLength !== '') h.set('content-length', String(contentLength));
|
| 458 |
const sealed = sealTransformStream(readable, rangeStart || 0);
|
|
@@ -787,7 +853,8 @@ self.addEventListener('fetch', function(e) {
|
|
| 787 |
|
| 788 |
// 3. Cross-origin requests: proxy through worker
|
| 789 |
if (requestUrl.origin !== self.location.origin) {
|
| 790 |
-
|
|
|
|
| 791 |
return;
|
| 792 |
}
|
| 793 |
if (shouldBlock(requestUrl.href)) {
|
|
@@ -842,7 +909,9 @@ self.addEventListener('fetch', function(e) {
|
|
| 842 |
requestUrl.pathname === '/api/i' ||
|
| 843 |
requestUrl.pathname === '/x/s' ||
|
| 844 |
requestUrl.pathname === '/x/t' ||
|
| 845 |
-
requestUrl.pathname.indexOf('/x/s/') === 0
|
|
|
|
|
|
|
| 846 |
e.respondWith(
|
| 847 |
fetch(e.request).then(function(res) {
|
| 848 |
// Error JSON (502/4xx) — never XOR-decrypt
|
|
@@ -854,7 +923,8 @@ self.addEventListener('fetch', function(e) {
|
|
| 854 |
var ct = (res.headers.get('content-type') || '').toLowerCase();
|
| 855 |
// Block Rimon MITM only when response is NOT from our worker markers
|
| 856 |
if (!mode && !sealed && (rimon.indexOf('BLOCK') !== -1 || srv.indexOf('lighttpd') !== -1)) {
|
| 857 |
-
|
|
|
|
| 858 |
status: 502,
|
| 859 |
headers: { 'content-type': 'application/json', 'cache-control': 'no-store' }
|
| 860 |
});
|
|
@@ -1821,9 +1891,9 @@ const UI_HTML = `<!DOCTYPE html>
|
|
| 1821 |
return;
|
| 1822 |
}
|
| 1823 |
document.cookie = '__proxy_target=' + encodeURIComponent(u.origin) + '; Path=/; max-age=31536000; SameSite=Lax; Secure';
|
| 1824 |
-
window.location.href = '/
|
| 1825 |
} catch (e) {
|
| 1826 |
-
window.location.href = '/
|
| 1827 |
}
|
| 1828 |
}
|
| 1829 |
|
|
@@ -2149,10 +2219,67 @@ async function importCookiesPost(request) {
|
|
| 2149 |
}
|
| 2150 |
}
|
| 2151 |
|
| 2152 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2153 |
|
| 2154 |
-
function makeSorryResponse(origin, videoId) {
|
| 2155 |
-
if (videoId) return youtubeNativePlayerPage(videoId, origin);
|
| 2156 |
return makeSorryResponsePlain(origin);
|
| 2157 |
}
|
| 2158 |
function makeSorryResponsePlain(origin) {
|
|
@@ -2423,7 +2550,7 @@ function isAllowedMediaDirectUrl(u) {
|
|
| 2423 |
}
|
| 2424 |
}
|
| 2425 |
|
| 2426 |
-
function youtubeNativePlayerPage(videoId, origin, title, prefillQ) {
|
| 2427 |
const safeId = String(videoId || '').replace(/[^\w-]/g, '');
|
| 2428 |
const q0 = String(prefillQ || '').replace(/[<>&"]/g, '');
|
| 2429 |
const pageTitle = (title || 'YouTube') + (safeId ? (' — ' + safeId) : '');
|
|
@@ -2715,7 +2842,7 @@ function youtubeNativePlayerPage(videoId, origin, title, prefillQ) {
|
|
| 2715 |
'</div>' +
|
| 2716 |
'</div>' +
|
| 2717 |
'<input id="vid" value="' + safeId.replace(/"/g, '') + '" tabindex="-1"/>' +
|
| 2718 |
-
'<script>\n' + PLAYER_CLIENT_JS + '\n</script></body></html>';
|
| 2719 |
|
| 2720 |
return new Response(html, {
|
| 2721 |
status: 200,
|
|
@@ -2807,13 +2934,12 @@ const PLAYER_CLIENT_JS = [
|
|
| 2807 |
" function mediaUrl(id,itag,dl,strict,directUrl){",
|
| 2808 |
" // Stealth path + XOR token only (no clear video id — harder for Etrog URL filters)",
|
| 2809 |
" var it=String(itag||currentItag||'18');",
|
| 2810 |
-
"
|
|
|
|
| 2811 |
" if(directUrl) payload.u=String(directUrl);",
|
| 2812 |
" var tok=encodeTok(payload);",
|
| 2813 |
-
" //
|
| 2814 |
-
" var u='/x/s/'+tok.slice(0,12)+'.bin?d='+encodeURIComponent(tok)+'&i='+encodeURIComponent(it);",
|
| 2815 |
-
" if(strict) u+='&strict=1';",
|
| 2816 |
-
" if(dl) u+='&dl=1';",
|
| 2817 |
" return u;",
|
| 2818 |
" }",
|
| 2819 |
" // Client-side Innertube via sandboxed iframe (Origin:null). CF Worker IPs get LOGIN_REQUIRED;",
|
|
@@ -2875,7 +3001,7 @@ const PLAYER_CLIENT_JS = [
|
|
| 2875 |
" }",
|
| 2876 |
" function ensureSw(){",
|
| 2877 |
" if(!('serviceWorker' in navigator)) return Promise.resolve();",
|
| 2878 |
-
" return navigator.serviceWorker.register('/sw.js?v=
|
| 2879 |
" return navigator.serviceWorker.ready;",
|
| 2880 |
" }).then(function(){",
|
| 2881 |
" if(navigator.serviceWorker.controller) return;",
|
|
@@ -3234,6 +3360,7 @@ const PLAYER_CLIENT_JS = [
|
|
| 3234 |
" var last=null;",
|
| 3235 |
" // 1) Client-side format resolve (Origin:null iframe) — bypasses CF LOGIN_REQUIRED",
|
| 3236 |
" try{",
|
|
|
|
| 3237 |
" setStatus('מאתר זרם מהדפדפן…');",
|
| 3238 |
" var cFormats=await resolveFormatsClient(id);",
|
| 3239 |
" if(gen!==playGen) return;",
|
|
@@ -4808,7 +4935,15 @@ async function tryHtmlPlayer(videoId, pageUrl, cookieHeader) {
|
|
| 4808 |
}
|
| 4809 |
}
|
| 4810 |
|
| 4811 |
-
async function fetchPlayerFormats(videoId, request) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4812 |
let lastPlayability = null;
|
| 4813 |
let lastStatus = 0;
|
| 4814 |
let lastSource = '';
|
|
@@ -5231,11 +5366,11 @@ async function serveYoutubeThumb(v, quality, request) {
|
|
| 5231 |
return sealedResponse(tiny, 200, 'image/jpeg', { 'Cache-Control': 'public, max-age=120', 'X-Etrog-Thumb': 'fallback' }, 0);
|
| 5232 |
}
|
| 5233 |
|
| 5234 |
-
async function handleYtStream(request, url) {
|
| 5235 |
let videoId = url.searchParams.get('v') || url.searchParams.get('id');
|
| 5236 |
let itag = url.searchParams.get('i') || url.searchParams.get('itag') || '18';
|
| 5237 |
let directMediaUrl = '';
|
| 5238 |
-
const token = url.searchParams.get('d');
|
| 5239 |
if (token) {
|
| 5240 |
const decoded = decodeMediaToken(token);
|
| 5241 |
if (decoded) {
|
|
@@ -5358,26 +5493,16 @@ async function handleYtStream(request, url) {
|
|
| 5358 |
},
|
| 5359 |
});
|
| 5360 |
const h = new Headers();
|
| 5361 |
-
applyCorsHeaders(h, request);
|
| 5362 |
const ctOut = origCt.startsWith('video/') || origCt.startsWith('audio/') ? origCt : 'video/mp4';
|
| 5363 |
-
h.set('content-type', ctOut);
|
| 5364 |
-
h.set('cache-control', 'no-store');
|
| 5365 |
-
h.set('X-Etrog-Mode', 'stream-unsealed');
|
| 5366 |
h.set('X-Etrog-Itag', String(prefer));
|
| 5367 |
h.set('X-Etrog-Source', 'client-direct-url');
|
| 5368 |
if (directMediaUrl) h.set('X-Etrog-Resolved-Url', directMediaUrl);
|
| 5369 |
-
h.set('accept-ranges', 'bytes');
|
| 5370 |
const cr = mediaRes.headers.get('content-range');
|
| 5371 |
const cl = mediaRes.headers.get('content-length');
|
| 5372 |
if (cr) h.set('content-range', cr);
|
| 5373 |
-
if (cl) h.set('content-length', cl);
|
| 5374 |
if (wantDownload) {
|
| 5375 |
h.set('content-disposition', 'attachment; filename="' + String(videoId).replace(/[^\w-]+/g, '_') + '.mp4"');
|
| 5376 |
}
|
| 5377 |
-
h.set(
|
| 5378 |
-
'Access-Control-Expose-Headers',
|
| 5379 |
-
'Content-Length, Content-Range, Accept-Ranges, Content-Type, Content-Disposition, X-Etrog-Mode, X-Etrog-Itag, X-Etrog-Source, X-Etrog-Resolved-Url'
|
| 5380 |
-
);
|
| 5381 |
// Cache this direct URL for the itag briefly
|
| 5382 |
try {
|
| 5383 |
const vid = String(videoId);
|
|
@@ -5393,7 +5518,7 @@ async function handleYtStream(request, url) {
|
|
| 5393 |
exp: Date.now() + 6 * 60 * 1000,
|
| 5394 |
};
|
| 5395 |
} catch (_) {}
|
| 5396 |
-
return
|
| 5397 |
} catch (_) {}
|
| 5398 |
}
|
| 5399 |
}
|
|
@@ -5421,13 +5546,13 @@ async function handleYtStream(request, url) {
|
|
| 5421 |
? [{ itag: String(prefer), url: directMediaUrl, mime: 'video/mp4', progressive: true }]
|
| 5422 |
: [];
|
| 5423 |
|
| 5424 |
-
let pack = await getPlayerFormatsCached(videoId, request);
|
| 5425 |
// Only one retry if cache was stale empty (not LOGIN_REQUIRED)
|
| 5426 |
if (!pack?.ok || !pack.formats.length) {
|
| 5427 |
const st0 = pack?.playability?.status || '';
|
| 5428 |
if (st0 !== 'LOGIN_REQUIRED' && !/LOGIN_REQUIRED/i.test(String(pack?.attemptsLog || ''))) {
|
| 5429 |
FORMAT_CACHE.delete(String(videoId));
|
| 5430 |
-
pack = await getPlayerFormatsCached(videoId, request);
|
| 5431 |
}
|
| 5432 |
}
|
| 5433 |
if (!pack?.ok || !pack.formats.length) {
|
|
@@ -5451,6 +5576,7 @@ async function handleYtStream(request, url) {
|
|
| 5451 |
lastStatus: pack?.lastStatus,
|
| 5452 |
lastSource: pack?.lastSource || '',
|
| 5453 |
attempts: pack?.attemptsLog || [],
|
|
|
|
| 5454 |
hasCookies: !!pack?.hasCookies,
|
| 5455 |
hasAuth: !!pack?.hasAuth,
|
| 5456 |
// Only now hand client the residential googlevideo URL (after server resolve failed)
|
|
@@ -5586,18 +5712,11 @@ async function handleYtStream(request, url) {
|
|
| 5586 |
const cr = mediaRes.headers.get('content-range');
|
| 5587 |
const cl = mediaRes.headers.get('content-length');
|
| 5588 |
const h = new Headers();
|
| 5589 |
-
applyCorsHeaders(h, request);
|
| 5590 |
-
h.set('content-type', origCt.startsWith('video/') ? origCt : 'video/mp4');
|
| 5591 |
-
h.set('cache-control', 'no-store');
|
| 5592 |
-
h.set('X-Etrog-Mode', 'stream-unsealed');
|
| 5593 |
h.set('X-Etrog-Itag', prefer);
|
| 5594 |
h.set('X-Etrog-Cache', 'media-url');
|
| 5595 |
h.set('X-Etrog-Resolved-Url', mediaHit.url);
|
| 5596 |
-
h.set('accept-ranges', 'bytes');
|
| 5597 |
if (cr) h.set('content-range', cr);
|
| 5598 |
-
|
| 5599 |
-
h.set('Access-Control-Expose-Headers', 'Content-Length, Content-Range, Accept-Ranges, Content-Type, X-Etrog-Mode, X-Etrog-Itag, X-Etrog-Cache, X-Etrog-Resolved-Url');
|
| 5600 |
-
return new Response(mediaRes.body, { status: mediaRes.status, headers: h });
|
| 5601 |
}
|
| 5602 |
} else if (vidCache?.byItag) {
|
| 5603 |
delete vidCache.byItag[String(prefer)];
|
|
@@ -5613,6 +5732,17 @@ async function handleYtStream(request, url) {
|
|
| 5613 |
];
|
| 5614 |
|
| 5615 |
async function tryFetchMedia(fmtUrl) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5616 |
// 2 UAs × cookies-off first — hard 5s timeout so CF Worker never hangs → 502
|
| 5617 |
for (const ua of mediaUAs) {
|
| 5618 |
for (const useCk of ytAuthForMedia ? [false, true] : [false]) {
|
|
@@ -5708,29 +5838,19 @@ async function handleYtStream(request, url) {
|
|
| 5708 |
// XOR-seal broke <video> when Service Worker was missing/outdated → error 4.
|
| 5709 |
// Thumbs remain sealed via /x/t.
|
| 5710 |
const h = new Headers();
|
| 5711 |
-
applyCorsHeaders(h, request);
|
| 5712 |
const ctOut = origCt.startsWith('video/') || origCt.startsWith('audio/') ? origCt : 'video/mp4';
|
| 5713 |
-
h.set('content-type', ctOut);
|
| 5714 |
-
h.set('cache-control', 'no-store');
|
| 5715 |
-
h.set('X-Etrog-Mode', 'stream-unsealed');
|
| 5716 |
h.set('X-Etrog-Itag', String(fmt.itag));
|
| 5717 |
if (fmt.url) h.set('X-Etrog-Resolved-Url', fmt.url);
|
| 5718 |
h.set('X-Etrog-Client', pack.client || '');
|
| 5719 |
h.set('X-Etrog-Len', contentLength ? String(contentLength) : 'unknown');
|
| 5720 |
-
h.set('accept-ranges', 'bytes');
|
| 5721 |
-
h.set(
|
| 5722 |
-
'Access-Control-Expose-Headers',
|
| 5723 |
-
'Content-Length, Content-Range, Accept-Ranges, Content-Type, X-Etrog-Mode, X-Etrog-Itag, X-Etrog-Bytes, X-Etrog-Len, X-Etrog-Resolved-Url'
|
| 5724 |
-
);
|
| 5725 |
if (wantDownload) {
|
| 5726 |
h.set(
|
| 5727 |
'content-disposition',
|
| 5728 |
'attachment; filename="' + String(videoId).replace(/[^\w-]+/g, '_') + '.mp4"'
|
| 5729 |
);
|
| 5730 |
}
|
| 5731 |
-
if (clHeader) h.set('content-length', clHeader);
|
| 5732 |
if (cr) h.set('content-range', cr);
|
| 5733 |
-
return
|
| 5734 |
}
|
| 5735 |
|
| 5736 |
// Pass 1: try ordered formats
|
|
@@ -5748,7 +5868,7 @@ async function handleYtStream(request, url) {
|
|
| 5748 |
if (lastMediaStatus === 403 || lastMediaStatus === 0 || lastMediaStatus === 451) {
|
| 5749 |
try {
|
| 5750 |
FORMAT_CACHE.delete(String(videoId));
|
| 5751 |
-
const pack2 = await getPlayerFormatsCached(videoId, request);
|
| 5752 |
if (pack2?.ok && pack2.formats?.length) {
|
| 5753 |
pack = pack2;
|
| 5754 |
const by2 = new Map(pack2.formats.map((f) => [String(f.itag), f]));
|
|
@@ -5886,7 +6006,7 @@ export default {
|
|
| 5886 |
// ── Password gate: blocks ALL routes (/, /ytp, /cdn/, /api/*, /sw.js, …) ──
|
| 5887 |
// Session cookie only — re-prompt after browser close. Direct links need same session.
|
| 5888 |
{
|
| 5889 |
-
const gateRes = await requireGate(request, url);
|
| 5890 |
if (gateRes) return gateRes;
|
| 5891 |
}
|
| 5892 |
|
|
@@ -5896,9 +6016,11 @@ export default {
|
|
| 5896 |
url.pathname === '/api/m' ||
|
| 5897 |
url.pathname === '/api/media' ||
|
| 5898 |
url.pathname === '/x/s' ||
|
| 5899 |
-
url.pathname.startsWith('/x/s/')
|
|
|
|
|
|
|
| 5900 |
) {
|
| 5901 |
-
return handleYtStream(request, url);
|
| 5902 |
}
|
| 5903 |
|
| 5904 |
// ── Stealth thumbs /x/t (token) + legacy /api/i ──
|
|
@@ -5932,7 +6054,7 @@ export default {
|
|
| 5932 |
} catch {}
|
| 5933 |
}
|
| 5934 |
// Allow empty v — player opens search UI (no forced sample video)
|
| 5935 |
-
return youtubeNativePlayerPage(v, url.origin, null, prefillQ);
|
| 5936 |
}
|
| 5937 |
|
| 5938 |
// ── Route: /api/health ──
|
|
@@ -6020,6 +6142,32 @@ export default {
|
|
| 6020 |
}
|
| 6021 |
}
|
| 6022 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6023 |
// ── Route: /sw.js ──
|
| 6024 |
if (url.pathname === '/sw.js') {
|
| 6025 |
return new Response(SW_JS, {
|
|
@@ -6047,6 +6195,12 @@ export default {
|
|
| 6047 |
return request.method === 'POST' ? importCookiesPost(request) : importCookiesForm();
|
| 6048 |
}
|
| 6049 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6050 |
// ג”€ג”€ Parse cookies ג”€ג”€
|
| 6051 |
const cookieHeader = request.headers.get('cookie') || '';
|
| 6052 |
const cookies = parseCookies(cookieHeader);
|
|
@@ -6234,7 +6388,7 @@ export default {
|
|
| 6234 |
const streamUrl = new URL(url.origin + '/api/m');
|
| 6235 |
streamUrl.searchParams.set('v', vid);
|
| 6236 |
streamUrl.searchParams.set('i', itag);
|
| 6237 |
-
return handleYtStream(request, streamUrl);
|
| 6238 |
}
|
| 6239 |
return new Response(null, {
|
| 6240 |
status: 204,
|
|
@@ -6463,7 +6617,7 @@ export default {
|
|
| 6463 |
if (location) {
|
| 6464 |
const absLoc = new URL(location, targetStr).toString();
|
| 6465 |
if (absLoc.includes('google.com/sorry/')) {
|
| 6466 |
-
return makeSorryResponse(url.origin, extractYoutubeVideoId(targetUrl) || extractYoutubeVideoId(absLoc));
|
| 6467 |
}
|
| 6468 |
const rh = new Headers(response.headers);
|
| 6469 |
rh.set('location', url.origin + PROXY_PATH + encodeUrl(absLoc));
|
|
@@ -6610,7 +6764,7 @@ export default {
|
|
| 6610 |
return new Response(body, { status: 502, headers: ah });
|
| 6611 |
}
|
| 6612 |
if (targetUrl.hostname.includes('google.com') && targetUrl.pathname.includes('/sorry/')) {
|
| 6613 |
-
return makeSorryResponse(url.origin);
|
| 6614 |
}
|
| 6615 |
const rewritten = rewriteHtmlResponse(response, targetUrl, url.origin, baseProxyTarget);
|
| 6616 |
let htmlText = await rewritten.text();
|
|
|
|
| 7 |
|
| 8 |
const PROXY_PATH = '/cdn/';
|
| 9 |
const KEY = [97, 110, 116, 105, 103, 114, 97, 118, 105, 116, 121]; // "antigravity"
|
| 10 |
+
const SW_VERSION = 101;
|
| 11 |
|
| 12 |
// ── Access gate (password) ──────────────────────────────────────────────────
|
| 13 |
// Session cookie only (no Max-Age) → cleared when the browser is closed.
|
| 14 |
const GATE_PASSWORD = 'מוישי';
|
| 15 |
const GATE_COOKIE = '__proxy_gate';
|
| 16 |
const GATE_PEPPER = 'etrog-gate-v1-antigravity'; // used only to HMAC the session token
|
| 17 |
+
// Built-in YouTube cookie slot. Leave EMPTY: fresh auth comes from the Home Relay
|
| 18 |
+
// (Firefox session) via YT_RELAY_URL. Do NOT paste a personal Google session here.
|
| 19 |
+
const BUILTIN_YOUTUBE_COOKIE = ""; // was a leaked live Google session — removed
|
| 20 |
|
| 21 |
+
// ── Home Relay Helpers ───────────────────────────────────────────────────────
|
| 22 |
+
function relayConfigured(env) {
|
| 23 |
+
return !!(env && env.YT_RELAY_URL && env.YT_RELAY_SECRET);
|
| 24 |
+
}
|
| 25 |
+
function relayBase(env) {
|
| 26 |
+
return String(env.YT_RELAY_URL || '').replace(/\/+$/, '');
|
| 27 |
+
}
|
| 28 |
+
async function relayPlayer(env, videoId, cookie, workerOrigin = null) {
|
| 29 |
+
let r, j, relayErr = null;
|
| 30 |
+
try {
|
| 31 |
+
r = await fetch(relayBase(env) + '/v1/yt/player', {
|
| 32 |
+
method: 'POST',
|
| 33 |
+
headers: {
|
| 34 |
+
authorization: 'Bearer ' + env.YT_RELAY_SECRET,
|
| 35 |
+
'content-type': 'application/json',
|
| 36 |
+
'Bypass-Tunnel-Reminder': 'true',
|
| 37 |
+
...(workerOrigin ? { 'X-Worker-Url': workerOrigin } : {}),
|
| 38 |
+
},
|
| 39 |
+
body: JSON.stringify({ videoId, cookie: cookie || '' }),
|
| 40 |
+
signal: AbortSignal.timeout(20000),
|
| 41 |
+
});
|
| 42 |
+
j = await r.json().catch((e) => { relayErr = 'json-parse:' + e.message; return null; });
|
| 43 |
+
} catch (e) {
|
| 44 |
+
return { ok: false, playability: null, attemptsLog: [], source: 'home-relay', relayErr: 'fetch-fail:' + e.message };
|
| 45 |
+
}
|
| 46 |
+
if (!j || !j.ok || !j.formats || !j.formats.length) {
|
| 47 |
+
return { ok: false, playability: j?.playability, attemptsLog: j?.attempts || [], source: 'home-relay', relayErr: relayErr || ('relay-http:' + (r?.status || '?')) };
|
| 48 |
+
}
|
| 49 |
+
return {
|
| 50 |
+
ok: true,
|
| 51 |
+
formats: j.formats.map((f) => ({
|
| 52 |
+
itag: f.itag,
|
| 53 |
+
url: f.url,
|
| 54 |
+
mime: (f.mimeType || '').split(';')[0],
|
| 55 |
+
progressive: !!f.progressive,
|
| 56 |
+
hasAudio: !!f.hasAudio,
|
| 57 |
+
hasVideo: !!f.hasVideo,
|
| 58 |
+
bitrate: f.bitrate || 0,
|
| 59 |
+
width: f.width || 0,
|
| 60 |
+
height: f.height || 0,
|
| 61 |
+
})),
|
| 62 |
+
playability: j.playability,
|
| 63 |
+
client: j.client,
|
| 64 |
+
hasCookies: !!cookie,
|
| 65 |
+
hasAuth: !!cookie,
|
| 66 |
+
attemptsLog: j.attempts || [],
|
| 67 |
+
via: 'home-relay',
|
| 68 |
+
};
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
async function relayFetch(env, targetUrl, init = {}) {
|
| 72 |
+
const u = new URL(relayBase(env) + '/v1/fetch');
|
| 73 |
+
u.searchParams.set('url', targetUrl);
|
| 74 |
+
const headers = {
|
| 75 |
+
authorization: 'Bearer ' + env.YT_RELAY_SECRET,
|
| 76 |
+
'Bypass-Tunnel-Reminder': 'true',
|
| 77 |
+
};
|
| 78 |
+
if (init.range) headers.range = init.range;
|
| 79 |
+
if (init.userAgent) headers['x-upstream-ua'] = init.userAgent;
|
| 80 |
+
return fetch(u.toString(), { headers, signal: init.signal });
|
| 81 |
+
}
|
| 82 |
|
| 83 |
async function gateToken() {
|
| 84 |
const data = new TextEncoder().encode(GATE_PEPPER + '|' + GATE_PASSWORD);
|
|
|
|
| 107 |
return '';
|
| 108 |
}
|
| 109 |
|
| 110 |
+
async function isGateOpen(request, env) {
|
| 111 |
+
const auth = request.headers.get('authorization');
|
| 112 |
+
if (auth && env && env.YT_RELAY_SECRET && auth === 'Bearer ' + env.YT_RELAY_SECRET) {
|
| 113 |
+
return true;
|
| 114 |
+
}
|
| 115 |
const got = getCookieValue(request.headers.get('cookie') || '', GATE_COOKIE);
|
| 116 |
if (!got) return false;
|
| 117 |
const expect = await gateToken();
|
|
|
|
| 215 |
});
|
| 216 |
}
|
| 217 |
|
| 218 |
+
async function requireGate(request, url, env) {
|
| 219 |
// Login endpoint always reachable
|
| 220 |
if (url.pathname === '/__gate') {
|
| 221 |
if (request.method === 'POST') return handleGatePost(request, url);
|
|
|
|
| 232 |
},
|
| 233 |
});
|
| 234 |
}
|
| 235 |
+
if (await isGateOpen(request, env)) return null; // allowed
|
| 236 |
|
| 237 |
// API / media / XHR: 401 JSON so clients don't get HTML as media
|
| 238 |
const dest = (request.headers.get('sec-fetch-dest') || '').toLowerCase();
|
|
|
|
| 292 |
FORMAT_CACHE.delete(first);
|
| 293 |
}
|
| 294 |
}
|
| 295 |
+
async function getPlayerFormatsCached(videoId, request, env) {
|
| 296 |
const cached = cacheGetFormats(videoId);
|
| 297 |
if (cached) return { ...cached, fromCache: true };
|
| 298 |
+
const pack = await fetchPlayerFormats(videoId, request, env);
|
| 299 |
if (pack && pack.ok) cacheSetFormats(videoId, pack);
|
| 300 |
return pack;
|
| 301 |
}
|
|
|
|
| 518 |
h.set('cache-control', 'no-store');
|
| 519 |
h.set(
|
| 520 |
'Access-Control-Expose-Headers',
|
| 521 |
+
'Content-Length, Content-Range, Accept-Ranges, Content-Type, Content-Disposition, X-Proxy-Obfuscated, X-Original-Content-Type, X-Seal-Offset, X-Etrog-Mode, X-Etrog-Itag, X-Etrog-Bytes, X-Etrog-Len, X-Etrog-Cache, X-Etrog-Client, X-Etrog-Resolved-Url'
|
| 522 |
);
|
| 523 |
if (contentLength != null && contentLength !== '') h.set('content-length', String(contentLength));
|
| 524 |
const sealed = sealTransformStream(readable, rangeStart || 0);
|
|
|
|
| 853 |
|
| 854 |
// 3. Cross-origin requests: proxy through worker
|
| 855 |
if (requestUrl.origin !== self.location.origin) {
|
| 856 |
+
var PASSTHROUGH = ['cdn.search.brave.com'];
|
| 857 |
+
if (PASSTHROUGH.indexOf(requestUrl.hostname) !== -1 || requestUrl.searchParams.has('etrog_direct') || requestUrl.href.indexOf('etrog_direct') !== -1) {
|
| 858 |
return;
|
| 859 |
}
|
| 860 |
if (shouldBlock(requestUrl.href)) {
|
|
|
|
| 909 |
requestUrl.pathname === '/api/i' ||
|
| 910 |
requestUrl.pathname === '/x/s' ||
|
| 911 |
requestUrl.pathname === '/x/t' ||
|
| 912 |
+
requestUrl.pathname.indexOf('/x/s/') === 0 ||
|
| 913 |
+
requestUrl.pathname === '/assets/chunk' ||
|
| 914 |
+
requestUrl.pathname.indexOf('/assets/chunk-') === 0) {
|
| 915 |
e.respondWith(
|
| 916 |
fetch(e.request).then(function(res) {
|
| 917 |
// Error JSON (502/4xx) — never XOR-decrypt
|
|
|
|
| 923 |
var ct = (res.headers.get('content-type') || '').toLowerCase();
|
| 924 |
// Block Rimon MITM only when response is NOT from our worker markers
|
| 925 |
if (!mode && !sealed && (rimon.indexOf('BLOCK') !== -1 || srv.indexOf('lighttpd') !== -1)) {
|
| 926 |
+
console.error('[SW Filter Block] URL:', e.request.url, 'Rimon:', rimon, 'Server:', srv);
|
| 927 |
+
return new Response(JSON.stringify({ error: 'filter_block', hint: 'Etrog/Rimon intercepted stream: ' + e.request.url }), {
|
| 928 |
status: 502,
|
| 929 |
headers: { 'content-type': 'application/json', 'cache-control': 'no-store' }
|
| 930 |
});
|
|
|
|
| 1891 |
return;
|
| 1892 |
}
|
| 1893 |
document.cookie = '__proxy_target=' + encodeURIComponent(u.origin) + '; Path=/; max-age=31536000; SameSite=Lax; Secure';
|
| 1894 |
+
window.location.href = '/gate?to=' + encodeURIComponent(encodeUrl(u.href));
|
| 1895 |
} catch (e) {
|
| 1896 |
+
window.location.href = '/gate?to=' + encodeURIComponent(encodeUrl(url));
|
| 1897 |
}
|
| 1898 |
}
|
| 1899 |
|
|
|
|
| 2219 |
}
|
| 2220 |
}
|
| 2221 |
|
| 2222 |
+
function makeGateResponse(toEncoded, origin) {
|
| 2223 |
+
return new Response(`<!DOCTYPE html>
|
| 2224 |
+
<html lang="he" dir="rtl">
|
| 2225 |
+
<head>
|
| 2226 |
+
<meta charset="UTF-8">
|
| 2227 |
+
<title>מתחבר...</title>
|
| 2228 |
+
<link href="https://fonts.googleapis.com/css2?family=Assistant:wght@400;600;700&display=swap" rel="stylesheet">
|
| 2229 |
+
<style>
|
| 2230 |
+
body {
|
| 2231 |
+
background: #07070f; color: #f8fafc; font-family: 'Assistant', sans-serif;
|
| 2232 |
+
display: flex; flex-direction: column; justify-content: center; align-items: center; min-height: 100vh; margin: 0;
|
| 2233 |
+
}
|
| 2234 |
+
.spinner {
|
| 2235 |
+
border: 3px solid rgba(99, 102, 241, 0.1); width: 50px; height: 50px;
|
| 2236 |
+
border-radius: 50%; border-left-color: #6366f1; animation: spin 1s linear infinite;
|
| 2237 |
+
margin-bottom: 1.5rem;
|
| 2238 |
+
}
|
| 2239 |
+
h1 { font-size: 1.5rem; font-weight: 700; margin: 0; }
|
| 2240 |
+
p { color: #94a3b8; font-size: 0.95rem; margin: 0.5rem 0 0 0; }
|
| 2241 |
+
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
|
| 2242 |
+
</style>
|
| 2243 |
+
</head>
|
| 2244 |
+
<body>
|
| 2245 |
+
<div class="spinner"></div>
|
| 2246 |
+
<h1>מכין חיבור מאובטח...</h1>
|
| 2247 |
+
<p>אנא המתן רגע קט</p>
|
| 2248 |
+
<script>
|
| 2249 |
+
var to = \`\${toEncoded}\`;
|
| 2250 |
+
var targetUrl = '/cdn/' + to;
|
| 2251 |
+
|
| 2252 |
+
function go() {
|
| 2253 |
+
window.location.replace(targetUrl);
|
| 2254 |
+
}
|
| 2255 |
+
|
| 2256 |
+
if ('serviceWorker' in navigator) {
|
| 2257 |
+
navigator.serviceWorker.register('/sw.js?v=${SW_VERSION}', { scope: '/' }).then(function() {
|
| 2258 |
+
return navigator.serviceWorker.ready;
|
| 2259 |
+
}).then(function() {
|
| 2260 |
+
if (navigator.serviceWorker.controller) {
|
| 2261 |
+
go();
|
| 2262 |
+
} else {
|
| 2263 |
+
navigator.serviceWorker.addEventListener('controllerchange', function() {
|
| 2264 |
+
go();
|
| 2265 |
+
});
|
| 2266 |
+
setTimeout(go, 1200);
|
| 2267 |
+
}
|
| 2268 |
+
}).catch(function() {
|
| 2269 |
+
go();
|
| 2270 |
+
});
|
| 2271 |
+
} else {
|
| 2272 |
+
go();
|
| 2273 |
+
}
|
| 2274 |
+
</script>
|
| 2275 |
+
</body>
|
| 2276 |
+
</html>`, { headers: { 'Content-Type': 'text/html; charset=utf-8' } });
|
| 2277 |
+
}
|
| 2278 |
+
|
| 2279 |
+
// ─── Section 9: Error / Sorry Page ───
|
| 2280 |
|
| 2281 |
+
function makeSorryResponse(origin, videoId, env) {
|
| 2282 |
+
if (videoId) return youtubeNativePlayerPage(videoId, origin, null, '', env);
|
| 2283 |
return makeSorryResponsePlain(origin);
|
| 2284 |
}
|
| 2285 |
function makeSorryResponsePlain(origin) {
|
|
|
|
| 2550 |
}
|
| 2551 |
}
|
| 2552 |
|
| 2553 |
+
function youtubeNativePlayerPage(videoId, origin, title, prefillQ, env) {
|
| 2554 |
const safeId = String(videoId || '').replace(/[^\w-]/g, '');
|
| 2555 |
const q0 = String(prefillQ || '').replace(/[<>&"]/g, '');
|
| 2556 |
const pageTitle = (title || 'YouTube') + (safeId ? (' — ' + safeId) : '');
|
|
|
|
| 2842 |
'</div>' +
|
| 2843 |
'</div>' +
|
| 2844 |
'<input id="vid" value="' + safeId.replace(/"/g, '') + '" tabindex="-1"/>' +
|
| 2845 |
+
'<script>\nwindow.YT_RELAY_ACTIVE = ' + JSON.stringify(relayConfigured(env)) + ';\nwindow.SW_VERSION = ' + SW_VERSION + ';\n' + PLAYER_CLIENT_JS + '\n</script></body></html>';
|
| 2846 |
|
| 2847 |
return new Response(html, {
|
| 2848 |
status: 200,
|
|
|
|
| 2934 |
" function mediaUrl(id,itag,dl,strict,directUrl){",
|
| 2935 |
" // Stealth path + XOR token only (no clear video id — harder for Etrog URL filters)",
|
| 2936 |
" var it=String(itag||currentItag||'18');",
|
| 2937 |
+
" // itag, strict, dl are encoded inside payload (inside tok) — NOT exposed as URL params",
|
| 2938 |
+
" var payload={v:id,i:it,t:Date.now(),s:strict?1:0,d:dl?1:0};",
|
| 2939 |
" if(directUrl) payload.u=String(directUrl);",
|
| 2940 |
" var tok=encodeTok(payload);",
|
| 2941 |
+
" // Short generic path — no YouTube-specific params visible to network filters",
|
| 2942 |
+
" var u='/x/s/'+tok.slice(0,12)+'.bin?d='+encodeURIComponent(tok)+'&i='+encodeURIComponent(String(it.length));",
|
|
|
|
|
|
|
| 2943 |
" return u;",
|
| 2944 |
" }",
|
| 2945 |
" // Client-side Innertube via sandboxed iframe (Origin:null). CF Worker IPs get LOGIN_REQUIRED;",
|
|
|
|
| 3001 |
" }",
|
| 3002 |
" function ensureSw(){",
|
| 3003 |
" if(!('serviceWorker' in navigator)) return Promise.resolve();",
|
| 3004 |
+
" return navigator.serviceWorker.register('/sw.js?v=' + (window.SW_VERSION || '98'),{scope:'/'}).then(function(){",
|
| 3005 |
" return navigator.serviceWorker.ready;",
|
| 3006 |
" }).then(function(){",
|
| 3007 |
" if(navigator.serviceWorker.controller) return;",
|
|
|
|
| 3360 |
" var last=null;",
|
| 3361 |
" // 1) Client-side format resolve (Origin:null iframe) — bypasses CF LOGIN_REQUIRED",
|
| 3362 |
" try{",
|
| 3363 |
+
" if(window.YT_RELAY_ACTIVE) throw new Error('relay active');",
|
| 3364 |
" setStatus('מאתר זרם מהדפדפן…');",
|
| 3365 |
" var cFormats=await resolveFormatsClient(id);",
|
| 3366 |
" if(gen!==playGen) return;",
|
|
|
|
| 4935 |
}
|
| 4936 |
}
|
| 4937 |
|
| 4938 |
+
async function fetchPlayerFormats(videoId, request, env) {
|
| 4939 |
+
if (relayConfigured(env)) {
|
| 4940 |
+
const ytCookies = await getYoutubeCookieHeader(request);
|
| 4941 |
+
let workerOrigin = '';
|
| 4942 |
+
try { workerOrigin = new URL(request.url).origin; } catch (_) {}
|
| 4943 |
+
const pack = await relayPlayer(env, videoId, ytCookies || '', workerOrigin);
|
| 4944 |
+
if (pack.ok) return pack;
|
| 4945 |
+
}
|
| 4946 |
+
|
| 4947 |
let lastPlayability = null;
|
| 4948 |
let lastStatus = 0;
|
| 4949 |
let lastSource = '';
|
|
|
|
| 5366 |
return sealedResponse(tiny, 200, 'image/jpeg', { 'Cache-Control': 'public, max-age=120', 'X-Etrog-Thumb': 'fallback' }, 0);
|
| 5367 |
}
|
| 5368 |
|
| 5369 |
+
async function handleYtStream(request, url, env) {
|
| 5370 |
let videoId = url.searchParams.get('v') || url.searchParams.get('id');
|
| 5371 |
let itag = url.searchParams.get('i') || url.searchParams.get('itag') || '18';
|
| 5372 |
let directMediaUrl = '';
|
| 5373 |
+
const token = url.searchParams.get('hash') || url.searchParams.get('d');
|
| 5374 |
if (token) {
|
| 5375 |
const decoded = decodeMediaToken(token);
|
| 5376 |
if (decoded) {
|
|
|
|
| 5493 |
},
|
| 5494 |
});
|
| 5495 |
const h = new Headers();
|
|
|
|
| 5496 |
const ctOut = origCt.startsWith('video/') || origCt.startsWith('audio/') ? origCt : 'video/mp4';
|
|
|
|
|
|
|
|
|
|
| 5497 |
h.set('X-Etrog-Itag', String(prefer));
|
| 5498 |
h.set('X-Etrog-Source', 'client-direct-url');
|
| 5499 |
if (directMediaUrl) h.set('X-Etrog-Resolved-Url', directMediaUrl);
|
|
|
|
| 5500 |
const cr = mediaRes.headers.get('content-range');
|
| 5501 |
const cl = mediaRes.headers.get('content-length');
|
| 5502 |
if (cr) h.set('content-range', cr);
|
|
|
|
| 5503 |
if (wantDownload) {
|
| 5504 |
h.set('content-disposition', 'attachment; filename="' + String(videoId).replace(/[^\w-]+/g, '_') + '.mp4"');
|
| 5505 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5506 |
// Cache this direct URL for the itag briefly
|
| 5507 |
try {
|
| 5508 |
const vid = String(videoId);
|
|
|
|
| 5518 |
exp: Date.now() + 6 * 60 * 1000,
|
| 5519 |
};
|
| 5520 |
} catch (_) {}
|
| 5521 |
+
return sealedStreamResponse(plainBody, mediaRes.status === 206 ? 206 : 200, ctOut, h, parseRangeStart(clientRange), cl);
|
| 5522 |
} catch (_) {}
|
| 5523 |
}
|
| 5524 |
}
|
|
|
|
| 5546 |
? [{ itag: String(prefer), url: directMediaUrl, mime: 'video/mp4', progressive: true }]
|
| 5547 |
: [];
|
| 5548 |
|
| 5549 |
+
let pack = await getPlayerFormatsCached(videoId, request, env);
|
| 5550 |
// Only one retry if cache was stale empty (not LOGIN_REQUIRED)
|
| 5551 |
if (!pack?.ok || !pack.formats.length) {
|
| 5552 |
const st0 = pack?.playability?.status || '';
|
| 5553 |
if (st0 !== 'LOGIN_REQUIRED' && !/LOGIN_REQUIRED/i.test(String(pack?.attemptsLog || ''))) {
|
| 5554 |
FORMAT_CACHE.delete(String(videoId));
|
| 5555 |
+
pack = await getPlayerFormatsCached(videoId, request, env);
|
| 5556 |
}
|
| 5557 |
}
|
| 5558 |
if (!pack?.ok || !pack.formats.length) {
|
|
|
|
| 5576 |
lastStatus: pack?.lastStatus,
|
| 5577 |
lastSource: pack?.lastSource || '',
|
| 5578 |
attempts: pack?.attemptsLog || [],
|
| 5579 |
+
relayErr: pack?.relayErr || null,
|
| 5580 |
hasCookies: !!pack?.hasCookies,
|
| 5581 |
hasAuth: !!pack?.hasAuth,
|
| 5582 |
// Only now hand client the residential googlevideo URL (after server resolve failed)
|
|
|
|
| 5712 |
const cr = mediaRes.headers.get('content-range');
|
| 5713 |
const cl = mediaRes.headers.get('content-length');
|
| 5714 |
const h = new Headers();
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5715 |
h.set('X-Etrog-Itag', prefer);
|
| 5716 |
h.set('X-Etrog-Cache', 'media-url');
|
| 5717 |
h.set('X-Etrog-Resolved-Url', mediaHit.url);
|
|
|
|
| 5718 |
if (cr) h.set('content-range', cr);
|
| 5719 |
+
return sealedStreamResponse(mediaRes.body, mediaRes.status, origCt.startsWith('video/') ? origCt : 'video/mp4', h, parseRangeStart(clientRange), cl);
|
|
|
|
|
|
|
| 5720 |
}
|
| 5721 |
} else if (vidCache?.byItag) {
|
| 5722 |
delete vidCache.byItag[String(prefer)];
|
|
|
|
| 5732 |
];
|
| 5733 |
|
| 5734 |
async function tryFetchMedia(fmtUrl) {
|
| 5735 |
+
if (relayConfigured(env)) {
|
| 5736 |
+
try {
|
| 5737 |
+
return await relayFetch(env, fmtUrl, {
|
| 5738 |
+
range: clientRange || undefined,
|
| 5739 |
+
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
| 5740 |
+
signal: AbortSignal.timeout(8000),
|
| 5741 |
+
});
|
| 5742 |
+
} catch (_) {
|
| 5743 |
+
return null;
|
| 5744 |
+
}
|
| 5745 |
+
}
|
| 5746 |
// 2 UAs × cookies-off first — hard 5s timeout so CF Worker never hangs → 502
|
| 5747 |
for (const ua of mediaUAs) {
|
| 5748 |
for (const useCk of ytAuthForMedia ? [false, true] : [false]) {
|
|
|
|
| 5838 |
// XOR-seal broke <video> when Service Worker was missing/outdated → error 4.
|
| 5839 |
// Thumbs remain sealed via /x/t.
|
| 5840 |
const h = new Headers();
|
|
|
|
| 5841 |
const ctOut = origCt.startsWith('video/') || origCt.startsWith('audio/') ? origCt : 'video/mp4';
|
|
|
|
|
|
|
|
|
|
| 5842 |
h.set('X-Etrog-Itag', String(fmt.itag));
|
| 5843 |
if (fmt.url) h.set('X-Etrog-Resolved-Url', fmt.url);
|
| 5844 |
h.set('X-Etrog-Client', pack.client || '');
|
| 5845 |
h.set('X-Etrog-Len', contentLength ? String(contentLength) : 'unknown');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5846 |
if (wantDownload) {
|
| 5847 |
h.set(
|
| 5848 |
'content-disposition',
|
| 5849 |
'attachment; filename="' + String(videoId).replace(/[^\w-]+/g, '_') + '.mp4"'
|
| 5850 |
);
|
| 5851 |
}
|
|
|
|
| 5852 |
if (cr) h.set('content-range', cr);
|
| 5853 |
+
return sealedStreamResponse(plainBody, statusOut, ctOut, h, parseRangeStart(clientRange), clHeader);
|
| 5854 |
}
|
| 5855 |
|
| 5856 |
// Pass 1: try ordered formats
|
|
|
|
| 5868 |
if (lastMediaStatus === 403 || lastMediaStatus === 0 || lastMediaStatus === 451) {
|
| 5869 |
try {
|
| 5870 |
FORMAT_CACHE.delete(String(videoId));
|
| 5871 |
+
const pack2 = await getPlayerFormatsCached(videoId, request, env);
|
| 5872 |
if (pack2?.ok && pack2.formats?.length) {
|
| 5873 |
pack = pack2;
|
| 5874 |
const by2 = new Map(pack2.formats.map((f) => [String(f.itag), f]));
|
|
|
|
| 6006 |
// ── Password gate: blocks ALL routes (/, /ytp, /cdn/, /api/*, /sw.js, …) ──
|
| 6007 |
// Session cookie only — re-prompt after browser close. Direct links need same session.
|
| 6008 |
{
|
| 6009 |
+
const gateRes = await requireGate(request, url, env);
|
| 6010 |
if (gateRes) return gateRes;
|
| 6011 |
}
|
| 6012 |
|
|
|
|
| 6016 |
url.pathname === '/api/m' ||
|
| 6017 |
url.pathname === '/api/media' ||
|
| 6018 |
url.pathname === '/x/s' ||
|
| 6019 |
+
url.pathname.startsWith('/x/s/') ||
|
| 6020 |
+
url.pathname === '/assets/chunk' ||
|
| 6021 |
+
url.pathname.startsWith('/assets/chunk-')
|
| 6022 |
) {
|
| 6023 |
+
return handleYtStream(request, url, env);
|
| 6024 |
}
|
| 6025 |
|
| 6026 |
// ── Stealth thumbs /x/t (token) + legacy /api/i ──
|
|
|
|
| 6054 |
} catch {}
|
| 6055 |
}
|
| 6056 |
// Allow empty v — player opens search UI (no forced sample video)
|
| 6057 |
+
return youtubeNativePlayerPage(v, url.origin, null, prefillQ, env);
|
| 6058 |
}
|
| 6059 |
|
| 6060 |
// ── Route: /api/health ──
|
|
|
|
| 6142 |
}
|
| 6143 |
}
|
| 6144 |
|
| 6145 |
+
// ── Route: /api/resolve-helper ──
|
| 6146 |
+
if (url.pathname === '/api/resolve-helper') {
|
| 6147 |
+
if (request.method !== 'POST') {
|
| 6148 |
+
return new Response('method not allowed', { status: 405 });
|
| 6149 |
+
}
|
| 6150 |
+
const auth = request.headers.get('authorization');
|
| 6151 |
+
if (auth !== 'Bearer ' + env.YT_RELAY_SECRET) {
|
| 6152 |
+
return new Response('unauthorized', { status: 401 });
|
| 6153 |
+
}
|
| 6154 |
+
try {
|
| 6155 |
+
const body = await request.json();
|
| 6156 |
+
const videoId = body.videoId;
|
| 6157 |
+
const client = body.client;
|
| 6158 |
+
const cookie = body.cookie || '';
|
| 6159 |
+
const res = await tryInnertubeClient(videoId, client, cookie);
|
| 6160 |
+
return new Response(JSON.stringify(res), {
|
| 6161 |
+
headers: { 'content-type': 'application/json; charset=utf-8' }
|
| 6162 |
+
});
|
| 6163 |
+
} catch (e) {
|
| 6164 |
+
return new Response(JSON.stringify({ ok: false, error: e.message }), {
|
| 6165 |
+
status: 500,
|
| 6166 |
+
headers: { 'content-type': 'application/json' }
|
| 6167 |
+
});
|
| 6168 |
+
}
|
| 6169 |
+
}
|
| 6170 |
+
|
| 6171 |
// ── Route: /sw.js ──
|
| 6172 |
if (url.pathname === '/sw.js') {
|
| 6173 |
return new Response(SW_JS, {
|
|
|
|
| 6195 |
return request.method === 'POST' ? importCookiesPost(request) : importCookiesForm();
|
| 6196 |
}
|
| 6197 |
|
| 6198 |
+
// ── Route: /gate (Pre-SW gate for SPAs) ──
|
| 6199 |
+
if (url.pathname === '/gate') {
|
| 6200 |
+
const to = url.searchParams.get('to') || '';
|
| 6201 |
+
return makeGateResponse(to, url.origin);
|
| 6202 |
+
}
|
| 6203 |
+
|
| 6204 |
// ג”€ג”€ Parse cookies ג”€ג”€
|
| 6205 |
const cookieHeader = request.headers.get('cookie') || '';
|
| 6206 |
const cookies = parseCookies(cookieHeader);
|
|
|
|
| 6388 |
const streamUrl = new URL(url.origin + '/api/m');
|
| 6389 |
streamUrl.searchParams.set('v', vid);
|
| 6390 |
streamUrl.searchParams.set('i', itag);
|
| 6391 |
+
return handleYtStream(request, streamUrl, env);
|
| 6392 |
}
|
| 6393 |
return new Response(null, {
|
| 6394 |
status: 204,
|
|
|
|
| 6617 |
if (location) {
|
| 6618 |
const absLoc = new URL(location, targetStr).toString();
|
| 6619 |
if (absLoc.includes('google.com/sorry/')) {
|
| 6620 |
+
return makeSorryResponse(url.origin, extractYoutubeVideoId(targetUrl) || extractYoutubeVideoId(absLoc), env);
|
| 6621 |
}
|
| 6622 |
const rh = new Headers(response.headers);
|
| 6623 |
rh.set('location', url.origin + PROXY_PATH + encodeUrl(absLoc));
|
|
|
|
| 6764 |
return new Response(body, { status: 502, headers: ah });
|
| 6765 |
}
|
| 6766 |
if (targetUrl.hostname.includes('google.com') && targetUrl.pathname.includes('/sorry/')) {
|
| 6767 |
+
return makeSorryResponse(url.origin, null, env);
|
| 6768 |
}
|
| 6769 |
const rewritten = rewriteHtmlResponse(response, targetUrl, url.origin, baseProxyTarget);
|
| 6770 |
let htmlText = await rewritten.text();
|