(proxy)
| 226 | } |
| 227 | |
| 228 | function proxyKey(proxy) { |
| 229 | if (!proxy || !proxy.host) return 'default'; |
| 230 | // Sanitize to [A-Za-z0-9_] — the key flows into a filesystem path |
| 231 | // (`${LS_DATA_DIR}/${key}`) and a shell-quoted mkdir, so strip any |
| 232 | // special character that could slip past execSync's naive quoting. |
| 233 | const safeHost = proxy.host.replace(/[^a-zA-Z0-9]/g, '_'); |
| 234 | const safePort = String(proxy.port || 8080).replace(/[^0-9]/g, ''); |
| 235 | let key = `px_${safeHost}_${safePort}`; |
| 236 | // v2.0.68/v2.0.71 (#119 CharwinYAO): sticky-IP proxy services (ipwo, |
| 237 | // lunaproxy, smartproxy, oxylabs, bright data) embed a per-IP session |
| 238 | // id inside the username. Default behaviour pools all sticky sessions |
| 239 | // onto one LS instance (host:port identical) so multiple accounts |
| 240 | // share one LS sessionId / Windsurf fingerprint, tripping upstream's |
| 241 | // 30-min rate limit even though egress IPs differ. |
| 242 | // |
| 243 | // Auto-on (v2.0.71): username matches a known sticky session pattern |
| 244 | // (`_sid_`, `-session-`, `-sticky`, `-sessid-`, `+ws_`) → segregate. |
| 245 | // Static-IP proxies don't carry these markers so memory stays bounded. |
| 246 | // Operator can force-on (`WINDSURFAPI_LS_PER_PROXY_USER=1`) to |
| 247 | // segregate every distinct username, or force-off (`=0`) to disable. |
| 248 | let segregateByUser = false; |
| 249 | if (process.env.WINDSURFAPI_LS_PER_PROXY_USER === '0') { |
| 250 | segregateByUser = false; |
| 251 | } else if (process.env.WINDSURFAPI_LS_PER_PROXY_USER === '1') { |
| 252 | segregateByUser = !!proxy.username; |
| 253 | } else if (proxy.username && isStickyUsername(proxy.username)) { |
| 254 | segregateByUser = true; |
| 255 | } |
| 256 | if (segregateByUser) { |
| 257 | // Cap user portion at 32 chars to keep filesystem paths sane on |
| 258 | // Windows where MAX_PATH still bites; sticky session ids are |
| 259 | // typically <16 chars anyway. |
| 260 | const safeUser = String(proxy.username).replace(/[^a-zA-Z0-9]/g, '_').slice(0, 32); |
| 261 | if (safeUser) key += `_u${safeUser}`; |
| 262 | } |
| 263 | return key; |
| 264 | } |
| 265 | |
| 266 | function publicLsKey(key) { |
| 267 | const s = String(key || ''); |
no test coverage detected