v2portal 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. v2portal-0.1.0/PKG-INFO +11 -0
  2. v2portal-0.1.0/README.md +483 -0
  3. v2portal-0.1.0/pyproject.toml +28 -0
  4. v2portal-0.1.0/setup.cfg +4 -0
  5. v2portal-0.1.0/src/v2portal.egg-info/PKG-INFO +11 -0
  6. v2portal-0.1.0/src/v2portal.egg-info/SOURCES.txt +101 -0
  7. v2portal-0.1.0/src/v2portal.egg-info/dependency_links.txt +1 -0
  8. v2portal-0.1.0/src/v2portal.egg-info/entry_points.txt +2 -0
  9. v2portal-0.1.0/src/v2portal.egg-info/requires.txt +7 -0
  10. v2portal-0.1.0/src/v2portal.egg-info/top_level.txt +1 -0
  11. v2portal-0.1.0/src/v2raycli/__init__.py +3 -0
  12. v2portal-0.1.0/src/v2raycli/__main__.py +4 -0
  13. v2portal-0.1.0/src/v2raycli/app.py +3146 -0
  14. v2portal-0.1.0/src/v2raycli/backup.py +172 -0
  15. v2portal-0.1.0/src/v2raycli/config.py +78 -0
  16. v2portal-0.1.0/src/v2raycli/connection.py +298 -0
  17. v2portal-0.1.0/src/v2raycli/diagnostics.py +42 -0
  18. v2portal-0.1.0/src/v2raycli/engines/__init__.py +48 -0
  19. v2portal-0.1.0/src/v2raycli/engines/base.py +79 -0
  20. v2portal-0.1.0/src/v2raycli/engines/binary.py +326 -0
  21. v2portal-0.1.0/src/v2raycli/engines/singbox.py +622 -0
  22. v2portal-0.1.0/src/v2raycli/engines/xray.py +439 -0
  23. v2portal-0.1.0/src/v2raycli/errors.py +16 -0
  24. v2portal-0.1.0/src/v2raycli/exchange.py +249 -0
  25. v2portal-0.1.0/src/v2raycli/firewall.py +185 -0
  26. v2portal-0.1.0/src/v2raycli/geo.py +56 -0
  27. v2portal-0.1.0/src/v2raycli/models.py +265 -0
  28. v2portal-0.1.0/src/v2raycli/outbounds/__init__.py +1 -0
  29. v2portal-0.1.0/src/v2raycli/outbounds/groups.py +782 -0
  30. v2portal-0.1.0/src/v2raycli/outbounds/manual.py +257 -0
  31. v2portal-0.1.0/src/v2raycli/outbounds/vpn.py +98 -0
  32. v2portal-0.1.0/src/v2raycli/routing/__init__.py +1 -0
  33. v2portal-0.1.0/src/v2raycli/routing/rules.py +121 -0
  34. v2portal-0.1.0/src/v2raycli/runner.py +102 -0
  35. v2portal-0.1.0/src/v2raycli/servers.py +458 -0
  36. v2portal-0.1.0/src/v2raycli/service.py +115 -0
  37. v2portal-0.1.0/src/v2raycli/storage.py +494 -0
  38. v2portal-0.1.0/src/v2raycli/subs/__init__.py +1 -0
  39. v2portal-0.1.0/src/v2raycli/subs/fetcher.py +93 -0
  40. v2portal-0.1.0/src/v2raycli/subs/health.py +78 -0
  41. v2portal-0.1.0/src/v2raycli/subs/parser.py +307 -0
  42. v2portal-0.1.0/src/v2raycli/subs/share.py +681 -0
  43. v2portal-0.1.0/src/v2raycli/test/__init__.py +1 -0
  44. v2portal-0.1.0/src/v2raycli/test/latency.py +929 -0
  45. v2portal-0.1.0/src/v2raycli/traffic.py +70 -0
  46. v2portal-0.1.0/src/v2raycli/tui/__init__.py +1 -0
  47. v2portal-0.1.0/src/v2raycli/tui/app_screen.py +99 -0
  48. v2portal-0.1.0/src/v2raycli/tui/connection_screen.py +89 -0
  49. v2portal-0.1.0/src/v2raycli/tui/groups_screen.py +44 -0
  50. v2portal-0.1.0/src/v2raycli/tui/manage.py +422 -0
  51. v2portal-0.1.0/src/v2raycli/tui/routing_screen.py +260 -0
  52. v2portal-0.1.0/src/v2raycli/tui/servers_screen.py +136 -0
  53. v2portal-0.1.0/src/v2raycli/tui/settings_screen.py +117 -0
  54. v2portal-0.1.0/src/v2raycli/tui/test_screen.py +88 -0
  55. v2portal-0.1.0/src/v2raycli/tui/widgets.py +128 -0
  56. v2portal-0.1.0/tests/test_acceptance_flow.py +170 -0
  57. v2portal-0.1.0/tests/test_app.py +411 -0
  58. v2portal-0.1.0/tests/test_autoupdate.py +124 -0
  59. v2portal-0.1.0/tests/test_backup.py +192 -0
  60. v2portal-0.1.0/tests/test_binary.py +279 -0
  61. v2portal-0.1.0/tests/test_cli_commands.py +783 -0
  62. v2portal-0.1.0/tests/test_cli_surface.py +83 -0
  63. v2portal-0.1.0/tests/test_code_quality.py +45 -0
  64. v2portal-0.1.0/tests/test_config.py +21 -0
  65. v2portal-0.1.0/tests/test_config_gen.py +1032 -0
  66. v2portal-0.1.0/tests/test_connection_errors.py +247 -0
  67. v2portal-0.1.0/tests/test_controller.py +174 -0
  68. v2portal-0.1.0/tests/test_diagnostics.py +37 -0
  69. v2portal-0.1.0/tests/test_engine.py +21 -0
  70. v2portal-0.1.0/tests/test_errors.py +59 -0
  71. v2portal-0.1.0/tests/test_exchange.py +286 -0
  72. v2portal-0.1.0/tests/test_fetcher.py +148 -0
  73. v2portal-0.1.0/tests/test_firewall.py +226 -0
  74. v2portal-0.1.0/tests/test_geo.py +48 -0
  75. v2portal-0.1.0/tests/test_graph_flows.py +149 -0
  76. v2portal-0.1.0/tests/test_groups.py +523 -0
  77. v2portal-0.1.0/tests/test_health.py +96 -0
  78. v2portal-0.1.0/tests/test_latency.py +659 -0
  79. v2portal-0.1.0/tests/test_manual.py +135 -0
  80. v2portal-0.1.0/tests/test_models.py +65 -0
  81. v2portal-0.1.0/tests/test_parser.py +195 -0
  82. v2portal-0.1.0/tests/test_resolve_refs.py +179 -0
  83. v2portal-0.1.0/tests/test_routing.py +176 -0
  84. v2portal-0.1.0/tests/test_runner.py +124 -0
  85. v2portal-0.1.0/tests/test_servers.py +891 -0
  86. v2portal-0.1.0/tests/test_service.py +100 -0
  87. v2portal-0.1.0/tests/test_share.py +217 -0
  88. v2portal-0.1.0/tests/test_storage.py +362 -0
  89. v2portal-0.1.0/tests/test_temp_server.py +59 -0
  90. v2portal-0.1.0/tests/test_traffic.py +184 -0
  91. v2portal-0.1.0/tests/test_tui_connection.py +201 -0
  92. v2portal-0.1.0/tests/test_tui_groups.py +53 -0
  93. v2portal-0.1.0/tests/test_tui_manage.py +95 -0
  94. v2portal-0.1.0/tests/test_tui_pty.py +93 -0
  95. v2portal-0.1.0/tests/test_tui_routing.py +348 -0
  96. v2portal-0.1.0/tests/test_tui_servers.py +111 -0
  97. v2portal-0.1.0/tests/test_tui_settings.py +72 -0
  98. v2portal-0.1.0/tests/test_tui_test_screen.py +93 -0
  99. v2portal-0.1.0/tests/test_tui_widgets.py +98 -0
  100. v2portal-0.1.0/tests/test_verify_acceptance.py +25 -0
  101. v2portal-0.1.0/tests/test_verify_engines.py +41 -0
  102. v2portal-0.1.0/tests/test_vpn.py +77 -0
  103. v2portal-0.1.0/tests/test_vpn_connect.py +117 -0
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: v2portal
3
+ Version: 0.1.0
4
+ Summary: Interactive v2ray CLI client (sing-box + xray-core) with subscriptions, groups, and split routing
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: prompt_toolkit>=3.0
7
+ Requires-Dist: rich>=13.0
8
+ Requires-Dist: httpx[socks]>=0.26
9
+ Requires-Dist: platformdirs>=3.0
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest>=7.0; extra == "dev"
@@ -0,0 +1,483 @@
1
+ # v2raycli
2
+
3
+ Non-interactive CLI client for v2ray proxy management. It wraps two proxy
4
+ engines — **sing-box** (default) and **xray-core** — behind a single config,
5
+ and lets you add subscriptions/proxies, chain or balance them, test latency,
6
+ and run inbound servers from the command line.
7
+
8
+ Runs on **Linux**, **Windows**, and **Termux (Android)**.
9
+
10
+ ## Features
11
+
12
+ - **Dual engine** — sing-box (default) + xray-core, resolved per profile/group.
13
+ xray is used automatically for `ssr` and the `leastLoad` strategy.
14
+ - **Subscriptions** — paste a subscription URL or raw base64/plain payload;
15
+ import, update (deleted nodes are pruned), test a single subscription, and
16
+ auto-update on a schedule (`auto_update_days`).
17
+ - **Protocols** — vmess, vless, trojan, ss, ssr, socks, http, wireguard,
18
+ hysteria2, tuic, plus raw JSON configs. OpenVPN / OpenConnect via the system
19
+ clients.
20
+ - **Groups** — `balancer` (latency / random / roundRobin / leastLoad) and
21
+ `chain` (proxy through proxy).
22
+ - **Split routing** — domain/keyword/regex rules, IP CIDR, and geoip/geosite.
23
+ Rules can target any profile, subscription, group, or server, plus
24
+ `direct` or `block`. Stale rules are auto-cleaned when a profile, group,
25
+ or server is deleted.
26
+ - **LAN proxy inbound** — sing-box serves SOCKS5 *and* HTTP on one port;
27
+ xray exposes SOCKS5 plus HTTP CONNECT on adjacent ports, with optional
28
+ username/password auth.
29
+ - **Outbound testing** — `--probe` separately measures endpoint ICMP/TCP
30
+ reachability with DNS/refusal/timeout classification; `--ws-test` validates
31
+ WS/WSS handshake and ping/pong; `--test` measures real proxy request delay
32
+ through the engine.
33
+ - **Traffic stats** — cumulative per-profile/group up/down usage (sing-box),
34
+ recorded when a server stops.
35
+ - **Config on disk** — a single JSON file in your platform config dir.
36
+
37
+ ## Install
38
+
39
+ ### pip (Linux / macOS / Termux)
40
+
41
+ ```bash
42
+ pip install .
43
+ # or, for development:
44
+ pip install -e .[dev]
45
+ ```
46
+
47
+ > **Tip:** If the `v2raycli` command is not found after install, use
48
+ > `python -m v2raycli` as a fallback, or install with `pipx install .`
49
+ > (install pipx first: `pip install pipx`). `pipx` handles PATH setup
50
+ > automatically.
51
+
52
+ ### Termux (Android)
53
+
54
+ ```bash
55
+ pkg update && pkg install python
56
+ pip install .
57
+ ```
58
+
59
+ The engine binaries (sing-box / xray) download automatically to
60
+ `~/.config/v2raycli/bin` on first use, with arm64 assets on Termux.
61
+
62
+ > Note: OpenVPN / OpenConnect may require root on Android and are not
63
+ > chainable/balanceable — they run the system client directly.
64
+
65
+ ### Windows
66
+
67
+ ```bash
68
+ pip install .
69
+ ```
70
+
71
+ After installing, the `v2raycli` command is placed in Python's `Scripts`
72
+ directory (e.g. `%LOCALAPPDATA%\Python\pythonX.Y-64\Scripts`). If the
73
+ command is not found, **one of these will work**:
74
+
75
+ 1. Add the `Scripts` folder to your system `PATH` permanently:
76
+ ```powershell
77
+ [Environment]::SetEnvironmentVariable(
78
+ "Path",
79
+ $env:Path + ";" + [IO.Path]::Combine([Environment]::GetFolderPath("LocalApplicationData"), "Python", "python$([Environment]::Version.ToString(3))", "Scripts"),
80
+ "User"
81
+ )
82
+ ```
83
+ Then restart your terminal.
84
+
85
+ 2. Or use `python -m v2raycli` instead of `v2raycli` — this always works
86
+ regardless of PATH.
87
+
88
+ Config lives in `%APPDATA%\v2raycli`. Engine binaries download to
89
+ `%APPDATA%\v2raycli\bin`. When LAN sharing is enabled, allow the program
90
+ through the Windows firewall.
91
+
92
+ ### PyInstaller (single folder)
93
+
94
+ A spec is included:
95
+
96
+ ```bash
97
+ pip install pyinstaller
98
+ pyinstaller v2raycli.spec
99
+ ```
100
+
101
+ Engine binaries and geo assets still download on first run.
102
+
103
+ ## Quickstart
104
+
105
+ The primary interface is non-interactive and safe for scripts. Run
106
+ `v2raycli --help` or `v2raycli COMMAND --help` for the complete tree. The
107
+ interactive TUI (run `v2raycli` with no arguments) is a modern rich-styled
108
+ panel UI with screens for Connect, a Servers dashboard (live status +
109
+ start/stop), a Groups tree, Subscriptions, Test, Routing, and Settings:
110
+
111
+ ```bash
112
+ v2raycli status
113
+ v2raycli profile list
114
+ v2raycli subscription list
115
+ v2raycli group list
116
+ v2raycli group tree
117
+ v2raycli test latency all
118
+ ```
119
+
120
+ Every mutating command uses explicit arguments and writes the config only after
121
+ validation. The full command layout is:
122
+
123
+ ```text
124
+ profile list | add | rename | edit | remove | export
125
+ subscription list | add | edit | rename | update | remove (aliases: sub, subscriptions)
126
+ group list | add | tree | edit | remove | add-member | remove-member (alias: groups)
127
+ server list | add | edit | start | stop | restart | remove (alias: sv)
128
+ routing list | mode | add | move | enable | disable | remove
129
+ backup create | list | restore
130
+ settings show/set app settings, engine update
131
+ config show | export | import (backup/restore)
132
+ service install | uninstall
133
+ test latency | endpoint | websocket
134
+ ```
135
+
136
+ **One ID space, auto-detected references.** Every entity — profile,
137
+ subscription, group, server — has a unique ID from a single counter. Any
138
+ command that takes a *target reference* accepts a **profile, subscription,
139
+ group, or server ID** and detects the type automatically. References are
140
+ resolved at use time: a subscription always contributes its *current*
141
+ profiles, nested groups expand recursively (with dedup and cycle protection),
142
+ and a server member resolves to a socks/http profile through its local
143
+ inbound — so updated subscriptions flow everywhere automatically.
144
+
145
+ Examples:
146
+
147
+ ```bash
148
+ # Add a manual profile
149
+ v2raycli profile add socks office-proxy 127.0.0.1 1080
150
+ v2raycli profile add share us-node 'vless://...'
151
+ v2raycli profile edit ID --name "Office proxy" --host 10.0.0.2
152
+
153
+ # Import a subscription
154
+ v2raycli subscription add my-provider https://example.com/sub
155
+ v2raycli subscription edit SUB_ID --name 'Renamed provider'
156
+ v2raycli profile list --subscription SUB_ID
157
+
158
+ # Groups accept profiles, subscriptions, groups, and servers (auto-detected)
159
+ v2raycli group add balancer fastest PROFILE_A SUB_ID GROUP_B SERVER_ID --strategy latency
160
+ v2raycli group add chain chained PROFILE_A PROFILE_B
161
+ v2raycli group edit GROUP_ID --strategy random
162
+
163
+ # Render the nested group/subscription/server hierarchy
164
+ v2raycli group tree
165
+
166
+ # Add a running server as a local socks/http profile
167
+ v2raycli profile add server via-server SERVER_ID
168
+
169
+ # Start a proxy server on a port — REF is auto-detected
170
+ v2raycli server add --port 1080 REF --name 'US proxy'
171
+ v2raycli server edit SERVER_ID --outbound REF
172
+ v2raycli server start SERVER_ID
173
+
174
+ # Servers run in the background and survive terminal close
175
+ v2raycli server list
176
+ v2raycli sv stop SERVER_ID
177
+ v2raycli sv restart --all
178
+
179
+ # View and change settings
180
+ v2raycli settings
181
+ v2raycli settings test-url
182
+ v2raycli settings mixed-port 1081
183
+ v2raycli settings default-engine xray
184
+ v2raycli settings engine update both
185
+
186
+ # Update all subscriptions, filter profiles by kind
187
+ v2raycli subscription update --all
188
+ v2raycli profile list --kind socks
189
+
190
+ # Test by group, subscription, or profile ID
191
+ v2raycli test latency GROUP_ID
192
+ v2raycli test endpoint SUB_ID
193
+
194
+ # Routing rules can target any reference
195
+ v2raycli routing add block --domain 'keyword:ads'
196
+ v2raycli routing add direct --geoip cn
197
+ v2raycli routing add proxy --domain netflix.com --target SUB_ID
198
+
199
+ # Multiple servers on different ports
200
+ v2raycli server add --port 1081 GROUP_ID --protocol http --name 'Balancer'
201
+ v2raycli server start --all
202
+ ```
203
+
204
+ ### Engine updates
205
+
206
+ Engine updates are never automatic. Use `v2raycli settings engine update sing-box`,
207
+ `v2raycli settings engine update xray`, or `v2raycli settings engine update both`. Only binaries
208
+ configured with `binary_path: "auto"` are replaceable; custom and system paths
209
+ are protected. Downloads are staged, version-checked, atomically replaced, and
210
+ rolled back if verification fails. For restricted networks, pass an ephemeral
211
+ proxy with `--proxy` on the update command (e.g. `v2raycli settings engine update both --proxy
212
+ socks5://127.0.0.1:1080`); it is never stored.
213
+
214
+ ### Auto-update
215
+
216
+ Set `auto_update_days` on a subscription to have it re-fetched automatically
217
+ on startup when stale (never updated, or older than N days). Fetch failures are
218
+ logged and skipped — they never block startup. Disable per-run with
219
+ `--no-auto-update`.
220
+
221
+ ### Subscription health
222
+
223
+ On startup the CLI warns (to stderr) about expired subscriptions and those
224
+ expiring within 7 days. `v2raycli health` prints a table of expiry status and
225
+ traffic used for every enabled subscription.
226
+
227
+
228
+ ## Adding proxies
229
+
230
+ You can add proxies via CLI:
231
+
232
+ | Type | What it asks for |
233
+ |---|---|
234
+ | Subscription | URL (also `file://` and `paste://` accepted) |
235
+ | Share link | a `vmess://`, `vless://`, `trojan://`, `ss://`, `ssr://`, `hysteria2://`, `tuic://`, `wireguard://`, `socks://`, `http://` link |
236
+ | Socks / HTTP | server address, port, optional username/password |
237
+ | WireGuard | private key, address, peers |
238
+ | Hysteria2 / TUIC | server, auth, TLS/transport options |
239
+ | Raw config | paste a v2ray/xray outbound JSON object (uses xray engine) |
240
+ | OpenVPN | config file path or inline config + args |
241
+ | OpenConnect | server + args |
242
+
243
+ ## Groups
244
+
245
+ - **Balancer** — pick references (profiles, subscriptions, groups, servers)
246
+ and a strategy (`latency`, `random`, `roundRobin`, `leastLoad`).
247
+ `leastLoad` forces xray-core. Everything resolves dynamically.
248
+ A lone profile is rejected — a single profile is not a group.
249
+ - **Chain** — pick an ordered list; traffic flows through each in order.
250
+ - Groups can **nest**: a balancer can contain other groups. A server
251
+ member resolves to a socks/http profile pointing at that server's local
252
+ inbound ("localhost calling") — traffic physically passes through it.
253
+ Members are resolved recursively at use time, deduplicated, and cycles
254
+ (group→group and server→group→server) are rejected.
255
+ - `v2raycli group tree` renders the whole hierarchy — top-level groups with
256
+ their members (profiles, subscriptions and their current nodes, servers,
257
+ nested groups) plus any subscription/server/profile no group references.
258
+ - VPN profiles cannot join balancers/chains.
259
+
260
+ A **subscription used as a target** (server outbound, routing rule, group
261
+ member) resolves as a strategy-based balancer over its *current*
262
+ profiles — refresh the subscription and the target follows automatically.
263
+
264
+ ## Split routing
265
+
266
+ In **split** mode, the first matching rule wins. Rules can target a specific
267
+ profile, group, `direct` (bypass proxy), or `block` (drop traffic).
268
+
269
+ ### CLI
270
+
271
+ ```bash
272
+ # Switch to split mode
273
+ v2raycli routing mode split
274
+
275
+ # Block ads
276
+ v2raycli routing add block --domain keyword:ads
277
+ v2raycli routing add block --geosite category-ads-all
278
+
279
+ # Bypass local and Chinese IPs
280
+ v2raycli routing add direct --ip 192.168.0.0/16
281
+ v2raycli routing add direct --geoip cn --geoip private
282
+
283
+ # Route specific sites through a profile, group, or server
284
+ v2raycli routing add proxy --domain netflix.com --target PROFILE_ID
285
+ v2raycli routing add proxy --geosite gfw --target GROUP_ID
286
+ v2raycli routing add proxy --domain intranet.corp --target SERVER_ID
287
+
288
+ # List all rules
289
+ v2raycli routing list
290
+ v2raycli routing list --json
291
+
292
+ # Reorder rules (first match wins)
293
+ v2raycli routing move RULE_ID up
294
+ v2raycli routing move RULE_ID down
295
+
296
+ # Remove a rule
297
+ v2raycli routing remove RULE_ID
298
+ ```
299
+
300
+ ### Match types
301
+
302
+ | Matcher | Syntax | Example |
303
+ |---|---|---|
304
+ | Domain (exact) | `--domain example.com` | `--domain netflix.com` |
305
+ | Domain (keyword) | `--domain keyword:ads` | `--domain keyword:ads` |
306
+ | Domain (regex) | `--domain regex:^x\\.` | `--domain regex:^ads\\.` |
307
+ | Domain (geosite) | `--domain geosite:category` | `--domain geosite:gfw` |
308
+ | IP/CIDR | `--ip 10.0.0.0/8` | `--ip 192.168.0.0/16` |
309
+ | GeoIP | `--geoip cn` | `--geoip cn --geoip private` |
310
+ | GeoSite | `--geosite category-ads-all` | `--geosite gfw` |
311
+
312
+ ### Target any reference
313
+
314
+ Proxy rules can target any profile, subscription, group, or server by ID —
315
+ not just the server's default outbound. A server target routes matching
316
+ traffic through that server's local inbound. This lets you route different
317
+ traffic through different outbounds:
318
+
319
+ ```bash
320
+ # Route Netflix through a US profile
321
+ v2raycli routing add proxy --domain netflix.com --target US_PROFILE_ID
322
+
323
+ # Route streaming through a low-latency balancer group
324
+ v2raycli routing add proxy --geosite streaming --target BALANCER_GROUP_ID
325
+
326
+ # No --target: the rule follows the server's own outbound
327
+ v2raycli routing add proxy --domain example.com
328
+ ```
329
+
330
+ ### Automatic cleanup
331
+
332
+ When you delete a profile or group, any routing rules targeting it are
333
+ automatically removed — no broken configs.
334
+
335
+ ### Geo assets
336
+
337
+ On sing-box, `geosite:`/`geoip:` entries compile to rule-sets that
338
+ auto-download from SagerNet/sing-geosite and SagerNet/sing-geoip. On xray,
339
+ `geoip.dat`/`geosite.dat` download to `geo/` (found via `XRAY_LOCATION_ASSET`).
340
+
341
+ ## LAN sharing
342
+
343
+ By default `allow_lan` is enabled — the inbound listens on `0.0.0.0` so other
344
+ devices on your network can use `socks5://<your-ip>:1080` /
345
+ `http://<your-ip>:1080`. Optionally enable inbound auth (username/password).
346
+
347
+ ## Traffic stats
348
+
349
+ Enable **Settings → Traffic stats** (or set `settings.traffic_api: true` in the
350
+ config) to have sing-box expose its Clash API on `127.0.0.1`
351
+ (`traffic_api_port`, default 9090). The CLI polls cumulative up/down bytes and
352
+ adds them to the server's outbound target when the server stops. Works with
353
+ sing-box (the default engine); xray has no Clash-compatible HTTP API so its
354
+ traffic is not counted.
355
+
356
+ ## Run as a service
357
+
358
+ Keep all enabled servers running across reboots (no ad-hoc `connect` command
359
+ — connections are servers):
360
+
361
+ ```bash
362
+ v2raycli server add --port 1080 REF
363
+ v2raycli server start --all
364
+ v2raycli service install
365
+ ```
366
+
367
+ - **Linux** — writes a systemd *user* unit to
368
+ `~/.config/systemd/user/v2raycli.service`; enable it with
369
+ `systemctl --user enable --now v2raycli`.
370
+ - **Termux** — writes a `termux-services` run script to
371
+ `~/.termux/sv/v2raycli/run`; enable with `sv-enable v2raycli`.
372
+
373
+ Remove it with `v2raycli service uninstall`.
374
+
375
+ ## Engine selection
376
+
377
+ - **sing-box** is the default and serves most protocols plus `latency`/`random`/
378
+ `roundRobin`. WireGuard profiles are emitted as sing-box **endpoints** (the
379
+ format since 1.13, where the WireGuard outbound was removed) and can be
380
+ chained, balanced and routed to like any other outbound.
381
+ - **xray-core** is used automatically for `ssr` and `leastLoad`.
382
+ - Override per profile/group, or change the global default in Settings.
383
+
384
+ ### Routing guide: per-destination proxy selection
385
+
386
+ You can route different traffic through different outbounds using split routing
387
+ rules. Here's a practical setup:
388
+
389
+ ```bash
390
+ # Enable split routing
391
+ v2raycli routing mode split
392
+
393
+ # Route Epic Games traffic through a Berlin profile
394
+ v2raycli routing add proxy --domain epicgames.com --target BERLIN_PROFILE_ID
395
+ v2raycli routing add proxy --domain fortnite.com --target BERLIN_PROFILE_ID
396
+
397
+ # Route YouTube traffic through a balancer group
398
+ v2raycli routing add proxy --domain youtube.com --target GROUP_1_ID
399
+ v2raycli routing add proxy --domain googlevideo.com --target GROUP_1_ID
400
+
401
+ # Route everything else through a subscription (auto-detected as a member)
402
+ v2raycli group add balancer default SUB_ID_A SUB_ID_2
403
+
404
+ # Route Russian websites directly (no proxy)
405
+ v2raycli routing add direct --geoip ru
406
+ v2raycli routing add direct --geosite ru
407
+
408
+ # Block ads globally
409
+ v2raycli routing add block --geosite category-ads-all
410
+
411
+ # Review your rules
412
+ v2raycli routing list
413
+ ```
414
+
415
+ Rules are evaluated in order; the first match wins. Use `v2raycli routing move`
416
+ to reorder them.
417
+
418
+ ## Troubleshooting
419
+
420
+ - **Port in use** — change the port with `v2raycli settings mixed-port <port>`.
421
+ - **"binary not found"** — binaries download on first use; if download fails,
422
+ set `engines.<name>.binary_path` in the config to a local binary, or
423
+ `"system"` to use one on `PATH`.
424
+ - **LAN unreachable** — check the firewall (Windows) and that devices share the
425
+ network.
426
+ - **Geo rules silent** — sing-box auto-downloads rule-sets, and xray's
427
+ `geoip.dat`/`geosite.dat` download to `geo/` on first use (both need
428
+ network).
429
+ - **VPN client missing** — install `openvpn` / `openconnect` and ensure it's on
430
+ `PATH`.
431
+ - **`v2raycli` command not found after `pip install .`** — the script is in
432
+ Python's `Scripts` directory which may not be on `PATH`. Fix: use
433
+ `python -m v2raycli`, add `Scripts` to your `PATH`, or reinstall with
434
+ `pipx install .`.
435
+
436
+ ## Config layout
437
+
438
+ Config is JSON at `<config-dir>/config.json`. Derived dirs under the same base:
439
+
440
+ - `runtime/` — generated engine configs and test results
441
+ - `bin/` — downloaded engine binaries
442
+ - `geo/` — xray `geoip.dat`/`geosite.dat` assets (sing-box rule-sets are
443
+ cached by the engine)
444
+ - `backup/` — automatic config backups
445
+
446
+ ## Development
447
+
448
+ ```bash
449
+ pip install -e .[dev]
450
+ pytest
451
+ python scripts/verify_acceptance.py --json
452
+ ```
453
+
454
+ GitHub Actions runs the pytest suite and the credential-free acceptance smoke
455
+ on Python 3.10, 3.11, and 3.12 for pushes and pull requests. Live engine,
456
+ remote-node, and platform walkthroughs remain separate from CI.
457
+
458
+ Read-only platform diagnostics (does not load or modify the config, download
459
+ binaries, or start processes):
460
+
461
+ ```bash
462
+ python scripts/verify_platform.py
463
+ python scripts/verify_platform.py --json
464
+ ```
465
+
466
+ Credential-free orchestration smoke (subscription import, split routing,
467
+ connection switching, test dispatch, cleanup, and OpenVPN/OpenConnect argv
468
+ validation;
469
+ does not download engines or contact remote nodes):
470
+
471
+ ```bash
472
+ python scripts/verify_acceptance.py
473
+ python scripts/verify_acceptance.py --json
474
+ ```
475
+
476
+ Live engine verification (downloads sing-box + xray, runs their own config
477
+ checks, and exercises the proxy end-to-end):
478
+
479
+ ```bash
480
+ python scripts/verify_engines.py
481
+ # If GitHub access requires a local HTTP/SOCKS proxy:
482
+ python scripts/verify_engines.py --proxy socks5://127.0.0.1:10808
483
+ ```
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "v2portal"
7
+ version = "0.1.0"
8
+ description = "Interactive v2ray CLI client (sing-box + xray-core) with subscriptions, groups, and split routing"
9
+ requires-python = ">=3.10"
10
+ dependencies = [
11
+ "prompt_toolkit>=3.0",
12
+ "rich>=13.0",
13
+ "httpx[socks]>=0.26",
14
+ "platformdirs>=3.0",
15
+ ]
16
+
17
+ [project.optional-dependencies]
18
+ dev = ["pytest>=7.0"]
19
+
20
+ [project.scripts]
21
+ v2portal = "v2raycli.app:main"
22
+
23
+ [tool.setuptools.packages.find]
24
+ where = ["src"]
25
+
26
+ [tool.pytest.ini_options]
27
+ pythonpath = ["src"]
28
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: v2portal
3
+ Version: 0.1.0
4
+ Summary: Interactive v2ray CLI client (sing-box + xray-core) with subscriptions, groups, and split routing
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: prompt_toolkit>=3.0
7
+ Requires-Dist: rich>=13.0
8
+ Requires-Dist: httpx[socks]>=0.26
9
+ Requires-Dist: platformdirs>=3.0
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest>=7.0; extra == "dev"
@@ -0,0 +1,101 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/v2portal.egg-info/PKG-INFO
4
+ src/v2portal.egg-info/SOURCES.txt
5
+ src/v2portal.egg-info/dependency_links.txt
6
+ src/v2portal.egg-info/entry_points.txt
7
+ src/v2portal.egg-info/requires.txt
8
+ src/v2portal.egg-info/top_level.txt
9
+ src/v2raycli/__init__.py
10
+ src/v2raycli/__main__.py
11
+ src/v2raycli/app.py
12
+ src/v2raycli/backup.py
13
+ src/v2raycli/config.py
14
+ src/v2raycli/connection.py
15
+ src/v2raycli/diagnostics.py
16
+ src/v2raycli/errors.py
17
+ src/v2raycli/exchange.py
18
+ src/v2raycli/firewall.py
19
+ src/v2raycli/geo.py
20
+ src/v2raycli/models.py
21
+ src/v2raycli/runner.py
22
+ src/v2raycli/servers.py
23
+ src/v2raycli/service.py
24
+ src/v2raycli/storage.py
25
+ src/v2raycli/traffic.py
26
+ src/v2raycli/engines/__init__.py
27
+ src/v2raycli/engines/base.py
28
+ src/v2raycli/engines/binary.py
29
+ src/v2raycli/engines/singbox.py
30
+ src/v2raycli/engines/xray.py
31
+ src/v2raycli/outbounds/__init__.py
32
+ src/v2raycli/outbounds/groups.py
33
+ src/v2raycli/outbounds/manual.py
34
+ src/v2raycli/outbounds/vpn.py
35
+ src/v2raycli/routing/__init__.py
36
+ src/v2raycli/routing/rules.py
37
+ src/v2raycli/subs/__init__.py
38
+ src/v2raycli/subs/fetcher.py
39
+ src/v2raycli/subs/health.py
40
+ src/v2raycli/subs/parser.py
41
+ src/v2raycli/subs/share.py
42
+ src/v2raycli/test/__init__.py
43
+ src/v2raycli/test/latency.py
44
+ src/v2raycli/tui/__init__.py
45
+ src/v2raycli/tui/app_screen.py
46
+ src/v2raycli/tui/connection_screen.py
47
+ src/v2raycli/tui/groups_screen.py
48
+ src/v2raycli/tui/manage.py
49
+ src/v2raycli/tui/routing_screen.py
50
+ src/v2raycli/tui/servers_screen.py
51
+ src/v2raycli/tui/settings_screen.py
52
+ src/v2raycli/tui/test_screen.py
53
+ src/v2raycli/tui/widgets.py
54
+ tests/test_acceptance_flow.py
55
+ tests/test_app.py
56
+ tests/test_autoupdate.py
57
+ tests/test_backup.py
58
+ tests/test_binary.py
59
+ tests/test_cli_commands.py
60
+ tests/test_cli_surface.py
61
+ tests/test_code_quality.py
62
+ tests/test_config.py
63
+ tests/test_config_gen.py
64
+ tests/test_connection_errors.py
65
+ tests/test_controller.py
66
+ tests/test_diagnostics.py
67
+ tests/test_engine.py
68
+ tests/test_errors.py
69
+ tests/test_exchange.py
70
+ tests/test_fetcher.py
71
+ tests/test_firewall.py
72
+ tests/test_geo.py
73
+ tests/test_graph_flows.py
74
+ tests/test_groups.py
75
+ tests/test_health.py
76
+ tests/test_latency.py
77
+ tests/test_manual.py
78
+ tests/test_models.py
79
+ tests/test_parser.py
80
+ tests/test_resolve_refs.py
81
+ tests/test_routing.py
82
+ tests/test_runner.py
83
+ tests/test_servers.py
84
+ tests/test_service.py
85
+ tests/test_share.py
86
+ tests/test_storage.py
87
+ tests/test_temp_server.py
88
+ tests/test_traffic.py
89
+ tests/test_tui_connection.py
90
+ tests/test_tui_groups.py
91
+ tests/test_tui_manage.py
92
+ tests/test_tui_pty.py
93
+ tests/test_tui_routing.py
94
+ tests/test_tui_servers.py
95
+ tests/test_tui_settings.py
96
+ tests/test_tui_test_screen.py
97
+ tests/test_tui_widgets.py
98
+ tests/test_verify_acceptance.py
99
+ tests/test_verify_engines.py
100
+ tests/test_vpn.py
101
+ tests/test_vpn_connect.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ v2portal = v2raycli.app:main
@@ -0,0 +1,7 @@
1
+ prompt_toolkit>=3.0
2
+ rich>=13.0
3
+ httpx[socks]>=0.26
4
+ platformdirs>=3.0
5
+
6
+ [dev]
7
+ pytest>=7.0
@@ -0,0 +1 @@
1
+ v2raycli
@@ -0,0 +1,3 @@
1
+ """v2raycli — interactive v2ray CLI client (sing-box + xray-core)."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,4 @@
1
+ from v2raycli.app import main
2
+
3
+ if __name__ == "__main__":
4
+ raise SystemExit(main())