@sip-protocol/sdk 0.7.2 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +267 -0
- package/dist/{TransportWebUSB-TQ7WZ4LE.mjs → TransportWebUSB-YQMAGJAJ.mjs} +12 -9
- package/dist/browser.d.mts +10 -4
- package/dist/browser.d.ts +10 -4
- package/dist/browser.js +48874 -18336
- package/dist/browser.mjs +674 -48
- package/dist/chunk-4GRJ5MAW.mjs +152 -0
- package/dist/chunk-5D7A3L3W.mjs +717 -0
- package/dist/chunk-64AYA5F5.mjs +7834 -0
- package/dist/chunk-GMDGB22A.mjs +379 -0
- package/dist/chunk-I534WKN7.mjs +328 -0
- package/dist/chunk-IBZVA5Y7.mjs +1003 -0
- package/dist/chunk-PRRZAWJE.mjs +223 -0
- package/dist/{chunk-UJCSKKID.mjs → chunk-XGB3TDIC.mjs} +13 -1
- package/dist/chunk-YWGJ77A2.mjs +33806 -0
- package/dist/{chunk-6WGN57S2.mjs → chunk-Z3K7W5S3.mjs} +48 -0
- package/dist/constants-LHAAUC2T.mjs +51 -0
- package/dist/dist-2OGQ7FED.mjs +3957 -0
- package/dist/dist-IFHPYLDX.mjs +254 -0
- package/dist/fulfillment_proof-ANHVPKTB.mjs +21 -0
- package/dist/funding_proof-ICFZ5LHY.mjs +21 -0
- package/dist/index-DXh2IGkz.d.ts +24681 -0
- package/dist/index-DeE1ZzA4.d.mts +24681 -0
- package/dist/index.d.mts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +48676 -17318
- package/dist/index.mjs +583 -19
- package/dist/interface-Bf7w1PLW.d.mts +679 -0
- package/dist/interface-Bf7w1PLW.d.ts +679 -0
- package/dist/{noir-DKfEzWy9.d.mts → noir-kzbLVTei.d.mts} +31 -21
- package/dist/{noir-DKfEzWy9.d.ts → noir-kzbLVTei.d.ts} +31 -21
- package/dist/proofs/halo2.d.mts +151 -0
- package/dist/proofs/halo2.d.ts +151 -0
- package/dist/proofs/halo2.js +350 -0
- package/dist/proofs/halo2.mjs +11 -0
- package/dist/proofs/kimchi.d.mts +160 -0
- package/dist/proofs/kimchi.d.ts +160 -0
- package/dist/proofs/kimchi.js +431 -0
- package/dist/proofs/kimchi.mjs +13 -0
- package/dist/proofs/noir.d.mts +1 -1
- package/dist/proofs/noir.d.ts +1 -1
- package/dist/proofs/noir.js +74 -18
- package/dist/proofs/noir.mjs +84 -24
- package/dist/solana-U3MEGU7W.mjs +280 -0
- package/dist/validity_proof-3POXLPNY.mjs +21 -0
- package/package.json +54 -21
- package/src/adapters/index.ts +41 -0
- package/src/adapters/jupiter.ts +571 -0
- package/src/adapters/near-intents.ts +135 -0
- package/src/advisor/advisor.ts +653 -0
- package/src/advisor/index.ts +54 -0
- package/src/advisor/tools.ts +303 -0
- package/src/advisor/types.ts +164 -0
- package/src/chains/ethereum/announcement.ts +536 -0
- package/src/chains/ethereum/bnb-optimizations.ts +474 -0
- package/src/chains/ethereum/commitment.ts +522 -0
- package/src/chains/ethereum/constants.ts +462 -0
- package/src/chains/ethereum/deployment.ts +596 -0
- package/src/chains/ethereum/gas-estimation.ts +538 -0
- package/src/chains/ethereum/index.ts +268 -0
- package/src/chains/ethereum/optimizations.ts +614 -0
- package/src/chains/ethereum/privacy-adapter.ts +855 -0
- package/src/chains/ethereum/registry.ts +584 -0
- package/src/chains/ethereum/rpc.ts +905 -0
- package/src/chains/ethereum/stealth.ts +491 -0
- package/src/chains/ethereum/token.ts +790 -0
- package/src/chains/ethereum/transfer.ts +637 -0
- package/src/chains/ethereum/types.ts +456 -0
- package/src/chains/ethereum/viewing-key.ts +455 -0
- package/src/chains/near/commitment.ts +608 -0
- package/src/chains/near/constants.ts +284 -0
- package/src/chains/near/function-call.ts +871 -0
- package/src/chains/near/history.ts +654 -0
- package/src/chains/near/implicit-account.ts +840 -0
- package/src/chains/near/index.ts +393 -0
- package/src/chains/near/native-transfer.ts +658 -0
- package/src/chains/near/nep141.ts +775 -0
- package/src/chains/near/privacy-adapter.ts +889 -0
- package/src/chains/near/resolver.ts +971 -0
- package/src/chains/near/rpc.ts +1016 -0
- package/src/chains/near/stealth.ts +419 -0
- package/src/chains/near/types.ts +317 -0
- package/src/chains/near/viewing-key.ts +876 -0
- package/src/chains/solana/anchor-transfer.ts +386 -0
- package/src/chains/solana/commitment.ts +577 -0
- package/src/chains/solana/constants.ts +126 -12
- package/src/chains/solana/ephemeral-keys.ts +543 -0
- package/src/chains/solana/index.ts +276 -1
- package/src/chains/solana/key-derivation.ts +418 -0
- package/src/chains/solana/kit-compat.ts +334 -0
- package/src/chains/solana/optimizations.ts +560 -0
- package/src/chains/solana/privacy-adapter.ts +605 -0
- package/src/chains/solana/providers/generic.ts +201 -0
- package/src/chains/solana/providers/helius-enhanced-types.ts +336 -0
- package/src/chains/solana/providers/helius-enhanced.ts +623 -0
- package/src/chains/solana/providers/helius.ts +402 -0
- package/src/chains/solana/providers/index.ts +85 -0
- package/src/chains/solana/providers/interface.ts +221 -0
- package/src/chains/solana/providers/quicknode.ts +409 -0
- package/src/chains/solana/providers/triton.ts +426 -0
- package/src/chains/solana/providers/webhook.ts +790 -0
- package/src/chains/solana/rpc-client.ts +1150 -0
- package/src/chains/solana/scan.ts +170 -73
- package/src/chains/solana/sol-transfer.ts +732 -0
- package/src/chains/solana/spl-transfer.ts +886 -0
- package/src/chains/solana/stealth-scanner.ts +703 -0
- package/src/chains/solana/sunspot-verifier.ts +453 -0
- package/src/chains/solana/transaction-builder.ts +755 -0
- package/src/chains/solana/transfer.ts +74 -5
- package/src/chains/solana/types.ts +77 -7
- package/src/chains/solana/utils.ts +110 -0
- package/src/chains/solana/viewing-key.ts +807 -0
- package/src/compliance/fireblocks.ts +921 -0
- package/src/compliance/index.ts +37 -0
- package/src/compliance/range-sas.ts +956 -0
- package/src/config/endpoints.ts +100 -0
- package/src/crypto.ts +11 -8
- package/src/errors.ts +82 -0
- package/src/evm/erc4337-relayer.ts +830 -0
- package/src/evm/index.ts +47 -0
- package/src/fees/calculator.ts +396 -0
- package/src/fees/index.ts +87 -0
- package/src/fees/near-contract.ts +429 -0
- package/src/fees/types.ts +268 -0
- package/src/index.ts +785 -1
- package/src/intent.ts +6 -3
- package/src/logger.ts +324 -0
- package/src/network/index.ts +80 -0
- package/src/network/proxy.ts +691 -0
- package/src/optimizations/index.ts +541 -0
- package/src/oracle/types.ts +1 -0
- package/src/privacy-backends/arcium-types.ts +727 -0
- package/src/privacy-backends/arcium.ts +719 -0
- package/src/privacy-backends/combined-privacy.ts +866 -0
- package/src/privacy-backends/cspl-token.ts +595 -0
- package/src/privacy-backends/cspl-types.ts +512 -0
- package/src/privacy-backends/cspl.ts +907 -0
- package/src/privacy-backends/health.ts +488 -0
- package/src/privacy-backends/inco-types.ts +323 -0
- package/src/privacy-backends/inco.ts +616 -0
- package/src/privacy-backends/index.ts +336 -0
- package/src/privacy-backends/interface.ts +906 -0
- package/src/privacy-backends/lru-cache.ts +343 -0
- package/src/privacy-backends/magicblock.ts +458 -0
- package/src/privacy-backends/mock.ts +258 -0
- package/src/privacy-backends/privacycash-types.ts +278 -0
- package/src/privacy-backends/privacycash.ts +456 -0
- package/src/privacy-backends/private-swap.ts +570 -0
- package/src/privacy-backends/rate-limiter.ts +683 -0
- package/src/privacy-backends/registry.ts +690 -0
- package/src/privacy-backends/router.ts +626 -0
- package/src/privacy-backends/shadowwire.ts +449 -0
- package/src/privacy-backends/sip-native.ts +256 -0
- package/src/privacy-logger.ts +191 -0
- package/src/production-safety.ts +373 -0
- package/src/proofs/aggregator.ts +1029 -0
- package/src/proofs/browser-composer.ts +1150 -0
- package/src/proofs/browser.ts +113 -25
- package/src/proofs/cache/index.ts +127 -0
- package/src/proofs/cache/interface.ts +545 -0
- package/src/proofs/cache/key-generator.ts +188 -0
- package/src/proofs/cache/lru-cache.ts +481 -0
- package/src/proofs/cache/multi-tier-cache.ts +575 -0
- package/src/proofs/cache/persistent-cache.ts +788 -0
- package/src/proofs/compliance-proof.ts +872 -0
- package/src/proofs/composer/base.ts +923 -0
- package/src/proofs/composer/index.ts +25 -0
- package/src/proofs/composer/interface.ts +518 -0
- package/src/proofs/composer/types.ts +383 -0
- package/src/proofs/converters/halo2.ts +452 -0
- package/src/proofs/converters/index.ts +208 -0
- package/src/proofs/converters/interface.ts +363 -0
- package/src/proofs/converters/kimchi.ts +462 -0
- package/src/proofs/converters/noir.ts +451 -0
- package/src/proofs/fallback.ts +888 -0
- package/src/proofs/halo2.ts +42 -0
- package/src/proofs/index.ts +471 -0
- package/src/proofs/interface.ts +13 -0
- package/src/proofs/kimchi.ts +42 -0
- package/src/proofs/lazy.ts +1004 -0
- package/src/proofs/mock.ts +25 -1
- package/src/proofs/noir.ts +111 -30
- package/src/proofs/orchestrator.ts +960 -0
- package/src/proofs/parallel/concurrency.ts +297 -0
- package/src/proofs/parallel/dependency-graph.ts +602 -0
- package/src/proofs/parallel/executor.ts +420 -0
- package/src/proofs/parallel/index.ts +131 -0
- package/src/proofs/parallel/interface.ts +685 -0
- package/src/proofs/parallel/worker-pool.ts +644 -0
- package/src/proofs/providers/halo2.ts +560 -0
- package/src/proofs/providers/index.ts +34 -0
- package/src/proofs/providers/kimchi.ts +641 -0
- package/src/proofs/validator.ts +881 -0
- package/src/proofs/verifier.ts +867 -0
- package/src/quantum/index.ts +112 -0
- package/src/quantum/winternitz-vault.ts +639 -0
- package/src/quantum/wots.ts +611 -0
- package/src/settlement/backends/direct-chain.ts +1 -0
- package/src/settlement/index.ts +9 -0
- package/src/settlement/router.ts +732 -46
- package/src/solana/index.ts +72 -0
- package/src/solana/jito-relayer.ts +687 -0
- package/src/solana/noir-verifier-types.ts +430 -0
- package/src/solana/noir-verifier.ts +816 -0
- package/src/stealth/address-derivation.ts +193 -0
- package/src/stealth/ed25519.ts +431 -0
- package/src/stealth/index.ts +233 -0
- package/src/stealth/meta-address.ts +221 -0
- package/src/stealth/secp256k1.ts +368 -0
- package/src/stealth/utils.ts +194 -0
- package/src/stealth.ts +50 -1504
- package/src/surveillance/algorithms/address-reuse.ts +143 -0
- package/src/surveillance/algorithms/cluster.ts +247 -0
- package/src/surveillance/algorithms/exchange.ts +295 -0
- package/src/surveillance/algorithms/temporal.ts +337 -0
- package/src/surveillance/analyzer.ts +442 -0
- package/src/surveillance/index.ts +64 -0
- package/src/surveillance/scoring.ts +372 -0
- package/src/surveillance/types.ts +264 -0
- package/src/sync/index.ts +106 -0
- package/src/sync/manager.ts +504 -0
- package/src/sync/mock-provider.ts +318 -0
- package/src/sync/oblivious.ts +625 -0
- package/src/tokens/index.ts +15 -0
- package/src/tokens/registry.ts +301 -0
- package/src/utils/deprecation.ts +94 -0
- package/src/utils/index.ts +9 -0
- package/src/wallet/ethereum/index.ts +68 -0
- package/src/wallet/ethereum/metamask-privacy.ts +420 -0
- package/src/wallet/ethereum/multi-wallet.ts +646 -0
- package/src/wallet/ethereum/privacy-adapter.ts +700 -0
- package/src/wallet/ethereum/types.ts +3 -1
- package/src/wallet/ethereum/walletconnect-adapter.ts +675 -0
- package/src/wallet/hardware/index.ts +10 -0
- package/src/wallet/hardware/ledger-privacy.ts +414 -0
- package/src/wallet/index.ts +71 -0
- package/src/wallet/near/adapter.ts +626 -0
- package/src/wallet/near/index.ts +86 -0
- package/src/wallet/near/meteor-wallet.ts +1153 -0
- package/src/wallet/near/my-near-wallet.ts +790 -0
- package/src/wallet/near/wallet-selector.ts +702 -0
- package/src/wallet/solana/adapter.ts +6 -4
- package/src/wallet/solana/index.ts +13 -0
- package/src/wallet/solana/privacy-adapter.ts +567 -0
- package/src/wallet/sui/types.ts +6 -4
- package/src/zcash/rpc-client.ts +13 -6
- package/dist/chunk-3INS3PR5.mjs +0 -884
- package/dist/chunk-3OVABDRH.mjs +0 -17096
- package/dist/chunk-DLDWZFYC.mjs +0 -1495
- package/dist/chunk-E6SZWREQ.mjs +0 -57
- package/dist/chunk-G33LB27A.mjs +0 -16166
- package/dist/chunk-HGU6HZRC.mjs +0 -231
- package/dist/chunk-L2K34JCU.mjs +0 -1496
- package/dist/chunk-SN4ZDTVW.mjs +0 -16166
- package/dist/constants-VOI7BSLK.mjs +0 -27
- package/dist/index-BYZbDjal.d.ts +0 -11390
- package/dist/index-CHB3KuOB.d.mts +0 -11859
- package/dist/index-CzWPI6Le.d.ts +0 -11859
- package/dist/index-xbWjohNq.d.mts +0 -11390
- package/dist/solana-5EMCTPTS.mjs +0 -46
- package/dist/solana-Q4NAVBTS.mjs +0 -46
|
@@ -0,0 +1,691 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Network Privacy Layer - Proxy Support
|
|
3
|
+
*
|
|
4
|
+
* Provides Tor and SOCKS5/HTTP proxy support for routing RPC calls
|
|
5
|
+
* through privacy-preserving networks. Prevents IP address correlation
|
|
6
|
+
* with wallet activity.
|
|
7
|
+
*
|
|
8
|
+
* ## Features
|
|
9
|
+
*
|
|
10
|
+
* - Auto-detect Tor service (ports 9050, 9150)
|
|
11
|
+
* - SOCKS5 proxy support
|
|
12
|
+
* - HTTP/HTTPS proxy support
|
|
13
|
+
* - Environment variable configuration
|
|
14
|
+
* - Circuit rotation for unlinkability
|
|
15
|
+
*
|
|
16
|
+
* @example Using Tor
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import { createProxyAgent, TOR_PORTS } from '@sip-protocol/sdk'
|
|
19
|
+
*
|
|
20
|
+
* // Auto-detect Tor
|
|
21
|
+
* const agent = await createProxyAgent('tor')
|
|
22
|
+
*
|
|
23
|
+
* // Or specify port
|
|
24
|
+
* const agent = await createProxyAgent('socks5://127.0.0.1:9050')
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @example Using custom SOCKS5 proxy
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const agent = await createProxyAgent('socks5://myproxy.example.com:1080')
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @module network/proxy
|
|
33
|
+
* @see https://github.com/sip-protocol/sip-protocol/issues/489
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import type { Agent } from 'http'
|
|
37
|
+
|
|
38
|
+
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Proxy configuration type
|
|
42
|
+
*
|
|
43
|
+
* - `'tor'`: Auto-detect local Tor service
|
|
44
|
+
* - `'socks5://...'`: SOCKS5 proxy URL
|
|
45
|
+
* - `'http://...'` or `'https://...'`: HTTP proxy URL
|
|
46
|
+
* - `undefined`: No proxy (direct connection)
|
|
47
|
+
*/
|
|
48
|
+
export type ProxyConfig =
|
|
49
|
+
| 'tor'
|
|
50
|
+
| `socks5://${string}`
|
|
51
|
+
| `socks4://${string}`
|
|
52
|
+
| `http://${string}`
|
|
53
|
+
| `https://${string}`
|
|
54
|
+
| undefined
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Proxy type classification
|
|
58
|
+
*/
|
|
59
|
+
export type ProxyType = 'tor' | 'socks5' | 'socks4' | 'http' | 'https' | 'none'
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Parsed proxy configuration
|
|
63
|
+
*/
|
|
64
|
+
export interface ParsedProxyConfig {
|
|
65
|
+
/** Proxy type */
|
|
66
|
+
type: ProxyType
|
|
67
|
+
/** Proxy host */
|
|
68
|
+
host?: string
|
|
69
|
+
/** Proxy port */
|
|
70
|
+
port?: number
|
|
71
|
+
/** Full proxy URL */
|
|
72
|
+
url?: string
|
|
73
|
+
/** Authentication username (if any) */
|
|
74
|
+
username?: string
|
|
75
|
+
/** Authentication password (if any) */
|
|
76
|
+
password?: string
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Proxy agent options
|
|
81
|
+
*/
|
|
82
|
+
export interface ProxyAgentOptions {
|
|
83
|
+
/** Connection timeout in milliseconds */
|
|
84
|
+
timeout?: number
|
|
85
|
+
/** Whether to rotate circuits (Tor only) */
|
|
86
|
+
rotateCircuit?: boolean
|
|
87
|
+
/** Custom Tor control port for circuit rotation */
|
|
88
|
+
torControlPort?: number
|
|
89
|
+
/** Tor control password (for NEWNYM) */
|
|
90
|
+
torControlPassword?: string
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Result of proxy availability check
|
|
95
|
+
*/
|
|
96
|
+
export interface ProxyCheckResult {
|
|
97
|
+
/** Whether proxy is available */
|
|
98
|
+
available: boolean
|
|
99
|
+
/** Proxy type detected */
|
|
100
|
+
type: ProxyType
|
|
101
|
+
/** Proxy URL if available */
|
|
102
|
+
url?: string
|
|
103
|
+
/** Error message if not available */
|
|
104
|
+
error?: string
|
|
105
|
+
/** Response time in milliseconds */
|
|
106
|
+
responseTimeMs?: number
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Common Tor ports
|
|
113
|
+
*
|
|
114
|
+
* - 9050: Standalone Tor daemon
|
|
115
|
+
* - 9150: Tor Browser Bundle
|
|
116
|
+
*/
|
|
117
|
+
export const TOR_PORTS = [9050, 9150] as const
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Default Tor host
|
|
121
|
+
*/
|
|
122
|
+
export const TOR_HOST = '127.0.0.1'
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Default Tor control port (for NEWNYM signal)
|
|
126
|
+
*/
|
|
127
|
+
export const TOR_CONTROL_PORT = 9051
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Default connection timeout in milliseconds
|
|
131
|
+
*/
|
|
132
|
+
export const DEFAULT_PROXY_TIMEOUT = 30000
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Environment variable for proxy configuration
|
|
136
|
+
*/
|
|
137
|
+
export const PROXY_ENV_VAR = 'SIP_PROXY'
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Alternative environment variables to check
|
|
141
|
+
*/
|
|
142
|
+
export const PROXY_ENV_VARS = [
|
|
143
|
+
'SIP_PROXY',
|
|
144
|
+
'ALL_PROXY',
|
|
145
|
+
'HTTPS_PROXY',
|
|
146
|
+
'HTTP_PROXY',
|
|
147
|
+
'SOCKS_PROXY',
|
|
148
|
+
] as const
|
|
149
|
+
|
|
150
|
+
// ─── Parsing ─────────────────────────────────────────────────────────────────
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Parse a proxy configuration string into structured data
|
|
154
|
+
*
|
|
155
|
+
* @param config - Proxy configuration string
|
|
156
|
+
* @returns Parsed configuration
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```typescript
|
|
160
|
+
* parseProxyConfig('tor')
|
|
161
|
+
* // { type: 'tor', host: '127.0.0.1', port: 9050 }
|
|
162
|
+
*
|
|
163
|
+
* parseProxyConfig('socks5://proxy.example.com:1080')
|
|
164
|
+
* // { type: 'socks5', host: 'proxy.example.com', port: 1080, url: '...' }
|
|
165
|
+
*
|
|
166
|
+
* parseProxyConfig('http://user:pass@proxy.com:8080')
|
|
167
|
+
* // { type: 'http', host: 'proxy.com', port: 8080, username: 'user', password: 'pass' }
|
|
168
|
+
* ```
|
|
169
|
+
*/
|
|
170
|
+
export function parseProxyConfig(config: ProxyConfig): ParsedProxyConfig {
|
|
171
|
+
if (!config) {
|
|
172
|
+
return { type: 'none' }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (config === 'tor') {
|
|
176
|
+
return {
|
|
177
|
+
type: 'tor',
|
|
178
|
+
host: TOR_HOST,
|
|
179
|
+
port: TOR_PORTS[0],
|
|
180
|
+
url: `socks5://${TOR_HOST}:${TOR_PORTS[0]}`,
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Parse URL
|
|
185
|
+
try {
|
|
186
|
+
const url = new URL(config)
|
|
187
|
+
const protocol = url.protocol.replace(':', '') as ProxyType
|
|
188
|
+
|
|
189
|
+
if (!['socks5', 'socks4', 'http', 'https'].includes(protocol)) {
|
|
190
|
+
throw new Error(`Unsupported proxy protocol: ${protocol}`)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
type: protocol,
|
|
195
|
+
host: url.hostname,
|
|
196
|
+
port: url.port ? parseInt(url.port, 10) : undefined,
|
|
197
|
+
url: config,
|
|
198
|
+
username: url.username || undefined,
|
|
199
|
+
password: url.password || undefined,
|
|
200
|
+
}
|
|
201
|
+
} catch {
|
|
202
|
+
throw new Error(
|
|
203
|
+
`Invalid proxy configuration: ${config}. ` +
|
|
204
|
+
`Expected 'tor', 'socks5://...', 'http://...', or 'https://...'`
|
|
205
|
+
)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Get proxy configuration from environment variables
|
|
211
|
+
*
|
|
212
|
+
* Checks SIP_PROXY, ALL_PROXY, HTTPS_PROXY, HTTP_PROXY, SOCKS_PROXY
|
|
213
|
+
*
|
|
214
|
+
* @returns Proxy configuration or undefined
|
|
215
|
+
*/
|
|
216
|
+
export function getProxyFromEnv(): ProxyConfig {
|
|
217
|
+
for (const envVar of PROXY_ENV_VARS) {
|
|
218
|
+
const value = process.env[envVar]
|
|
219
|
+
if (value) {
|
|
220
|
+
// Handle 'tor' shorthand
|
|
221
|
+
if (value.toLowerCase() === 'tor') {
|
|
222
|
+
return 'tor'
|
|
223
|
+
}
|
|
224
|
+
// Validate URL format
|
|
225
|
+
if (value.startsWith('socks') || value.startsWith('http')) {
|
|
226
|
+
return value as ProxyConfig
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return undefined
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// ─── Proxy Detection ─────────────────────────────────────────────────────────
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Check if Tor is available on a specific port
|
|
237
|
+
*
|
|
238
|
+
* @param port - Port to check
|
|
239
|
+
* @param host - Host to check (default: 127.0.0.1)
|
|
240
|
+
* @param timeout - Connection timeout in ms
|
|
241
|
+
* @returns True if Tor is responding
|
|
242
|
+
*/
|
|
243
|
+
export async function isTorAvailable(
|
|
244
|
+
port: number = TOR_PORTS[0],
|
|
245
|
+
host: string = TOR_HOST,
|
|
246
|
+
timeout: number = 5000
|
|
247
|
+
): Promise<boolean> {
|
|
248
|
+
// In Node.js, we'd use net.connect to check
|
|
249
|
+
// For now, return a simple check
|
|
250
|
+
return new Promise((resolve) => {
|
|
251
|
+
if (typeof globalThis.process === 'undefined') {
|
|
252
|
+
// Browser environment - Tor not directly available
|
|
253
|
+
resolve(false)
|
|
254
|
+
return
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
// Dynamic import to avoid bundling in browser
|
|
259
|
+
import('net').then(({ connect }) => {
|
|
260
|
+
const socket = connect({ host, port })
|
|
261
|
+
|
|
262
|
+
const timer = setTimeout(() => {
|
|
263
|
+
socket.destroy()
|
|
264
|
+
resolve(false)
|
|
265
|
+
}, timeout)
|
|
266
|
+
|
|
267
|
+
socket.on('connect', () => {
|
|
268
|
+
clearTimeout(timer)
|
|
269
|
+
socket.destroy()
|
|
270
|
+
resolve(true)
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
socket.on('error', () => {
|
|
274
|
+
clearTimeout(timer)
|
|
275
|
+
resolve(false)
|
|
276
|
+
})
|
|
277
|
+
}).catch(() => {
|
|
278
|
+
resolve(false)
|
|
279
|
+
})
|
|
280
|
+
} catch {
|
|
281
|
+
resolve(false)
|
|
282
|
+
}
|
|
283
|
+
})
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Auto-detect Tor service on common ports
|
|
288
|
+
*
|
|
289
|
+
* @param timeout - Connection timeout per port
|
|
290
|
+
* @returns Port number if found, undefined otherwise
|
|
291
|
+
*/
|
|
292
|
+
export async function detectTorPort(timeout: number = 5000): Promise<number | undefined> {
|
|
293
|
+
for (const port of TOR_PORTS) {
|
|
294
|
+
if (await isTorAvailable(port, TOR_HOST, timeout)) {
|
|
295
|
+
return port
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return undefined
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Check proxy availability and get connection info
|
|
303
|
+
*
|
|
304
|
+
* @param config - Proxy configuration
|
|
305
|
+
* @param timeout - Connection timeout
|
|
306
|
+
* @returns Availability result
|
|
307
|
+
*/
|
|
308
|
+
export async function checkProxyAvailability(
|
|
309
|
+
config: ProxyConfig,
|
|
310
|
+
timeout: number = DEFAULT_PROXY_TIMEOUT
|
|
311
|
+
): Promise<ProxyCheckResult> {
|
|
312
|
+
if (!config) {
|
|
313
|
+
return { available: true, type: 'none' }
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const start = Date.now()
|
|
317
|
+
|
|
318
|
+
if (config === 'tor') {
|
|
319
|
+
const port = await detectTorPort(timeout)
|
|
320
|
+
if (port) {
|
|
321
|
+
return {
|
|
322
|
+
available: true,
|
|
323
|
+
type: 'tor',
|
|
324
|
+
url: `socks5://${TOR_HOST}:${port}`,
|
|
325
|
+
responseTimeMs: Date.now() - start,
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
available: false,
|
|
330
|
+
type: 'tor',
|
|
331
|
+
error: `Tor not found on ports ${TOR_PORTS.join(', ')}. ` +
|
|
332
|
+
`Please start Tor or Tor Browser.`,
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// For SOCKS5/HTTP proxies, parse and validate
|
|
337
|
+
try {
|
|
338
|
+
const parsed = parseProxyConfig(config)
|
|
339
|
+
// In production, we'd do a test connection here
|
|
340
|
+
return {
|
|
341
|
+
available: true,
|
|
342
|
+
type: parsed.type,
|
|
343
|
+
url: parsed.url,
|
|
344
|
+
responseTimeMs: Date.now() - start,
|
|
345
|
+
}
|
|
346
|
+
} catch (error) {
|
|
347
|
+
return {
|
|
348
|
+
available: false,
|
|
349
|
+
type: 'none',
|
|
350
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// ─── Agent Creation ──────────────────────────────────────────────────────────
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Create a proxy agent for HTTP requests
|
|
359
|
+
*
|
|
360
|
+
* **Note:** This requires optional dependencies:
|
|
361
|
+
* - `socks-proxy-agent` for SOCKS4/5 proxies
|
|
362
|
+
* - `https-proxy-agent` for HTTP/HTTPS proxies
|
|
363
|
+
*
|
|
364
|
+
* Install with: `npm install socks-proxy-agent https-proxy-agent`
|
|
365
|
+
*
|
|
366
|
+
* @param config - Proxy configuration
|
|
367
|
+
* @param options - Agent options
|
|
368
|
+
* @returns HTTP Agent configured for proxy, or undefined for direct connection
|
|
369
|
+
* @throws Error if proxy dependencies not installed
|
|
370
|
+
*
|
|
371
|
+
* @example
|
|
372
|
+
* ```typescript
|
|
373
|
+
* // Using Tor
|
|
374
|
+
* const agent = await createProxyAgent('tor')
|
|
375
|
+
*
|
|
376
|
+
* // Using custom SOCKS5
|
|
377
|
+
* const agent = await createProxyAgent('socks5://127.0.0.1:1080')
|
|
378
|
+
*
|
|
379
|
+
* // Use with fetch
|
|
380
|
+
* fetch(url, { agent })
|
|
381
|
+
* ```
|
|
382
|
+
*/
|
|
383
|
+
export async function createProxyAgent(
|
|
384
|
+
config: ProxyConfig,
|
|
385
|
+
options: ProxyAgentOptions = {}
|
|
386
|
+
): Promise<Agent | undefined> {
|
|
387
|
+
if (!config) {
|
|
388
|
+
return undefined
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const { timeout = DEFAULT_PROXY_TIMEOUT } = options
|
|
392
|
+
|
|
393
|
+
// Handle Tor auto-detection
|
|
394
|
+
let proxyUrl: string
|
|
395
|
+
if (config === 'tor') {
|
|
396
|
+
const port = await detectTorPort(timeout)
|
|
397
|
+
if (!port) {
|
|
398
|
+
throw new Error(
|
|
399
|
+
`Tor not available on ports ${TOR_PORTS.join(', ')}. ` +
|
|
400
|
+
`Please start Tor (brew install tor && tor) or Tor Browser.`
|
|
401
|
+
)
|
|
402
|
+
}
|
|
403
|
+
proxyUrl = `socks5://${TOR_HOST}:${port}`
|
|
404
|
+
} else {
|
|
405
|
+
proxyUrl = config
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Parse to determine agent type
|
|
409
|
+
const parsed = parseProxyConfig(config === 'tor' ? `socks5://${TOR_HOST}:${TOR_PORTS[0]}` : config)
|
|
410
|
+
|
|
411
|
+
// Dynamic import of proxy agent libraries
|
|
412
|
+
try {
|
|
413
|
+
if (parsed.type === 'socks5' || parsed.type === 'socks4' || config === 'tor') {
|
|
414
|
+
// SOCKS proxy
|
|
415
|
+
const { SocksProxyAgent } = await import('socks-proxy-agent')
|
|
416
|
+
return new SocksProxyAgent(proxyUrl, { timeout })
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (parsed.type === 'http' || parsed.type === 'https') {
|
|
420
|
+
// HTTP proxy
|
|
421
|
+
const { HttpsProxyAgent } = await import('https-proxy-agent')
|
|
422
|
+
return new HttpsProxyAgent(proxyUrl, { timeout })
|
|
423
|
+
}
|
|
424
|
+
} catch (error) {
|
|
425
|
+
if ((error as NodeJS.ErrnoException).code === 'MODULE_NOT_FOUND') {
|
|
426
|
+
throw new Error(
|
|
427
|
+
`Proxy agent dependencies not installed. ` +
|
|
428
|
+
`Run: npm install socks-proxy-agent https-proxy-agent`
|
|
429
|
+
)
|
|
430
|
+
}
|
|
431
|
+
throw error
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return undefined
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// ─── Proxied Fetch ───────────────────────────────────────────────────────────
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Fetch function type with optional agent
|
|
441
|
+
*/
|
|
442
|
+
export type ProxiedFetch = (
|
|
443
|
+
url: string | URL,
|
|
444
|
+
options?: RequestInit & { agent?: Agent }
|
|
445
|
+
) => Promise<Response>
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Create a fetch function that uses a proxy agent
|
|
449
|
+
*
|
|
450
|
+
* @param agent - Proxy agent (from createProxyAgent)
|
|
451
|
+
* @returns Fetch function with proxy support
|
|
452
|
+
*
|
|
453
|
+
* @example
|
|
454
|
+
* ```typescript
|
|
455
|
+
* const agent = await createProxyAgent('tor')
|
|
456
|
+
* const proxiedFetch = createProxiedFetch(agent)
|
|
457
|
+
*
|
|
458
|
+
* // All requests go through Tor
|
|
459
|
+
* const response = await proxiedFetch('https://api.example.com/data')
|
|
460
|
+
* ```
|
|
461
|
+
*/
|
|
462
|
+
export function createProxiedFetch(agent: Agent | undefined): ProxiedFetch {
|
|
463
|
+
return async (url: string | URL, options: RequestInit & { agent?: Agent } = {}) => {
|
|
464
|
+
// In Node.js, we can pass agent directly
|
|
465
|
+
// The global fetch in Node 18+ supports agent option
|
|
466
|
+
return fetch(url, {
|
|
467
|
+
...options,
|
|
468
|
+
// @ts-expect-error - agent is valid for Node.js fetch
|
|
469
|
+
agent: agent ?? options.agent,
|
|
470
|
+
})
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// ─── Tor Circuit Rotation ────────────────────────────────────────────────────
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Request a new Tor circuit (NEWNYM)
|
|
478
|
+
*
|
|
479
|
+
* Requires Tor control port to be enabled with authentication.
|
|
480
|
+
* Configure in torrc:
|
|
481
|
+
* ```
|
|
482
|
+
* ControlPort 9051
|
|
483
|
+
* HashedControlPassword <your-hashed-password>
|
|
484
|
+
* ```
|
|
485
|
+
*
|
|
486
|
+
* @param controlPort - Tor control port (default: 9051)
|
|
487
|
+
* @param password - Control port password
|
|
488
|
+
* @returns True if circuit rotation succeeded
|
|
489
|
+
*
|
|
490
|
+
* @example
|
|
491
|
+
* ```typescript
|
|
492
|
+
* // Enable in torrc and set password
|
|
493
|
+
* const success = await rotateCircuit(9051, 'my-tor-password')
|
|
494
|
+
* if (success) {
|
|
495
|
+
* console.log('New Tor circuit established')
|
|
496
|
+
* }
|
|
497
|
+
* ```
|
|
498
|
+
*/
|
|
499
|
+
export async function rotateCircuit(
|
|
500
|
+
controlPort: number = TOR_CONTROL_PORT,
|
|
501
|
+
password?: string
|
|
502
|
+
): Promise<boolean> {
|
|
503
|
+
if (typeof globalThis.process === 'undefined') {
|
|
504
|
+
// Browser environment
|
|
505
|
+
console.warn('Circuit rotation not available in browser')
|
|
506
|
+
return false
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
try {
|
|
510
|
+
const { connect } = await import('net')
|
|
511
|
+
|
|
512
|
+
return new Promise((resolve) => {
|
|
513
|
+
const socket = connect({ host: TOR_HOST, port: controlPort })
|
|
514
|
+
|
|
515
|
+
let authenticated = false
|
|
516
|
+
|
|
517
|
+
socket.on('connect', () => {
|
|
518
|
+
if (password) {
|
|
519
|
+
socket.write(`AUTHENTICATE "${password}"\r\n`)
|
|
520
|
+
} else {
|
|
521
|
+
socket.write('AUTHENTICATE\r\n')
|
|
522
|
+
}
|
|
523
|
+
})
|
|
524
|
+
|
|
525
|
+
socket.on('data', (data) => {
|
|
526
|
+
const response = data.toString()
|
|
527
|
+
|
|
528
|
+
if (!authenticated && response.includes('250')) {
|
|
529
|
+
authenticated = true
|
|
530
|
+
socket.write('SIGNAL NEWNYM\r\n')
|
|
531
|
+
} else if (authenticated && response.includes('250')) {
|
|
532
|
+
socket.destroy()
|
|
533
|
+
resolve(true)
|
|
534
|
+
} else if (response.includes('515') || response.includes('5')) {
|
|
535
|
+
socket.destroy()
|
|
536
|
+
resolve(false)
|
|
537
|
+
}
|
|
538
|
+
})
|
|
539
|
+
|
|
540
|
+
socket.on('error', () => {
|
|
541
|
+
resolve(false)
|
|
542
|
+
})
|
|
543
|
+
|
|
544
|
+
// Timeout after 5 seconds
|
|
545
|
+
setTimeout(() => {
|
|
546
|
+
socket.destroy()
|
|
547
|
+
resolve(false)
|
|
548
|
+
}, 5000)
|
|
549
|
+
})
|
|
550
|
+
} catch {
|
|
551
|
+
return false
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// ─── Network Privacy Config ──────────────────────────────────────────────────
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Network privacy configuration for SDK clients
|
|
559
|
+
*/
|
|
560
|
+
export interface NetworkPrivacyConfig {
|
|
561
|
+
/**
|
|
562
|
+
* Proxy configuration
|
|
563
|
+
*
|
|
564
|
+
* - `'tor'`: Auto-detect local Tor
|
|
565
|
+
* - `'socks5://...'`: SOCKS5 proxy
|
|
566
|
+
* - `'http://...'`: HTTP proxy
|
|
567
|
+
* - `undefined`: Direct connection
|
|
568
|
+
*/
|
|
569
|
+
proxy?: ProxyConfig
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Rotate Tor circuit per request
|
|
573
|
+
*
|
|
574
|
+
* Requires Tor control port. Provides unlinkability between requests.
|
|
575
|
+
*
|
|
576
|
+
* @default false
|
|
577
|
+
*/
|
|
578
|
+
rotateCircuit?: boolean
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Tor control port for circuit rotation
|
|
582
|
+
*
|
|
583
|
+
* @default 9051
|
|
584
|
+
*/
|
|
585
|
+
torControlPort?: number
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Tor control password
|
|
589
|
+
*
|
|
590
|
+
* Required for rotateCircuit if Tor is configured with password authentication.
|
|
591
|
+
*/
|
|
592
|
+
torControlPassword?: string
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Connection timeout in milliseconds
|
|
596
|
+
*
|
|
597
|
+
* @default 30000
|
|
598
|
+
*/
|
|
599
|
+
timeout?: number
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Fallback to direct connection if proxy unavailable
|
|
603
|
+
*
|
|
604
|
+
* If true, will use direct connection when proxy fails.
|
|
605
|
+
* If false, will throw error when proxy fails.
|
|
606
|
+
*
|
|
607
|
+
* @default false
|
|
608
|
+
*/
|
|
609
|
+
fallbackToDirect?: boolean
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Default network privacy configuration
|
|
614
|
+
*/
|
|
615
|
+
export const DEFAULT_NETWORK_CONFIG: Required<Omit<NetworkPrivacyConfig, 'proxy' | 'torControlPassword'>> = {
|
|
616
|
+
rotateCircuit: false,
|
|
617
|
+
torControlPort: TOR_CONTROL_PORT,
|
|
618
|
+
timeout: DEFAULT_PROXY_TIMEOUT,
|
|
619
|
+
fallbackToDirect: false,
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Create a network privacy client
|
|
624
|
+
*
|
|
625
|
+
* @param config - Network privacy configuration
|
|
626
|
+
* @returns Proxied fetch function and utilities
|
|
627
|
+
*
|
|
628
|
+
* @example
|
|
629
|
+
* ```typescript
|
|
630
|
+
* const network = await createNetworkPrivacyClient({
|
|
631
|
+
* proxy: 'tor',
|
|
632
|
+
* rotateCircuit: true,
|
|
633
|
+
* torControlPassword: 'my-password',
|
|
634
|
+
* })
|
|
635
|
+
*
|
|
636
|
+
* // Use proxied fetch
|
|
637
|
+
* const response = await network.fetch('https://api.example.com')
|
|
638
|
+
*
|
|
639
|
+
* // Rotate circuit manually
|
|
640
|
+
* await network.rotateCircuit()
|
|
641
|
+
*
|
|
642
|
+
* // Check status
|
|
643
|
+
* console.log(network.status) // { type: 'tor', connected: true, ... }
|
|
644
|
+
* ```
|
|
645
|
+
*/
|
|
646
|
+
export async function createNetworkPrivacyClient(
|
|
647
|
+
config: NetworkPrivacyConfig = {}
|
|
648
|
+
): Promise<{
|
|
649
|
+
fetch: ProxiedFetch
|
|
650
|
+
rotateCircuit: () => Promise<boolean>
|
|
651
|
+
status: ProxyCheckResult
|
|
652
|
+
agent: Agent | undefined
|
|
653
|
+
}> {
|
|
654
|
+
const {
|
|
655
|
+
proxy,
|
|
656
|
+
rotateCircuit: shouldRotate = DEFAULT_NETWORK_CONFIG.rotateCircuit,
|
|
657
|
+
torControlPort = DEFAULT_NETWORK_CONFIG.torControlPort,
|
|
658
|
+
torControlPassword,
|
|
659
|
+
timeout = DEFAULT_NETWORK_CONFIG.timeout,
|
|
660
|
+
fallbackToDirect = DEFAULT_NETWORK_CONFIG.fallbackToDirect,
|
|
661
|
+
} = config
|
|
662
|
+
|
|
663
|
+
// Check proxy availability
|
|
664
|
+
const status = await checkProxyAvailability(proxy, timeout)
|
|
665
|
+
|
|
666
|
+
let agent: Agent | undefined
|
|
667
|
+
|
|
668
|
+
if (!status.available) {
|
|
669
|
+
if (fallbackToDirect) {
|
|
670
|
+
console.warn(
|
|
671
|
+
`[SIP-SDK] Proxy unavailable: ${status.error}. Falling back to direct connection.`
|
|
672
|
+
)
|
|
673
|
+
} else {
|
|
674
|
+
throw new Error(`Proxy unavailable: ${status.error}`)
|
|
675
|
+
}
|
|
676
|
+
} else {
|
|
677
|
+
agent = await createProxyAgent(proxy, { timeout })
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
return {
|
|
681
|
+
fetch: createProxiedFetch(agent),
|
|
682
|
+
rotateCircuit: async () => {
|
|
683
|
+
if (shouldRotate && (status.type === 'tor' || proxy === 'tor')) {
|
|
684
|
+
return rotateCircuit(torControlPort, torControlPassword)
|
|
685
|
+
}
|
|
686
|
+
return false
|
|
687
|
+
},
|
|
688
|
+
status,
|
|
689
|
+
agent,
|
|
690
|
+
}
|
|
691
|
+
}
|