Skip to content

Releases: xhqing/XPilot

Add real-traffic node testing, speed tests, VLESS+Reality support, and subscription by-name refresh

Choose a tag to compare

@xhqing xhqing released this 02 Aug 10:38

Added

  • Real-traffic node testing by default. The test command now starts a temporary xray instance per node (dedicated SOCKS port, bound to 127.0.0.1 only) and requests generate_204 through it, separating "TCP reachable" from "proxy actually usable." This fixes the long-standing issue where a TCP-only check marked a node as healthy even though proxy traffic never flowed (expired credentials, protocol handshake failure, no outbound connectivity on the peer). Output now shows separate TCP and proxy columns; pass --tcp to keep the fast TCP-only check. Added check_real_traffic, batch_check_real, _curl_through_socks (uses socks5h so DNS resolves through the proxy, avoiding local DNS-pollution false negatives), _find_free_port, and _kill_proc in xpilot/health_checker.py.
  • auto_switch redesigned as active best-node selection. Every interval seconds it runs a real-traffic check across all nodes, picks the lowest-latency usable node as the best, and switches whenever the best is not the current node — no longer just a fallback that switches only when the current node fails. hysteresis (default 0) prevents flapping between nodes with similar latency; when the current node is unusable it ignores hysteresis and switches to the best; when every node is unreachable it refreshes the subscription by name (with backoff), re-checks, and switches. The threshold option is replaced by hysteresis. Rewrote _check_and_switch in xpilot/auto_switch.py and added _try_refresh_subscription, _switch_to (reused by manual_switch), and _log_usable_status.
  • Subscription import with by-name refresh. NodeManager.import_from_subscription gains an update_existing parameter; when true, it matches existing nodes by name and overwrites their connection fields (address, port, UUID, password, cipher, transport, TLS, SNI) while preserving id, group, and custom name — so when the provider rotates credentials or IPs, the same node is actually refreshed instead of a suffixed duplicate being created (jms-c56s1jms-c56s1_1). Added the SUBSCRIPTION_REFRESH_FIELDS constant and _refresh_node_fields.
  • status warns when auto_switch is off, reminding you that the proxy will not auto-switch if the current node fails.
  • VLESS+Reality support. _parse_vless_link parses Reality parameters (pbk public key, sid short ID, fp fingerprint, flow) and URL-decodes the fragment node name (fixing %40/%3A garble after import). ProxyManager._generate_outbound emits realitySettings for Reality nodes and writes flow (e.g. xtls-rprx-vision) into the VLESS user object. The storage field whitelist (add_node / update_node / subscription refresh) now includes the Reality fields.
  • Automatic subscription cleanup with source tagging. import_from_subscription gains a source parameter that tags imported/refreshed nodes (e.g. source: 'JMS') and prunes nodes the subscription no longer returns. When a subscription renames a node (e.g. JMS-c56s1JMS-1336028@c56s1) or drops one, the stale entry is removed so the local set always matches what the subscription returns. Includes cliff protection (skips pruning when the returned count drops below 50% of existing, to avoid mass deletion on a transient outage). Manually added nodes have no source and are never pruned. Both subscription update and the auto_switch auto-refresh use this mode.
  • Usable-node anomaly logging. After each real-traffic check, if fewer nodes are usable than the total, auto_switch logs a warning listing the unreachable nodes and likely causes (written to log_file, default /tmp/xpilot.log); after subscription import, if a source's node count does not match what the subscription returned, it logs the reason (with added/refreshed/failed/pruned counts) so you can tell why usable nodes dropped.
  • xpilot test speed. Measures download speed via Cloudflare's __down endpoint (default 10 MB per node). --all-nodes tests every node's proxy speed concurrently; --direct tests direct (no-proxy) speed; --current tests the current node; --size sets the download size. Extracted _start_probe (shared temp-xray-probe logic reused by connectivity and speed) and added check_speed, check_direct_speed, batch_check_speed, and _curl_download in xpilot/health_checker.py.
  • test is now a command group (invoke_without_command keeps xpilot test --all-nodes / --tcp / --current / --group working) to host the speed subcommand. Breaking change: single-node testing moved from a positional argument (xpilot test my_node) to the --node option (xpilot test --node my_node), because a group's positional would collide with the subcommand name.
  • status shows three live metrics by default: the current node's real-traffic latency, through-node download speed, and direct download speed (downloading 5 MB through the running proxy and directly). --no-speed skips the speed test and shows latency only.
  • Latency comparison everywhere. Wherever "through-node latency" is shown, "direct latency" now appears alongside — status shows them side by side; test shows the direct-latency baseline in its connectivity summary for comparison. Added HealthChecker.check_direct_latency (a direct generate_204 request that auto-falls back to the Cloudflare endpoint behind the GFW).
  • New unit tests: subscription by-name refresh vs. default append, source auto-cleanup with cliff protection (tests/test_node_manager.py); real-traffic check failure paths and port probing (tests/test_health_checker.py); auto_switch real-traffic switch decisions and subscription-refresh backoff (tests/test_auto_switch.py); VLESS+Reality parsing and outbound-config generation (tests/test_subscription.py, tests/test_proxy_manager.py).

