feat: добавлена поддержка варианта sing-box Leadaxe (lx)

This commit is contained in:
Dushnilin
2026-07-20 08:29:56 +03:00
parent e1d7b00ba8
commit e0b70d6b99
21 changed files with 414 additions and 37 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "fe-app-tachyon",
"version": "1.2.11",
"version": "1.2.12",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "fe-app-tachyon",
"version": "1.2.11",
"version": "1.2.12",
"license": "MIT",
"devDependencies": {
"@babel/parser": "7.28.4",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "fe-app-tachyon",
"version": "1.2.11",
"version": "1.2.12",
"license": "MIT",
"type": "module",
"scripts": {
@@ -10,6 +10,7 @@ const componentActionKeyMap: Record<string, UpdatesActionKey> = {
'sing_box:install': 'singBoxInstall',
'sing_box:install_extended': 'singBoxInstallExtended',
'sing_box:install_extended_compressed': 'singBoxInstallExtendedCompressed',
'sing_box:install_lx': 'singBoxInstallLx',
'sing_box:install_tiny': 'singBoxInstallTiny',
'sing_box:install_stable': 'singBoxInstallStable',
'zapret:check_update': 'zapretCheck',
@@ -3,11 +3,16 @@ type SingBoxVariantFields = {
sing_box_extended?: number;
sing_box_tiny?: number;
sing_box_compressed?: number;
sing_box_lx?: number;
sing_box_tailscale?: number;
};
export function isExtendedSingBoxVersion(version?: string) {
return String(version || '').includes('extended');
return String(version || '').includes('extended') || String(version || '').includes('-lx');
}
export function isLxSingBoxVersion(version?: string) {
return String(version || '').includes('-lx');
}
function isVersionPlaceholder(version?: string) {
@@ -49,6 +54,11 @@ export function formatSingBoxVersion(value: SingBoxVariantFields) {
normalizedValue.sing_box_compressed
) {
variant = _('compressed');
} else if (
normalizedValue.sing_box_extended &&
normalizedValue.sing_box_lx
) {
variant = _('lx');
} else if (normalizedValue.sing_box_tiny) {
variant = _('tiny');
}
@@ -67,6 +77,7 @@ export function normalizeSingBoxVariantFields<T extends SingBoxVariantFields>(
sing_box_extended: singBoxExtended ? 1 : 0,
sing_box_tiny: singBoxExtended ? 0 : value.sing_box_tiny ? 1 : 0,
sing_box_compressed: singBoxExtended && value.sing_box_compressed ? 1 : 0,
sing_box_lx: singBoxExtended && value.sing_box_lx ? 1 : 0,
sing_box_tailscale: singBoxExtended || value.sing_box_tailscale ? 1 : 0,
} as T;
}
@@ -200,6 +200,7 @@ export interface StoreType {
sing_box_extended: number;
sing_box_tiny: number;
sing_box_compressed: number;
sing_box_lx: number;
sing_box_tailscale: number;
zapret_version: string;
zapret_installed: number;
@@ -218,6 +219,7 @@ export interface StoreType {
singBoxInstall: { loading: boolean };
singBoxInstallExtended: { loading: boolean };
singBoxInstallExtendedCompressed: { loading: boolean };
singBoxInstallLx: { loading: boolean };
singBoxInstallTiny: { loading: boolean };
singBoxInstallStable: { loading: boolean };
zapretCheck: { loading: boolean };
@@ -14,6 +14,7 @@ const UNKNOWN_SYSTEM_INFO: StoreType['diagnosticsSystemInfo'] = {
sing_box_extended: 0,
sing_box_tiny: 0,
sing_box_compressed: 0,
sing_box_lx: 0,
sing_box_tailscale: 1,
zapret_version: _('unknown'),
zapret_installed: 0,
@@ -1,4 +1,4 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { beforeEach, describe, expect, it, vi } from 'vitest';
const mocks = vi.hoisted(() => ({
executeShellCommand: vi.fn(),
@@ -38,6 +38,7 @@ function createUiState(
sing_box_extended: 1,
sing_box_tiny: 0,
sing_box_compressed: 0,
sing_box_lx: 0,
sing_box_tailscale: 1,
zapret_installed: 1,
zapret2_installed: 1,
@@ -1,4 +1,4 @@
import { beforeEach, describe, expect, it } from 'vitest';
import { beforeEach, describe, expect, it } from 'vitest';
import { applyUiStateToStore } from '../uiState.service';
import { store } from '../store.service';
import { Tachyon } from '../../types';
@@ -32,6 +32,7 @@ function createUiState(
sing_box_extended: 1,
sing_box_tiny: 0,
sing_box_compressed: 0,
sing_box_lx: 0,
sing_box_tailscale: 1,
zapret_installed: 1,
zapret2_installed: 0,
@@ -311,8 +312,9 @@ describe('applyUiStateToStore', () => {
applyUiStateToStore(
createUiState(undefined, {
sing_box_extended: 0,
sing_box_tiny: 1,
sing_box_tiny: 0,
sing_box_compressed: 0,
sing_box_lx: 0,
sing_box_tailscale: 0,
}),
);
@@ -19,6 +19,7 @@ function getEmptyUpdatesActions(): StoreType['updatesActions'] {
singBoxInstall: { loading: false },
singBoxInstallExtended: { loading: false },
singBoxInstallExtendedCompressed: { loading: false },
singBoxInstallLx: { loading: false },
singBoxInstallTiny: { loading: false },
singBoxInstallStable: { loading: false },
zapretCheck: { loading: false },
@@ -80,6 +81,7 @@ function applyServiceState(uiState: Tachyon.UiState) {
nextSystemInfo.sing_box_extended = uiState.capabilities.sing_box_extended;
nextSystemInfo.sing_box_tiny = uiState.capabilities.sing_box_tiny;
nextSystemInfo.sing_box_compressed = uiState.capabilities.sing_box_compressed;
nextSystemInfo.sing_box_lx = uiState.capabilities.sing_box_lx;
nextSystemInfo.sing_box_tailscale = uiState.capabilities.sing_box_tailscale;
store.set({
@@ -84,6 +84,7 @@ export const initialDiagnosticStore: Pick<
sing_box_extended: 0,
sing_box_tiny: 0,
sing_box_compressed: 0,
sing_box_lx: 0,
sing_box_tailscale: 1,
zapret_version: 'loading',
zapret_installed: 0,
@@ -136,6 +137,7 @@ export const initialDiagnosticStore: Pick<
singBoxInstall: { loading: false },
singBoxInstallExtended: { loading: false },
singBoxInstallExtendedCompressed: { loading: false },
singBoxInstallLx: { loading: false },
singBoxInstallTiny: { loading: false },
singBoxInstallStable: { loading: false },
zapretCheck: { loading: false },
@@ -370,6 +370,7 @@ async function fetchDiagnosticsProviderInfo({
sing_box_extended: uiState.capabilities.sing_box_extended,
sing_box_tiny: uiState.capabilities.sing_box_tiny,
sing_box_compressed: uiState.capabilities.sing_box_compressed,
sing_box_lx: uiState.capabilities.sing_box_lx,
sing_box_tailscale: uiState.capabilities.sing_box_tailscale,
zapret_installed: uiState.capabilities.zapret_installed,
zapret2_installed: uiState.capabilities.zapret2_installed,
@@ -339,6 +339,7 @@ function patchSystemInfoAfterMutation(result: Tachyon.ComponentActionResult) {
nextSystemInfo.sing_box_extended = 1;
nextSystemInfo.sing_box_tiny = 0;
nextSystemInfo.sing_box_compressed = 0;
nextSystemInfo.sing_box_lx = 0;
nextSystemInfo.sing_box_tailscale = 1;
}
@@ -346,6 +347,15 @@ function patchSystemInfoAfterMutation(result: Tachyon.ComponentActionResult) {
nextSystemInfo.sing_box_extended = 1;
nextSystemInfo.sing_box_tiny = 0;
nextSystemInfo.sing_box_compressed = 1;
nextSystemInfo.sing_box_lx = 0;
nextSystemInfo.sing_box_tailscale = 1;
}
if (result.action === 'install_lx') {
nextSystemInfo.sing_box_extended = 1;
nextSystemInfo.sing_box_tiny = 0;
nextSystemInfo.sing_box_compressed = 0;
nextSystemInfo.sing_box_lx = 1;
nextSystemInfo.sing_box_tailscale = 1;
}
@@ -353,6 +363,7 @@ function patchSystemInfoAfterMutation(result: Tachyon.ComponentActionResult) {
nextSystemInfo.sing_box_extended = 0;
nextSystemInfo.sing_box_tiny = 0;
nextSystemInfo.sing_box_compressed = 0;
nextSystemInfo.sing_box_lx = 0;
nextSystemInfo.sing_box_tailscale = 1;
}
@@ -360,6 +371,7 @@ function patchSystemInfoAfterMutation(result: Tachyon.ComponentActionResult) {
nextSystemInfo.sing_box_extended = 0;
nextSystemInfo.sing_box_tiny = 1;
nextSystemInfo.sing_box_compressed = 0;
nextSystemInfo.sing_box_lx = 0;
nextSystemInfo.sing_box_tailscale = 0;
}
}
@@ -820,10 +832,13 @@ function getComponentCards(): ComponentCard[] {
!systemInfo.sing_box_extended &&
!systemInfo.sing_box_tiny;
const singBoxExtended =
Boolean(systemInfo.sing_box_extended) && !systemInfo.sing_box_compressed;
Boolean(systemInfo.sing_box_extended) && !systemInfo.sing_box_compressed && !systemInfo.sing_box_lx;
const singBoxExtendedCompressed =
Boolean(systemInfo.sing_box_extended) &&
Boolean(systemInfo.sing_box_compressed);
const singBoxLx =
Boolean(systemInfo.sing_box_extended) &&
Boolean(systemInfo.sing_box_lx);
const singBoxTiny = Boolean(systemInfo.sing_box_tiny);
const tachyonActions = getInstalledUpdateActions(
@@ -875,6 +890,15 @@ function getComponentCards(): ComponentCard[] {
action: 'install_extended_compressed',
});
}
if (!singBoxLx) {
singBoxActions.push({
key: 'singBoxInstallLx',
text: 'Leadaxe (lx)',
icon: renderDownloadIcon24,
component: 'sing_box',
action: 'install_lx',
});
}
const zapretActions = getOptionalComponentActions({
component: 'zapret',
+2
View File
@@ -503,6 +503,7 @@ export namespace Tachyon {
sing_box_extended: 0 | 1;
sing_box_tiny: 0 | 1;
sing_box_compressed: 0 | 1;
sing_box_lx: 0 | 1;
sing_box_tailscale: 0 | 1;
zapret_installed: 0 | 1;
zapret2_installed: 0 | 1;
@@ -571,6 +572,7 @@ export namespace Tachyon {
| 'remove'
| 'install_extended'
| 'install_extended_compressed'
| 'install_lx'
| 'install_tiny'
| 'install_stable';
@@ -4521,6 +4521,7 @@ var initialDiagnosticStore = {
sing_box_extended: 0,
sing_box_tiny: 0,
sing_box_compressed: 0,
sing_box_lx: 0,
sing_box_tailscale: 1,
zapret_version: "loading",
zapret_installed: 0,
@@ -4573,6 +4574,7 @@ var initialDiagnosticStore = {
singBoxInstall: { loading: false },
singBoxInstallExtended: { loading: false },
singBoxInstallExtendedCompressed: { loading: false },
singBoxInstallLx: { loading: false },
singBoxInstallTiny: { loading: false },
singBoxInstallStable: { loading: false },
zapretCheck: { loading: false },
@@ -4927,6 +4929,7 @@ var componentActionKeyMap = {
"sing_box:install": "singBoxInstall",
"sing_box:install_extended": "singBoxInstallExtended",
"sing_box:install_extended_compressed": "singBoxInstallExtendedCompressed",
"sing_box:install_lx": "singBoxInstallLx",
"sing_box:install_tiny": "singBoxInstallTiny",
"sing_box:install_stable": "singBoxInstallStable",
"zapret:check_update": "zapretCheck",
@@ -4945,7 +4948,7 @@ function getComponentActionKey(component, action) {
// src/tachyon/helpers/singBoxVariant.ts
function isExtendedSingBoxVersion(version) {
return String(version || "").includes("extended");
return String(version || "").includes("extended") || String(version || "").includes("-lx");
}
function isVersionPlaceholder(version) {
const normalized = String(version || "").trim().toLowerCase();
@@ -4966,6 +4969,8 @@ function formatSingBoxVersion(value) {
let variant = "";
if (normalizedValue.sing_box_extended && normalizedValue.sing_box_compressed) {
variant = _("compressed");
} else if (normalizedValue.sing_box_extended && normalizedValue.sing_box_lx) {
variant = _("lx");
} else if (normalizedValue.sing_box_tiny) {
variant = _("tiny");
}
@@ -4979,6 +4984,7 @@ function normalizeSingBoxVariantFields(value) {
sing_box_extended: singBoxExtended ? 1 : 0,
sing_box_tiny: singBoxExtended ? 0 : value.sing_box_tiny ? 1 : 0,
sing_box_compressed: singBoxExtended && value.sing_box_compressed ? 1 : 0,
sing_box_lx: singBoxExtended && value.sing_box_lx ? 1 : 0,
sing_box_tailscale: singBoxExtended || value.sing_box_tailscale ? 1 : 0
};
}
@@ -5034,6 +5040,7 @@ function getEmptyUpdatesActions() {
singBoxInstall: { loading: false },
singBoxInstallExtended: { loading: false },
singBoxInstallExtendedCompressed: { loading: false },
singBoxInstallLx: { loading: false },
singBoxInstallTiny: { loading: false },
singBoxInstallStable: { loading: false },
zapretCheck: { loading: false },
@@ -5083,6 +5090,7 @@ function applyServiceState(uiState) {
nextSystemInfo.sing_box_extended = uiState.capabilities.sing_box_extended;
nextSystemInfo.sing_box_tiny = uiState.capabilities.sing_box_tiny;
nextSystemInfo.sing_box_compressed = uiState.capabilities.sing_box_compressed;
nextSystemInfo.sing_box_lx = uiState.capabilities.sing_box_lx;
nextSystemInfo.sing_box_tailscale = uiState.capabilities.sing_box_tailscale;
store.set({
servicesInfoWidget: {
@@ -8665,6 +8673,7 @@ var UNKNOWN_SYSTEM_INFO = {
sing_box_extended: 0,
sing_box_tiny: 0,
sing_box_compressed: 0,
sing_box_lx: 0,
sing_box_tailscale: 1,
zapret_version: _("unknown"),
zapret_installed: 0,
@@ -10218,6 +10227,7 @@ async function fetchDiagnosticsProviderInfo({
sing_box_extended: uiState.capabilities.sing_box_extended,
sing_box_tiny: uiState.capabilities.sing_box_tiny,
sing_box_compressed: uiState.capabilities.sing_box_compressed,
sing_box_lx: uiState.capabilities.sing_box_lx,
sing_box_tailscale: uiState.capabilities.sing_box_tailscale,
zapret_installed: uiState.capabilities.zapret_installed,
zapret2_installed: uiState.capabilities.zapret2_installed,
@@ -13438,24 +13448,35 @@ function patchSystemInfoAfterMutation(result) {
nextSystemInfo.sing_box_extended = 1;
nextSystemInfo.sing_box_tiny = 0;
nextSystemInfo.sing_box_compressed = 0;
nextSystemInfo.sing_box_lx = 0;
nextSystemInfo.sing_box_tailscale = 1;
}
if (result.action === "install_extended_compressed") {
nextSystemInfo.sing_box_extended = 1;
nextSystemInfo.sing_box_tiny = 0;
nextSystemInfo.sing_box_compressed = 1;
nextSystemInfo.sing_box_lx = 0;
nextSystemInfo.sing_box_tailscale = 1;
}
if (result.action === "install_lx") {
nextSystemInfo.sing_box_extended = 1;
nextSystemInfo.sing_box_tiny = 0;
nextSystemInfo.sing_box_compressed = 0;
nextSystemInfo.sing_box_lx = 1;
nextSystemInfo.sing_box_tailscale = 1;
}
if (result.action === "install_stable") {
nextSystemInfo.sing_box_extended = 0;
nextSystemInfo.sing_box_tiny = 0;
nextSystemInfo.sing_box_compressed = 0;
nextSystemInfo.sing_box_lx = 0;
nextSystemInfo.sing_box_tailscale = 1;
}
if (result.action === "install_tiny") {
nextSystemInfo.sing_box_extended = 0;
nextSystemInfo.sing_box_tiny = 1;
nextSystemInfo.sing_box_compressed = 0;
nextSystemInfo.sing_box_lx = 0;
nextSystemInfo.sing_box_tailscale = 0;
}
}
@@ -13798,8 +13819,9 @@ function getComponentCards() {
const byedpiInstalled = Boolean(systemInfo.byedpi_installed);
const singBoxInstalled = !isNotInstalled(systemInfo.sing_box_version);
const singBoxStable = singBoxInstalled && !systemInfo.sing_box_extended && !systemInfo.sing_box_tiny;
const singBoxExtended = Boolean(systemInfo.sing_box_extended) && !systemInfo.sing_box_compressed;
const singBoxExtended = Boolean(systemInfo.sing_box_extended) && !systemInfo.sing_box_compressed && !systemInfo.sing_box_lx;
const singBoxExtendedCompressed = Boolean(systemInfo.sing_box_extended) && Boolean(systemInfo.sing_box_compressed);
const singBoxLx = Boolean(systemInfo.sing_box_extended) && Boolean(systemInfo.sing_box_lx);
const singBoxTiny = Boolean(systemInfo.sing_box_tiny);
const tachyonActions = getInstalledUpdateActions(
"tachyon",
@@ -13848,6 +13870,15 @@ function getComponentCards() {
action: "install_extended_compressed"
});
}
if (!singBoxLx) {
singBoxActions.push({
key: "singBoxInstallLx",
text: "Leadaxe (lx)",
icon: renderDownloadIcon24,
component: "sing_box",
action: "install_lx"
});
}
const zapretActions = getOptionalComponentActions({
component: "zapret",
installed: zapretInstalled,
+206 -1
View File
@@ -1194,6 +1194,45 @@ function resolve_sing_box_extended_release(compressed) {
return set_sing_box_extended_release_from_json(release_json, compressed);
}
function set_sing_box_lx_release_from_json(release_json) {
if (as_string(release_json) == "")
return null;
let tag = trim(helper_output_input(release_json, "object-get-default", [ "tag_name", "" ]));
let lowered = lc(tag);
if (tag == "" || index(lowered, "alpha") >= 0 || index(lowered, "beta") >= 0 || index(lowered, "rc") >= 0 || index(lowered, "-lx") < 0)
return null;
let arch_suffix = resolve_sing_box_extended_arch_suffix();
if (arch_suffix == "")
return null;
let asset_url = trim(helper_output_input(release_json, "sing-box-lx-asset-url", [ arch_suffix ]));
if (asset_url == "")
return null;
return {
tag,
release_url: trim(helper_output_input(release_json, "object-get-default", [ "html_url", "" ])),
asset_url,
asset_name: path_basename(asset_url)
};
}
function resolve_sing_box_lx_release() {
let release_json = fetch_github_release_json("Leadaxe", "sing-box-lx");
let resolved = set_sing_box_lx_release_from_json(release_json);
if (resolved != null)
return resolved;
let releases_json = fetch_github_releases_json("Leadaxe", "sing-box-lx", "30");
if (releases_json == "")
return null;
let tag = trim(helper_output_input(releases_json, "sing-box-lx-release-tag", []));
if (tag == "")
return null;
release_json = helper_output_input(releases_json, "release-by-tag", [ tag ]);
return set_sing_box_lx_release_from_json(release_json);
}
function sing_box_runtime_output(mode, args) {
let command_args = [ LIB_DIR + "/singbox/runtime.uc", mode ];
for (let arg in (type(args) == "array" ? args : []))
@@ -1631,6 +1670,165 @@ function install_sing_box_extended(action, compressed) {
action_success("sing_box", action, label + " has been installed", new_version, latest_version, 1, "latest", release.release_url);
}
function install_sing_box_lx(action) {
init_tmp_dir() || action_fail("sing_box", action, "Failed to create temporary directory");
let label = "sing-box-lx";
let current_version = sing_box_runtime_output("version", []);
let current_variant = sing_box_runtime_output("variant", []);
let previous_marker = sing_box_runtime_output("read-variant-marker", []);
let previous_version_state = sing_box_runtime_output("read-version-state", []);
let release = resolve_sing_box_lx_release();
if (release == null)
action_fail("sing_box", action, "Failed to resolve " + label + " release", current_version);
let latest_version = normalize_sing_box_version(release.tag);
if (action == "check_update") {
if (!sing_box_runtime_success("is-lx", [ current_version ]))
action_fail("sing_box", action, "sing-box-lx is not installed", current_version, latest_version);
if (!sing_box_runtime_success("marker-is", [ "lx" ]))
action_fail("sing_box", action, "sing-box-lx is not installed", current_version, latest_version);
check_success("sing_box", normalize_sing_box_version(current_version), normalize_sing_box_version(latest_version), release.release_url);
}
let archive_file = tmp_dir + "/" + release.asset_name;
if (!download_with_retry(release.asset_url, archive_file, release.asset_name))
action_fail("sing_box", action, "Failed to download " + label, current_version, latest_version);
let binary_path = select_archive_member_path(archive_file, "sing-box");
if (binary_path == "") {
remove_file(archive_file);
action_fail("sing_box", action, "sing-box binary was not found in the downloaded archive", current_version, latest_version);
}
let cronet_path = select_archive_member_path(archive_file, "libcronet.so");
let extract_error = tmp_dir + "/sing-box-extract.err";
let tmp_binary = tmp_dir + "/sing-box.compressed." + owner_pid();
let tmp_cronet = "";
if (!command_success(command_from_args([ "tar", "-xzf", archive_file, "-O", binary_path ]) + " >" + shell_quote(tmp_binary) + " 2>" + shell_quote(extract_error)) ||
!file_nonempty(tmp_binary) ||
!command_success_from_args([ "chmod", "0755", tmp_binary ])) {
for (let line in split(read_file(extract_error), "\n"))
if (trim(as_string(line)) != "")
updates_log(line);
remove_file(tmp_binary);
remove_file(archive_file);
action_fail("sing_box", action, "Failed to extract " + label, current_version, latest_version);
}
if (cronet_path != "") {
tmp_cronet = tmp_dir + "/libcronet.so";
if (!command_success(command_from_args([ "tar", "-xzf", archive_file, "-O", cronet_path ]) + " >" + shell_quote(tmp_cronet) + " 2>" + shell_quote(extract_error)) ||
!file_nonempty(tmp_cronet) ||
!command_success_from_args([ "chmod", "0644", tmp_cronet ])) {
for (let line in split(read_file(extract_error), "\n"))
if (trim(as_string(line)) != "")
updates_log(line);
remove_file(tmp_binary);
remove_file(tmp_cronet);
remove_file(archive_file);
action_fail("sing_box", action, "Failed to extract libcronet.so from sing-box-lx archive", current_version, latest_version);
}
}
remove_file(archive_file);
stop_tachyon_before_sing_box_change();
let new_version = validate_sing_box_extended_binary(tmp_binary, tmp_dir);
if (new_version == "") {
remove_file(tmp_binary);
remove_file(tmp_cronet);
action_fail("sing_box", action, "Downloaded " + label + " failed validation", current_version, latest_version);
}
let backup_binary = "";
let backup_cronet = "";
let cronet_touched = false;
if (file_exists("/usr/bin/sing-box")) {
backup_binary = "/usr/bin/sing-box.tachyon-backup." + owner_pid();
if (!move_file_to_backup("/usr/bin/sing-box", backup_binary)) {
remove_file(backup_binary);
remove_file(tmp_binary);
remove_file(tmp_cronet);
remove_file(archive_file);
action_fail("sing_box", action, "Failed to backup current sing-box binary", current_version, latest_version);
}
}
if (cronet_path != "") {
cronet_touched = true;
if (file_exists("/usr/lib/libcronet.so")) {
backup_cronet = "/usr/lib/libcronet.so.tachyon-backup." + owner_pid();
if (!move_file_to_backup("/usr/lib/libcronet.so", backup_cronet)) {
restore_sing_box_after_failed_extended_install(current_variant, backup_binary, backup_cronet, previous_marker, previous_version_state, archive_file, cronet_touched);
remove_file(tmp_binary);
remove_file(tmp_cronet);
action_fail("sing_box", action, "Failed to backup current libcronet.so", current_version, latest_version);
}
}
}
for (let item in [
[ "sing-box-extended", "Removing sing-box-extended package before " + label + " installation" ],
[ "sing-box-tiny", "Removing sing-box-tiny package before " + label + " installation" ],
[ "sing-box", "Removing sing-box package before " + label + " installation" ]
]) {
if (!run_logged_pkg_remove_sing_box_conflict(item[0], item[1])) {
restore_sing_box_after_failed_extended_install(current_variant, backup_binary, backup_cronet, previous_marker, previous_version_state, archive_file, cronet_touched);
remove_file(tmp_binary);
remove_file(tmp_cronet);
action_fail("sing_box", action, "Failed to remove " + item[0] + " before " + label + " installation", current_version, latest_version);
}
}
remove_managed_sing_box_service_script();
if (!install_managed_sing_box_service_script()) {
restore_sing_box_after_failed_extended_install(current_variant, backup_binary, backup_cronet, previous_marker, previous_version_state, archive_file, cronet_touched);
remove_file(tmp_binary);
remove_file(tmp_cronet);
action_fail("sing_box", action, "Failed to install managed sing-box service for " + label, current_version, latest_version);
}
remove_file("/usr/bin/sing-box");
if (!install_staged_file(tmp_binary, "/usr/bin/sing-box", "0755")) {
remove_file("/usr/bin/sing-box");
restore_sing_box_after_failed_extended_install(current_variant, backup_binary, backup_cronet, previous_marker, previous_version_state, archive_file, cronet_touched);
action_fail("sing_box", action, "Failed to install " + label + " binary", current_version, latest_version);
}
if (tmp_cronet != "") {
remove_file("/usr/lib/libcronet.so");
if (!install_staged_file(tmp_cronet, "/usr/lib/libcronet.so", "0644")) {
remove_file("/usr/lib/libcronet.so");
restore_sing_box_after_failed_extended_install(current_variant, backup_binary, backup_cronet, previous_marker, previous_version_state, archive_file, cronet_touched);
action_fail("sing_box", action, "Failed to install libcronet.so for " + label, current_version, latest_version);
}
}
remove_file(archive_file);
new_version = validate_sing_box_extended_binary("/usr/bin/sing-box", "/usr/lib");
if (new_version == "") {
if (restore_sing_box_after_failed_extended_install(current_variant, backup_binary, backup_cronet, previous_marker, previous_version_state, archive_file, cronet_touched))
action_fail("sing_box", action, "Installed " + label + " failed validation; previous sing-box variant was restored", current_version, latest_version);
action_fail("sing_box", action, "Installed " + label + " failed validation and previous sing-box variant could not be restored", current_version, latest_version);
}
write_sing_box_variant_state("lx", new_version);
restart_tachyon_after_successful_change();
if (!wait_tachyon_running_after_sing_box_change()) {
updates_log(label + " did not start cleanly; restoring previous sing-box binary", "error");
if (file_exists(SERVICE_INIT))
command_success_from_args([ SERVICE_INIT, "stop" ]);
if (restore_sing_box_after_failed_extended_install(current_variant, backup_binary, backup_cronet, previous_marker, previous_version_state, archive_file, cronet_touched)) {
remove_file(backup_binary);
remove_file(backup_cronet);
action_fail("sing_box", action, label + " was installed but Tachyon did not start cleanly; previous sing-box variant was restored", current_version, latest_version);
}
action_fail("sing_box", action, label + " was installed but Tachyon did not start cleanly and previous sing-box variant could not be restored", current_version, latest_version);
}
remove_file(backup_binary);
remove_file(backup_cronet);
clear_version_caches();
updates_log("Installed " + label + " " + (new_version != "" ? new_version : "unknown"));
action_success("sing_box", action, label + " has been installed", new_version, latest_version, 1, "latest", release.release_url);
}
function install_package_sing_box(action, tiny) {
let package_name = tiny ? "sing-box-tiny" : "sing-box";
let conflict = tiny ? "sing-box" : "sing-box-tiny";
@@ -1817,6 +2015,10 @@ function dispatch_sing_box(action) {
install_sing_box_extended(action, true);
return;
}
if (action == "install_lx") {
install_sing_box_lx(action);
return;
}
if (action == "install_tiny") {
install_package_sing_box(action, true);
return;
@@ -1827,7 +2029,9 @@ function dispatch_sing_box(action) {
}
let variant = sing_box_runtime_output("variant", []);
if (variant == "extended-compressed")
if (variant == "lx")
install_sing_box_lx(action);
else if (variant == "extended-compressed")
install_sing_box_extended(action, true);
else if (variant == "extended")
install_sing_box_extended(action, false);
@@ -1861,6 +2065,7 @@ function component_action(component, action) {
install_tachyon();
else if (component == "sing_box" && (action == "check_update" || action == "install" ||
action == "install_extended" || action == "install_extended_compressed" ||
action == "install_lx" ||
action == "install_tiny" || action == "install_stable"))
dispatch_sing_box(action);
else if (component == "zapret" && (action == "check_update" || action == "install"))
@@ -698,6 +698,22 @@ function sing_box_extended_asset_url(arch_suffix, _unused, compressed) {
exit(1);
}
function sing_box_lx_asset_url(arch_suffix) {
let release = object_or_empty(read_stdin_json());
arch_suffix = as_string(arch_suffix);
if (arch_suffix == "")
exit(1);
let url = release_asset_url_by_suffix_from_release(release, "linux-" + arch_suffix + ".tar.gz");
if (url != "") {
print(url, "\n");
return;
}
exit(1);
}
function sing_box_extended_package_asset_url(distrib_arch, asset_ext) {
let release = object_or_empty(read_stdin_json());
let suffix;
@@ -915,6 +931,21 @@ function sing_box_extended_release_tag() {
}
}
function sing_box_lx_release_tag() {
for (let release in array_or_empty(read_stdin_json())) {
if (type(release) != "object")
continue;
if (release.draft === true || release.prerelease === true)
continue;
let tag = as_string(release.tag_name || "");
let lowered = lc(tag);
if (tag != "" && str_contains(lowered, "-lx") && !str_contains(lowered, "alpha") && !str_contains(lowered, "beta") && !str_contains(lowered, "rc")) {
print(tag, "\n");
return;
}
}
}
function text_first_chars(value, max_chars) {
value = as_string(value);
max_chars = int(max_chars || "0", 10) || 0;
@@ -1205,6 +1236,8 @@ else if (mode == "sing-box-extended-arch-suffix")
sing_box_extended_arch_suffix(ARGV[1], ARGV[2]);
else if (mode == "sing-box-extended-asset-url")
sing_box_extended_asset_url(ARGV[1], ARGV[2], ARGV[3]);
else if (mode == "sing-box-lx-asset-url")
sing_box_lx_asset_url(ARGV[1]);
else if (mode == "sing-box-extended-package-asset-url")
sing_box_extended_package_asset_url(ARGV[1], ARGV[2]);
else if (mode == "updates-opkg-package-installed")
@@ -1225,6 +1258,8 @@ else if (mode == "byedpi-select-asset")
byedpi_select_asset(ARGV[1], ARGV[2], ARGV[3]);
else if (mode == "sing-box-extended-release-tag")
sing_box_extended_release_tag();
else if (mode == "sing-box-lx-release-tag")
sing_box_lx_release_tag();
else if (mode == "file-last-nonblank-line")
file_last_nonblank_line(ARGV[1], ARGV[2], ARGV[3]);
else if (mode == "file-flat-snippet")
+15
View File
@@ -1798,6 +1798,10 @@ function sing_box_extended_marker_set(ctx) {
return sing_box_variant_marker(ctx, "extended");
}
function sing_box_lx_marker_set(ctx) {
return sing_box_variant_marker(ctx, "lx");
}
function sing_box_version_state(ctx) {
let path = as_string(ctx.sing_box_version_state_file);
if (path == "")
@@ -1825,6 +1829,10 @@ function sing_box_version_is_extended(version) {
return index(as_string(version), "extended") >= 0;
}
function sing_box_version_is_lx(version) {
return index(as_string(version), "-lx") >= 0;
}
function sing_box_is_extended(ctx, version) {
if (as_string(version) == "" && command_exists("sing-box") &&
(sing_box_compressed_marker_set(ctx) || sing_box_extended_marker_set(ctx)))
@@ -1833,6 +1841,13 @@ function sing_box_is_extended(ctx, version) {
return sing_box_version_is_extended(version != null ? version : get_sing_box_version(ctx));
}
function sing_box_is_lx(ctx, version) {
if (as_string(version) == "" && command_exists("sing-box") && sing_box_lx_marker_set(ctx))
return true;
return sing_box_version_is_lx(version != null ? version : get_sing_box_version(ctx));
}
function sing_box_output_has_build_tag(output, tag) {
tag = as_string(tag);
if (tag == "")
+9 -2
View File
@@ -999,6 +999,9 @@ function sing_box_capability_flags(sing_box_version, sing_box_version_output) {
module_success(SINGBOX_RUNTIME_UC, [ "is-extended", sing_box_version ]))
extended = 1;
if (sing_box_marker_is("lx") || module_success(SINGBOX_RUNTIME_UC, [ "is-lx", sing_box_version ]))
extended = 1;
if (extended == 0 && (sing_box_marker_is("tiny") || sing_box_tiny_package_installed()))
tiny = 1;
@@ -1066,6 +1069,7 @@ function build_system_info() {
let flags = sing_box_capability_flags(sing_box_version, sing_box_version_output);
let sing_box_compressed = flags.extended == 1 && sing_box_marker_is("extended-compressed") ? 1 : 0;
let sing_box_lx = flags.extended == 1 && sing_box_marker_is("lx") ? 1 : 0;
let zapret_installed = provider_installed(ZAPRET_RUNTIME_UC) ? 1 : 0;
let zapret_version = zapret_installed ? provider_version(ZAPRET_RUNTIME_UC) : "not installed";
@@ -1083,6 +1087,7 @@ function build_system_info() {
sing_box_extended: flags.extended,
sing_box_tiny: flags.tiny,
sing_box_compressed,
sing_box_lx,
sing_box_tailscale: flags.tailscale,
zapret_version,
zapret_installed,
@@ -1510,12 +1515,14 @@ function check_sing_box() {
sing_box_installed = 1;
let version = strip_leading_v(replace(module_output(SINGBOX_RUNTIME_UC, [ "version" ]), /[\r\n]+$/g, ""));
if (version != "") {
if (sing_box_marker_is("extended-compressed") || module_success(SINGBOX_RUNTIME_UC, [ "is-extended", version ]))
if (sing_box_marker_is("lx") || module_success(SINGBOX_RUNTIME_UC, [ "is-lx", version ]))
sing_box_extended = 1;
else if (sing_box_marker_is("extended-compressed") || module_success(SINGBOX_RUNTIME_UC, [ "is-extended", version ]))
sing_box_extended = 1;
if (module_success(HELPERS_UC, [ "version-at-least", version, "1.12.4" ]))
sing_box_version_ok = 1;
}
else if (sing_box_marker_is("extended-compressed"))
else if (sing_box_marker_is("extended-compressed") || sing_box_marker_is("lx"))
sing_box_extended = 1;
}
+17 -13
View File
@@ -1146,11 +1146,12 @@ function write_server_capabilities_json(sing_box_extended, sing_box_tiny, sing_b
});
}
function write_ui_capabilities_json(sing_box_extended, sing_box_tiny, sing_box_compressed, sing_box_tailscale, zapret_installed, zapret2_installed, byedpi_installed, server_inbounds_enabled_count) {
function write_ui_capabilities_json(sing_box_extended, sing_box_tiny, sing_box_compressed, sing_box_lx, sing_box_tailscale, zapret_installed, zapret2_installed, byedpi_installed, server_inbounds_enabled_count) {
write_json({
sing_box_extended: arg_number(sing_box_extended),
sing_box_tiny: arg_number(sing_box_tiny),
sing_box_compressed: arg_number(sing_box_compressed),
sing_box_lx: arg_number(sing_box_lx),
sing_box_tailscale: arg_number(sing_box_tailscale),
zapret_installed: arg_number(zapret_installed),
zapret2_installed: arg_number(zapret2_installed),
@@ -1313,7 +1314,9 @@ function render_flag_line(value, key, ok_message, fail_message) {
function sing_box_core_label(value) {
if (flag_is_one(value.sing_box_extended) && flag_is_one(value.sing_box_compressed))
return "extended compressed";
return "extended-compressed";
if (flag_is_one(value.sing_box_extended) && flag_is_one(value.sing_box_lx))
return "lx";
if (flag_is_one(value.sing_box_extended))
return "extended";
if (flag_is_one(value.sing_box_tiny))
@@ -1654,16 +1657,17 @@ function system_info_json() {
sing_box_extended: arg_number(ARGV[5]),
sing_box_tiny: arg_number(ARGV[6]),
sing_box_compressed: arg_number(ARGV[7]),
sing_box_tailscale: arg_number(ARGV[8]),
zapret_version: as_string(ARGV[9]),
zapret_installed: arg_number(ARGV[10]),
zapret2_version: as_string(ARGV[11]),
zapret2_installed: arg_number(ARGV[12]),
byedpi_version: as_string(ARGV[13]),
byedpi_installed: arg_number(ARGV[14]),
openwrt_version: as_string(ARGV[15]),
device_model: as_string(ARGV[16]),
generated_at: arg_number(ARGV[17])
sing_box_lx: arg_number(ARGV[8]),
sing_box_tailscale: arg_number(ARGV[9]),
zapret_version: as_string(ARGV[10]),
zapret_installed: arg_number(ARGV[11]),
zapret2_version: as_string(ARGV[12]),
zapret2_installed: arg_number(ARGV[13]),
byedpi_version: as_string(ARGV[14]),
byedpi_installed: arg_number(ARGV[15]),
openwrt_version: as_string(ARGV[16]),
device_model: as_string(ARGV[17]),
generated_at: arg_number(ARGV[18])
});
}
@@ -1944,7 +1948,7 @@ else if (mode == "inbounds-check-json")
else if (mode == "server-capabilities-json")
write_server_capabilities_json(ARGV[1], ARGV[2], ARGV[3]);
else if (mode == "ui-capabilities-json")
write_ui_capabilities_json(ARGV[1], ARGV[2], ARGV[3], ARGV[4], ARGV[5], ARGV[6], ARGV[7], ARGV[8]);
write_ui_capabilities_json(ARGV[1], ARGV[2], ARGV[3], ARGV[4], ARGV[5], ARGV[6], ARGV[7], ARGV[8], ARGV[9]);
else if (mode == "service-status-json")
write_service_status_json(ARGV[1], ARGV[2], ARGV[3], ARGV[4]);
else if (mode == "service-status-running")
+19 -7
View File
@@ -322,11 +322,12 @@ function ui_state_json() {
sing_box_extended: arg_number(ARGV[8]),
sing_box_tiny: arg_number(ARGV[9]),
sing_box_compressed: arg_number(ARGV[10]),
sing_box_tailscale: arg_number(ARGV[11]),
zapret_installed: arg_number(ARGV[12]),
zapret2_installed: arg_number(ARGV[13]),
byedpi_installed: arg_number(ARGV[14]),
server_inbounds_enabled_count: arg_number(ARGV[15])
sing_box_lx: arg_number(ARGV[11]),
sing_box_tailscale: arg_number(ARGV[12]),
zapret_installed: arg_number(ARGV[13]),
zapret2_installed: arg_number(ARGV[14]),
byedpi_installed: arg_number(ARGV[15]),
server_inbounds_enabled_count: arg_number(ARGV[16])
},
actions: action_state
});
@@ -1027,6 +1028,7 @@ function capability_flags() {
sing_box_extended: 0,
sing_box_tiny: 0,
sing_box_compressed: 0,
sing_box_lx: 0,
sing_box_tailscale: 0,
zapret_installed: file_executable(ZAPRET_PROVIDER_NFQWS_BIN) ? 1 : 0,
zapret2_installed: file_executable(ZAPRET2_PROVIDER_NFQWS2_BIN) ? 1 : 0,
@@ -1035,7 +1037,12 @@ function capability_flags() {
};
if (file_executable(SING_BOX_BIN_PATH)) {
if (marker_is("extended-compressed")) {
if (marker_is("lx")) {
result.sing_box_extended = 1;
result.sing_box_lx = 1;
result.sing_box_tailscale = 1;
}
else if (marker_is("extended-compressed")) {
result.sing_box_extended = 1;
result.sing_box_compressed = 1;
result.sing_box_tailscale = 1;
@@ -1052,7 +1059,12 @@ function capability_flags() {
}
else {
let info = sing_box_version_info();
if (info != null && index(info.version, "extended") >= 0) {
if (info != null && index(info.version, "-lx") >= 0) {
result.sing_box_extended = 1;
result.sing_box_lx = 1;
result.sing_box_tailscale = 1;
}
else if (info != null && index(info.version, "extended") >= 0) {
result.sing_box_extended = 1;
result.sing_box_tailscale = 1;
}
+23 -4
View File
@@ -265,6 +265,18 @@ function sing_box_is_extended(value) {
return sing_box_version_is_extended(value != "" ? value : sing_box_version());
}
function sing_box_version_is_lx(value) {
return index(as_string(value), "-lx") >= 0;
}
function sing_box_is_lx(value) {
value = as_string(value);
if (value == "" && command_exists("sing-box") && sing_box_marker_is("lx"))
return true;
return sing_box_version_is_lx(value != "" ? value : sing_box_version());
}
function output_has_build_tag(output, tag) {
tag = as_string(tag);
if (tag == "")
@@ -280,9 +292,9 @@ function sing_box_supports_tailscale(version, version_output) {
version = as_string(version);
version_output = as_string(version_output);
if (command_exists("sing-box") && sing_box_marker_is("extended-compressed"))
if (command_exists("sing-box") && (sing_box_marker_is("extended-compressed") || sing_box_marker_is("lx")))
return true;
if (sing_box_is_extended(version))
if (sing_box_is_extended(version) || sing_box_is_lx(version))
return true;
if (version_output != "")
return output_has_build_tag(version_output, "with_tailscale");
@@ -308,9 +320,10 @@ function sing_box_is_tiny(version, version_output) {
version = as_string(version);
version_output = as_string(version_output);
if (command_exists("sing-box") && sing_box_marker_is("extended-compressed"))
if (command_exists("sing-box") && (sing_box_marker_is("extended-compressed") || sing_box_marker_is("lx")))
return false;
if (sing_box_is_extended(version != "" ? version : sing_box_version()))
let computed_version = version != "" ? version : sing_box_version();
if (sing_box_is_extended(computed_version) || sing_box_is_lx(computed_version))
return false;
if (sing_box_package_installed("sing-box-tiny"))
return true;
@@ -324,10 +337,14 @@ function sing_box_variant() {
if (!command_exists("sing-box"))
return "not-installed";
if (sing_box_marker_is("lx"))
return "lx";
if (sing_box_marker_is("extended-compressed"))
return "extended-compressed";
version = sing_box_version();
if (sing_box_is_lx(version))
return "lx";
if (sing_box_is_extended(version))
return sing_box_marker_is("extended-compressed") ? "extended-compressed" : "extended";
if (sing_box_is_tiny(version, ""))
@@ -914,6 +931,8 @@ else if (mode == "marker-is")
exit(sing_box_marker_is(ARGV[1]) ? 0 : 1);
else if (mode == "is-extended")
exit(sing_box_is_extended(ARGV[1]) ? 0 : 1);
else if (mode == "is-lx")
exit(sing_box_is_lx(ARGV[1]) ? 0 : 1);
else if (mode == "is-tiny")
exit(sing_box_is_tiny(ARGV[1], ARGV[2]) ? 0 : 1);
else if (mode == "supports-tailscale")