Changed

  • subscription update now refreshes by name and imports new nodes instead of only appending new ones (matching the "update" semantics). Use node import to append without refreshing existing nodes.
  • Default auto_switch settings in xpilot/config.py add auto_update_subscription (default True) and subscription_refresh_cooldown (default 3600).
  • auto_switch.enabled defaults to True (was False). A fresh install or xpilot init now enables real-traffic auto-switching out of the box — no manual config set needed.

Fixed

  • subscription.fetch now uses a trust_env=False session to force a direct connection, bypassing environment variables and the macOS system proxy. Previously, xpilot start pointed the system proxy at the local xray, so when the proxy itself was broken the subscription request was intercepted too — a "proxy down → can't fetch subscription → can't recover proxy" deadlock.
  • _parse_ss_link rewritten to fix SIP002 (ss://base64(method:password)@host:port#name) parsing. Base64 characters (+ / =) in the userinfo made urlparse mistake the userinfo for hostname/port, so SS nodes failed to parse and were silently skipped during subscription updates, leaving stale nodes unrefreshed. Added _b64decode_loose for websafe-base64 compatibility.

Full Changelog: v0.3.0...v0.4.0

Add rollback command for reverting to a previous release

Choose a tag to compare

@github-actions github-actions released this 31 Jul 09:58

Added

  • xpilot rollback command: install an earlier GitHub Release — without --version it rolls back to the most recent release strictly older than the running version, and with --version X.Y.Z it installs the specified version. Installation reuses the --update asset-selection logic (wheel first, then sdist tarball, falling back to building from the tagged source when neither is present). To support this, xpilot/updater.py gains fetch_releases, pick_previous_release, find_release_by_version, and perform_rollback; the "pick asset + invoke pip install" step shared by --update and rollback is extracted into the internal helper _install_release. Adds corresponding unit tests in tests/test_updater.py.

Full Changelog: v0.2.0...v0.3.0

Add --update self-update command

Choose a tag to compare

@xhqing xhqing released this 31 Jul 08:51

Added

  • xpilot --update self-update command: checks the project's GitHub Release for a newer version and installs it automatically — preferring the wheel attached to the release, then the sdist tarball, falling back to building from the tagged source when neither is present; reports and exits when already on the latest version. xpilot is distributed only via GitHub Release (not published on PyPI), so the update source points to GitHub Release rather than pip install --upgrade xpilot. Adds the xpilot/updater.py module (latest-release lookup, version comparison, asset selection, pip install) with tests/test_updater.py unit tests.

Changed

  • Fixed a version baseline mismatch: xpilot/__init__.py __version__ and the cli.py --version output were synced from 0.1.0 to 0.1.1 to align with VERSION, so --update compares against an accurate current version.

Full Changelog: v0.1.1...v0.2.0

Show live node latency in the status command

Choose a tag to compare

@github-actions github-actions released this 28 Jul 10:55

Added

  • The status command now shows the live latency of the current node. It performs a TCP probe against the current node to measure real-time latency, replacing the previous approach of reading a cached latency field, so the status output reflects the node's current reachability.

Install

Download the wheel or sdist attached to this release and install with pip:

pip install dist/xpilot-0.1.1-py3-none-any.whl

Then run xpilot --help.

Initial release — pure-Python proxy toolkit built on Xray-core

Choose a tag to compare

@github-actions github-actions released this 23 Jul 06:52

First public release. xpilot is a pure-Python command-line proxy toolkit that uses Xray-core (v26.3.27) as its backend, providing node management, proxy service control, health checks, and automatic node switching.

Added

  • Supported protocols: VMess, VLESS, Trojan, Shadowsocks.
  • Node management: add, remove, edit, query, and import nodes.
  • Smart health detection: latency- and connectivity-based health checks.
  • Automatic node switching based on latency thresholds.
  • Subscription auto-import: supports Base64, JSON, and Clash subscription formats.
  • macOS system proxy integration: one-click enable/disable of the system proxy.
  • Flexible routing rule management: proxy, direct, and block rule types.
  • Command-line entry point xpilot (built on Click).
  • Unit tests, Docker deployment files, development toolchain, and release workflow.
  • MIT license.

Install

Download the wheel or sdist attached to this release and install with pip:

pip install dist/xpilot-0.1.0-py3-none-any.whl

Then run xpilot --help.