de-AI: remove AI comments and boolean theater across TypeScript + ucode
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
export function parseValueList(value: string): string[] {
|
||||
return value
|
||||
.split(/\n/) // Split to array by newline separator
|
||||
.map((line) => line.split('//')[0].split('#')[0]) // Remove comments
|
||||
.join(' ') // Build clean string
|
||||
.split(/[,\s]+/) // Split to array by comma and space
|
||||
.map((s) => s.trim()) // Remove extra spaces
|
||||
.filter(Boolean); // Leave nonempty items
|
||||
.split(/\n/)
|
||||
.map((line) => line.split('//')[0].split('#')[0])
|
||||
.join(' ')
|
||||
.split(/[,\s]+/)
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ async function isComponentActionStillRunning(
|
||||
state.job_id === jobId &&
|
||||
state.component === component &&
|
||||
state.action === action &&
|
||||
state.running === true,
|
||||
state.running,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ class TabService {
|
||||
attributeFilter: ['class'],
|
||||
});
|
||||
|
||||
// initial check
|
||||
this.notify();
|
||||
}
|
||||
|
||||
|
||||
@@ -303,7 +303,7 @@ async function completeSubscriptionUpdateJob(
|
||||
const shouldNotify = jobId
|
||||
? shouldNotifyOwnedUiAction('subscription', jobId)
|
||||
: false;
|
||||
const failed = !response.success || response.data.success === false;
|
||||
const failed = !response.success || !response.data.success;
|
||||
const message = response.success
|
||||
? response.data.message || _('Failed to update subscriptions')
|
||||
: response.error || _('Failed to update subscriptions');
|
||||
@@ -751,7 +751,6 @@ async function handleTestLatency(
|
||||
|
||||
try {
|
||||
if (latencyType === 'proxy') {
|
||||
// Test proxy latency immediately
|
||||
const parsedTag = tag.startsWith('[') ? JSON.parse(tag)[0] : tag;
|
||||
const response = await TachyonShellMethods.getClashApiProxyLatency(
|
||||
parsedTag,
|
||||
@@ -1936,7 +1935,6 @@ async function onStoreUpdate(
|
||||
}
|
||||
|
||||
async function onPageMount() {
|
||||
// Cleanup before mount
|
||||
onPageUnmount();
|
||||
|
||||
dashboardMounted = true;
|
||||
@@ -1956,7 +1954,6 @@ async function onPageMount() {
|
||||
}
|
||||
}
|
||||
|
||||
// Add new listener
|
||||
store.subscribe(onStoreUpdate);
|
||||
startActionStateWatcher();
|
||||
void renderSectionsWidget();
|
||||
@@ -1980,9 +1977,7 @@ function onPageUnmount() {
|
||||
stopActionStateWatcher();
|
||||
sectionsRefreshQueued = false;
|
||||
sectionsRefreshPromise = null;
|
||||
// Remove old listener
|
||||
store.unsubscribe(onStoreUpdate);
|
||||
// Clear store
|
||||
store.reset(['bandwidthWidget', 'trafficTotalWidget', 'systemInfoWidget']);
|
||||
}
|
||||
|
||||
|
||||
@@ -577,8 +577,6 @@ function renderAiWatchdogSection(state: AdvancedSettingsState) {
|
||||
{ class: 'tachyon_adv__hint' },
|
||||
_('Advanced self-healing features. Requires Watchdog to be enabled.'),
|
||||
),
|
||||
|
||||
// Health Checks group
|
||||
E('div', { class: 'tachyon_adv__group' }, [
|
||||
E('h4', { class: 'tachyon_adv__group-title' }, _('Health Checks')),
|
||||
toggle('proxyHealthEnabled', 'Enable Proxy Health Monitor'),
|
||||
@@ -606,8 +604,6 @@ function renderAiWatchdogSection(state: AdvancedSettingsState) {
|
||||
])
|
||||
: E('span', {}),
|
||||
]),
|
||||
|
||||
// Protection group
|
||||
E('div', { class: 'tachyon_adv__group' }, [
|
||||
E('h4', { class: 'tachyon_adv__group-title' }, _('Protection')),
|
||||
toggle(
|
||||
@@ -623,8 +619,6 @@ function renderAiWatchdogSection(state: AdvancedSettingsState) {
|
||||
'Graceful Degradation (skip failed checks, continue)',
|
||||
),
|
||||
]),
|
||||
|
||||
// Monitoring group
|
||||
E('div', { class: 'tachyon_adv__group' }, [
|
||||
E(
|
||||
'h4',
|
||||
@@ -657,8 +651,6 @@ function renderAiWatchdogSection(state: AdvancedSettingsState) {
|
||||
])
|
||||
: E('span', {}),
|
||||
]),
|
||||
|
||||
// Optimization group
|
||||
E('div', { class: 'tachyon_adv__group' }, [
|
||||
E('h4', { class: 'tachyon_adv__group-title' }, _('Optimization')),
|
||||
toggle(
|
||||
@@ -674,8 +666,6 @@ function renderAiWatchdogSection(state: AdvancedSettingsState) {
|
||||
'Persistent Smart Detect (survive reboots)',
|
||||
),
|
||||
]),
|
||||
|
||||
// AI Agent API group
|
||||
E('div', { class: 'tachyon_adv__group' }, [
|
||||
E(
|
||||
'h4',
|
||||
@@ -730,8 +720,6 @@ function renderAiWatchdogSection(state: AdvancedSettingsState) {
|
||||
),
|
||||
),
|
||||
]),
|
||||
|
||||
// AI Doctor (ChatGPT / DeepSeek API) group
|
||||
E('div', { class: 'tachyon_adv__group' }, [
|
||||
E(
|
||||
'h4',
|
||||
|
||||
@@ -19,7 +19,6 @@ export function render() {
|
||||
),
|
||||
),
|
||||
E('div', { class: 'tachyon_dashboard-page__content' }, [
|
||||
// Widgets section
|
||||
E('div', { class: 'tachyon_dashboard-page__widgets-section' }, [
|
||||
E(
|
||||
'div',
|
||||
@@ -62,9 +61,7 @@ export function render() {
|
||||
}),
|
||||
),
|
||||
]),
|
||||
// Clients section
|
||||
E('div', { id: 'dashboard-connections-grid' }, []),
|
||||
// All outbounds
|
||||
E(
|
||||
'div',
|
||||
{ id: 'dashboard-sections-grid' },
|
||||
|
||||
@@ -600,7 +600,7 @@ async function handleServiceRuntimeAction({
|
||||
throw new Error(result.error);
|
||||
}
|
||||
|
||||
if (result.data.success === false) {
|
||||
if (!result.data.success) {
|
||||
throw new Error(result.data.message || _('Service action failed'));
|
||||
}
|
||||
|
||||
@@ -867,7 +867,6 @@ async function handleRunAiDoctor() {
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
// Save to History
|
||||
const nowStr = new Date().toLocaleTimeString();
|
||||
saveAiDoctorHistory({ timestamp: nowStr, report, quickFixes });
|
||||
let historyEntries = getAiDoctorHistory();
|
||||
@@ -2151,7 +2150,6 @@ function restorePersistedDiagnosticRun() {
|
||||
async function onPageMount() {
|
||||
const preserveHiddenResult = diagnosticCompletedWhileHidden;
|
||||
|
||||
// Cleanup before mount
|
||||
onPageUnmount({
|
||||
preserveCompletedResult: preserveHiddenResult,
|
||||
preservePersistedRun: true,
|
||||
@@ -2187,23 +2185,13 @@ async function onPageMount() {
|
||||
resetDiagnosticsChecks();
|
||||
}
|
||||
|
||||
// Add new listener
|
||||
store.subscribe(onStoreUpdate);
|
||||
startServiceActionStateWatcher();
|
||||
|
||||
// Initial checks render
|
||||
renderDiagnosticsChecks();
|
||||
|
||||
// Initial run checks action render
|
||||
renderDiagnosticRunActionWidget();
|
||||
|
||||
// Initial available actions render
|
||||
renderDiagnosticAvailableActionsWidget();
|
||||
|
||||
// Initial system info render
|
||||
renderDiagnosticSystemInfoWidget();
|
||||
|
||||
// Initial Wiki disclaimer render
|
||||
renderWikiDisclaimerWidget();
|
||||
|
||||
if (hasRuntimeSnapshot) {
|
||||
@@ -2226,7 +2214,6 @@ function onPageUnmount({
|
||||
stopServiceActionStateWatcher();
|
||||
servicesInfoRefreshPromise = null;
|
||||
|
||||
// Remove old listener
|
||||
store.unsubscribe(onStoreUpdate);
|
||||
|
||||
if (!preserveCompletedResult && !store.get().diagnosticsRunAction.loading) {
|
||||
|
||||
@@ -85,7 +85,6 @@ export function renderAiChatModal() {
|
||||
chatInput.value = '';
|
||||
renderMessages();
|
||||
|
||||
// Typing indicator
|
||||
const typingMsg: ChatMessage = {
|
||||
sender: 'assistant',
|
||||
text: '🤖 ' + _('Thinking...'),
|
||||
@@ -104,7 +103,7 @@ export function renderAiChatModal() {
|
||||
'/usr/bin/tachyon',
|
||||
{ timeout: 120000 },
|
||||
)) as { success?: boolean; data?: unknown };
|
||||
chatHistory.pop(); // Remove typing indicator
|
||||
chatHistory.pop();
|
||||
|
||||
let answerText = _('Failed to receive response from AI service.');
|
||||
if (res && res.success) {
|
||||
@@ -153,7 +152,6 @@ export function renderAiChatModal() {
|
||||
if (e.key === 'Enter') handleSend();
|
||||
};
|
||||
|
||||
// Quick prompt buttons
|
||||
const quickPrompts = [
|
||||
{ label: '🩺 ' + _('Check YouTube'), query: 'Check YouTube availability' },
|
||||
{ label: '🔍 ' + _('Why Discord fails?'), query: 'Why Discord fails?' },
|
||||
|
||||
@@ -241,7 +241,6 @@ export function renderServiceCheckModal() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mode Switcher Toolbar
|
||||
const activeSectionsBtn = renderButton({
|
||||
text: 'Активные маршруты',
|
||||
classNames: [
|
||||
@@ -281,12 +280,10 @@ export function renderServiceCheckModal() {
|
||||
],
|
||||
);
|
||||
|
||||
// Extract unique sections for filter dropdown
|
||||
const sectionNames = Array.from(new Set(targets.map((t) => t.section)));
|
||||
let activeFilter = 'ALL';
|
||||
let searchQuery = '';
|
||||
|
||||
// Summary Stats Elements
|
||||
const totalStatEl = E(
|
||||
'b',
|
||||
{
|
||||
@@ -352,7 +349,6 @@ export function renderServiceCheckModal() {
|
||||
],
|
||||
);
|
||||
|
||||
// Progress Bar Container
|
||||
const progressBarInner = E('div', {
|
||||
style:
|
||||
'width: 0%; height: 100%; background: var(--color-success, #28a745); transition: width 0.2s ease;',
|
||||
@@ -366,7 +362,6 @@ export function renderServiceCheckModal() {
|
||||
[progressBarInner],
|
||||
);
|
||||
|
||||
// Section Filter Dropdown (compact select replacing noisy button grid)
|
||||
const sectionSelect = E(
|
||||
'select',
|
||||
{
|
||||
|
||||
@@ -30,7 +30,7 @@ export function shouldRefreshComponentStateBeforeRender(
|
||||
uiState?: Pick<Tachyon.UiState, 'actions'>,
|
||||
) {
|
||||
return Boolean(
|
||||
uiState?.actions.component.some((state) => state.running === true),
|
||||
uiState?.actions.component.some((state) => state.running),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -617,7 +617,7 @@ async function completeComponentActionJob(
|
||||
const shouldNotify = shouldNotifyOwnedUiAction('component', jobId);
|
||||
const modalController = getActiveProgressModalController();
|
||||
|
||||
if (!response.success || response.data.success === false) {
|
||||
if (!response.success || !response.data.success) {
|
||||
const message = response.success
|
||||
? response.data.message || _('Failed to execute')
|
||||
: response.error || _('Failed to execute');
|
||||
@@ -1071,7 +1071,6 @@ function getComponentCards(): ComponentCard[] {
|
||||
singBoxInstalled,
|
||||
);
|
||||
|
||||
// Add Sing-box variant actions (only show names, no 'Install' prefix)
|
||||
if (!singBoxStable) {
|
||||
singBoxActions.push({
|
||||
key: 'singBoxInstallStable',
|
||||
@@ -1226,7 +1225,6 @@ function renderComponentCard(card: ComponentCard) {
|
||||
|
||||
const checkResult = getVisibleCheckResult(card.component);
|
||||
|
||||
// 1. Header (displays Title, Current Version, no badges)
|
||||
const headerChildren: Node[] = [
|
||||
E('b', { class: 'tachyon_updates-page__component__title' }, card.title),
|
||||
E(
|
||||
@@ -1256,7 +1254,6 @@ function renderComponentCard(card: ComponentCard) {
|
||||
headerChildren,
|
||||
);
|
||||
|
||||
// 2. Details (renders status messages for check results)
|
||||
const detailsChildren: Node[] = [];
|
||||
|
||||
if (checkResult && checkResult.status) {
|
||||
@@ -1365,7 +1362,6 @@ function renderComponentCard(card: ComponentCard) {
|
||||
)
|
||||
: null;
|
||||
|
||||
// 3. Actions classification
|
||||
const primaryActions: ComponentActionButton[] = [];
|
||||
const dangerActions: ComponentActionButton[] = [];
|
||||
const variantActions: ComponentActionButton[] = [];
|
||||
@@ -1382,7 +1378,6 @@ function renderComponentCard(card: ComponentCard) {
|
||||
|
||||
const actionElements: Node[] = [];
|
||||
|
||||
// Render primary and danger buttons in a main row
|
||||
const primaryButtons = primaryActions.map((action) => {
|
||||
const loading = updatesActions[action.key].loading;
|
||||
const isUpdateOrInstall =
|
||||
@@ -1420,7 +1415,6 @@ function renderComponentCard(card: ComponentCard) {
|
||||
);
|
||||
}
|
||||
|
||||
// Render variant buttons if any
|
||||
if (variantActions.length > 0) {
|
||||
const variantButtons = variantActions.map((action) => {
|
||||
const loading = updatesActions[action.key].loading;
|
||||
|
||||
@@ -2832,7 +2832,6 @@ function render() {
|
||||
)
|
||||
),
|
||||
E("div", { class: "tachyon_dashboard-page__content" }, [
|
||||
// Widgets section
|
||||
E("div", { class: "tachyon_dashboard-page__widgets-section" }, [
|
||||
E(
|
||||
"div",
|
||||
@@ -2875,9 +2874,7 @@ function render() {
|
||||
})
|
||||
)
|
||||
]),
|
||||
// Clients section
|
||||
E("div", { id: "dashboard-connections-grid" }, []),
|
||||
// All outbounds
|
||||
E(
|
||||
"div",
|
||||
{ id: "dashboard-sections-grid" },
|
||||
@@ -3221,7 +3218,7 @@ async function isComponentActionStillRunning(jobId, component, action) {
|
||||
{ timeout: GET_UI_STATE_RPC_TIMEOUT_MS }
|
||||
);
|
||||
return response.success && response.data.actions.component.some(
|
||||
(state) => state.job_id === jobId && state.component === component && state.action === action && state.running === true
|
||||
(state) => state.job_id === jobId && state.component === component && state.action === action && state.running
|
||||
);
|
||||
}
|
||||
function componentActionFailure(response, parsedResponse) {
|
||||
@@ -6691,7 +6688,7 @@ async function completeSubscriptionUpdateJob(jobId, sectionName, response) {
|
||||
return;
|
||||
}
|
||||
const shouldNotify = jobId ? shouldNotifyOwnedUiAction("subscription", jobId) : false;
|
||||
const failed2 = !response.success || response.data.success === false;
|
||||
const failed2 = !response.success || !response.data.success;
|
||||
const message = response.success ? response.data.message || _("Failed to update subscriptions") : response.error || _("Failed to update subscriptions");
|
||||
if (failed2 && isTransientRpcError(message)) {
|
||||
void refreshRuntimeUiState({ force: true });
|
||||
@@ -12331,7 +12328,7 @@ async function handleServiceRuntimeAction({
|
||||
if (!result.success) {
|
||||
throw new Error(result.error);
|
||||
}
|
||||
if (result.data.success === false) {
|
||||
if (!result.data.success) {
|
||||
throw new Error(result.data.message || _("Service action failed"));
|
||||
}
|
||||
await waitForTachyonRunningState(expectedRunning);
|
||||
@@ -15916,7 +15913,7 @@ function shouldResetCheckResultsOnMount({
|
||||
}
|
||||
function shouldRefreshComponentStateBeforeRender(uiState) {
|
||||
return Boolean(
|
||||
uiState?.actions.component.some((state) => state.running === true)
|
||||
uiState?.actions.component.some((state) => state.running)
|
||||
);
|
||||
}
|
||||
function shouldExposeCheckResults({
|
||||
@@ -16718,7 +16715,7 @@ async function completeComponentActionJob(key, jobId, response) {
|
||||
}
|
||||
const shouldNotify = shouldNotifyOwnedUiAction("component", jobId);
|
||||
const modalController = getActiveProgressModalController();
|
||||
if (!response.success || response.data.success === false) {
|
||||
if (!response.success || !response.data.success) {
|
||||
const message = response.success ? response.data.message || _("Failed to execute") : response.error || _("Failed to execute");
|
||||
if (isTransientRpcError(message)) {
|
||||
setActionLoading(key, false);
|
||||
|
||||
@@ -23,8 +23,6 @@ function now_ms() {
|
||||
let common = require("core.common");
|
||||
let as_string = common.as_string;
|
||||
|
||||
// Creates an isolated bus. Each instance owns its subscriber table, dedup
|
||||
// state and counters, so tests can build a fresh bus per assertion.
|
||||
function bus() {
|
||||
let handlers = {}; // type -> array of subscriber records
|
||||
let last_emit = {}; // dedup key -> monotonic ms of last accepted emit
|
||||
|
||||
@@ -3083,7 +3083,6 @@ function diagnose_json() {
|
||||
let res = run_doctor_checks();
|
||||
let problems = [];
|
||||
|
||||
// Parse the text report lines into structured objects
|
||||
for (let line in split(res.report, "\n")) {
|
||||
line = trim(as_string(line));
|
||||
if (line == "") continue;
|
||||
@@ -3110,7 +3109,6 @@ function diagnose_json() {
|
||||
// "→" is U+2192, encoded as 3 UTF-8 bytes (E2 86 92); skip all 3
|
||||
let suggested_fix = arrow_idx >= 0 ? trim(substr(clean, arrow_idx + 3)) : "";
|
||||
|
||||
// Detect if already fixed inline (FIXED: prefix in suggested_fix)
|
||||
if (index(suggested_fix, "FIXED:") >= 0) {
|
||||
fixed_inline = true;
|
||||
severity = "info"; // was critical but already fixed
|
||||
|
||||
@@ -665,17 +665,15 @@ function nft_source_match_args(section, family, sets) {
|
||||
: [ "ip", "saddr", "@" + as_string(sets.sources) ];
|
||||
}
|
||||
|
||||
// Build a negative nftables source-IP match for excluded_ips.
|
||||
// nft accepts: ip saddr != { addr1, addr2, ... } as a single argument.
|
||||
function nft_excluded_source_match_args(section, family) {
|
||||
let excluded = list_option(section, "excluded_ips");
|
||||
if (length(excluded) == 0)
|
||||
return [];
|
||||
let ip_key = family == 6 ? "ip6" : "ip";
|
||||
let want_family = family;
|
||||
let addrs = [];
|
||||
for (let ip in excluded)
|
||||
if (core_ip.ip_family(as_string(ip)) == want_family)
|
||||
if (core_ip.ip_family(as_string(ip)) == family)
|
||||
push(addrs, as_string(ip));
|
||||
if (length(addrs) == 0)
|
||||
return [];
|
||||
|
||||
@@ -606,7 +606,6 @@ function stop_service(owner_pid) {
|
||||
}
|
||||
|
||||
let status = command_status_from_args([ BIN_PATH, "stop" ]);
|
||||
// Clean up orphaned logread -f processes left by watchdog instances.
|
||||
// These survive tachyon stop because background subprocesses (reload_firewall,
|
||||
// restart) inherit the logread pipe read-end, preventing SIGPIPE delivery.
|
||||
// Use pkill to target only logread in follow mode, not one-shot logread calls.
|
||||
|
||||
@@ -790,7 +790,6 @@ function start_main() {
|
||||
}
|
||||
|
||||
function start_impl() {
|
||||
// Clean up orphaned /tmp/tachyon-* temp files from processes killed mid-operation
|
||||
// (e.g. config generator killed during restart). 30-min threshold is safe since
|
||||
// all tachyon temp files are created and deleted within seconds under normal operation.
|
||||
command_status("find /tmp -maxdepth 1 -name 'tachyon-*' -mmin +30 -delete 2>/dev/null; true");
|
||||
|
||||
@@ -588,7 +588,6 @@ function reload_sing_box_runtime(previous_pid, config_hash_before, config_hash_a
|
||||
command_success_from_args([ "logger", "-t", "tachyon", "[info] Hot-reloading sing-box runtime (SIGHUP) PID: " + active_pid ]);
|
||||
if (active_pid > 0 && pid_is_sing_box(active_pid)) {
|
||||
if (command_success_from_args([ "kill", "-HUP", as_string(active_pid) ])) {
|
||||
// Verify process survived SIGHUP reload
|
||||
command_success_from_args([ "sleep", "1" ]);
|
||||
let check_pid = sing_box_service_pid_runtime();
|
||||
if (check_pid > 0 && pid_is_sing_box(check_pid)) {
|
||||
|
||||
@@ -1952,7 +1952,7 @@ function view_devices(token, chat_id, msg_id) {
|
||||
let ip = fields[2];
|
||||
let hostname = fields[3] == "*" ? "Неизвестно" : fields[3];
|
||||
|
||||
let is_blocked = blocked_macs[mac] ? true : false;
|
||||
let is_blocked = blocked_macs[mac];
|
||||
let status_icon = is_blocked ? "🚫" : "🟢";
|
||||
|
||||
text += status_icon + " <b>" + escape_html(hostname) + "</b>\n";
|
||||
@@ -2069,7 +2069,7 @@ function valid_updatable_component(name) {
|
||||
zapret2: 1,
|
||||
byedpi: 1
|
||||
};
|
||||
return allowed[name] ? true : false;
|
||||
return allowed[name];
|
||||
}
|
||||
|
||||
// ─── Dispatcher ──────────────────────────────────────────────────────────────
|
||||
@@ -2893,7 +2893,6 @@ function send_api(message) {
|
||||
let cfg = settings();
|
||||
if (cfg.enabled != "1" || !cfg.bot_token || !cfg.admin_ids) return 1;
|
||||
|
||||
// Check if this is a non-critical watchdog message and we are in quiet hours
|
||||
let is_critical = (index(message, "Упал") >= 0 || index(message, "Ошибка") >= 0);
|
||||
if (!is_critical && in_quiet_hours(cfg)) return 0;
|
||||
|
||||
|
||||
@@ -108,7 +108,6 @@ function switch_server(srv_name) {
|
||||
exec_output("sleep 1.5");
|
||||
}
|
||||
|
||||
// 1. Generate WireGuard keys
|
||||
let keypair_data = exec_output("/usr/bin/sing-box generate wg-keypair 2>/dev/null");
|
||||
let private_key = null;
|
||||
let public_key = null;
|
||||
@@ -135,7 +134,6 @@ if (!private_key || !public_key) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// 2. Perform registration
|
||||
let install_id = "";
|
||||
let fcm_token = "";
|
||||
let tos = exec_output("date -u +'%Y-%m-%dT%H:%M:%SZ' 2>/dev/null") || "2026-07-17T20:52:00Z";
|
||||
@@ -196,13 +194,11 @@ for (let prefix in api_prefixes) {
|
||||
let code = parsed.errors ? parsed.errors[0].code : null;
|
||||
|
||||
if (code == 1015 || index(as_string(msg), "1015") >= 0) {
|
||||
// Rotate proxy server if available
|
||||
if (length(available_servers) > 0 && server_try_index < 4) {
|
||||
let next_srv = available_servers[(server_try_index + 1) % length(available_servers)];
|
||||
server_try_index++;
|
||||
switch_server(next_srv);
|
||||
|
||||
// Retry request with rotated proxy server
|
||||
parsed = call_api("POST", url, "", tmp_req_file);
|
||||
if (parsed && parsed.success && parsed.result) {
|
||||
response_data = parsed.result;
|
||||
@@ -228,7 +224,6 @@ if (!response_data) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// 3. Make sure warp_enabled is true
|
||||
if (!response_data.warp_enabled) {
|
||||
let patch_body = { warp_enabled: true };
|
||||
let tmp_patch = exec_output("mktemp 2>/dev/null") || "/tmp/warp_patch.json";
|
||||
@@ -239,7 +234,6 @@ if (!response_data.warp_enabled) {
|
||||
fs.unlink(tmp_patch);
|
||||
}
|
||||
|
||||
// 4. Optionally apply WARP+ license key if provided as first arg
|
||||
let license_key = ARGV[0] || "";
|
||||
if (license_key != "") {
|
||||
let patch_body = { license_key: license_key };
|
||||
@@ -254,7 +248,6 @@ if (license_key != "") {
|
||||
// Restore original proxy server selection
|
||||
restore_original_server();
|
||||
|
||||
// 5. Select fastest endpoint IP using TCP latency probes (via curl)
|
||||
let probe_ips = [
|
||||
"162.159.192.1", "162.159.192.6",
|
||||
"162.159.193.1", "162.159.193.6",
|
||||
@@ -297,7 +290,6 @@ let warp_ports = [
|
||||
];
|
||||
let random_port = warp_ports[time() % length(warp_ports)];
|
||||
|
||||
// 6. Formulate CPS packets
|
||||
let warp_cps_payloads = [
|
||||
"<b 0xce000000010897a297ecc34cd6dd000044d0ec2e2e1ea2991f467ace4222129b5a098823784694b4897b9986ae0b7280135fa85e196d9ad980b150122129ce2a9379531b0fd3e871ca5fdb883c369832f730e272d7b8b74f393f9f0fa43f11e510ecb2219a52984410c204cf875585340c62238e14ad04dff382f2c200e0ee22fe743b9c6b8b043121c5710ec289f471c91ee414fca8b8be8419ae8ce7ffc53837f6ade262891895f3f4cecd31bc93ac5599e18e4f01b472362b8056c3172b513051f8322d1062997ef4a383b01706598d08d48c221d30e74c7ce000cdad36b706b1bf9b0607c32ec4b3203a4ee21ab64df336212b9758280803fcab14933b0e7ee1e04a7becce3e2633f4852585c567894a5f9efe9706a151b615856647e8b7dba69ab357b3982f554549bef9256111b2d67afde0b496f16962d4957ff654232aa9e845b61463908309cfd9de0a6abf5f425f577d7e5f6440652aa8da5f73588e82e9470f3b21b27b28c649506ae1a7f5f15b876f56abc4615f49911549b9bb39dd804fde182bd2dcec0c33bad9b138ca07d4a4a1650a2c2686acea05727e2a78962a840ae428f55627516e73c83dd8893b02358e81b524b4d99fda6df52b3a8d7a5291326e7ac9d773c5b43b8444554ef5aea104a738ed650aa979674bbed38da58ac29d87c29d387d80b526065baeb073ce65f075ccb56e47533aef357dceaa8293a523c5f6f790be90e4731123d3c6152a70576e90b4ab5bc5ead01576c68ab633ff7d36dcde2a0b2c68897e1acfc4d6483aaaeb635dd63c96b2b6a7a2bfe042f6aed82e5363aa850aace12ee3b1a93f30d8ab9537df483152a5527faca21efc9981b304f11fc95336f5b9637b174c5a0659e2b22e159a9fed4b8e93047371175b1d6d9cc8ab745f3b2281537d1c75fb9451871864efa5d184c38c185fd203de206751b92620f7c369e031d2041e152040920ac2c5ab5340bfc9d0561176abf10a147287ea90758575ac6a9f5ac9f390d0d5b23ee12af583383d994e22c0cf42383834bcd3ada1b3825a0664d8f3fb678261d57601ddf94a8a68a7c273a18c08aa99c7ad8c6c42eab67718843597ec9930457359dfdfbce024afc2dcf9348579a57d8d3490b2fa99f278f1c37d87dad9b221acd575192ffae1784f8e60ec7cee4068b6b988f0433d96d6a1b1865f4e155e9fe020279f434f3bf1bd117b717b92f6cd1cc9bea7d45978bcc3f24bda631a36910110a6ec06da35f8966c9279d130347594f13e9e07514fa370754d1424c0a1545c5070ef9fb2acd14233e8a50bfc5978b5bdf8bc1714731f798d21e2004117c61f2989dd44f0cf027b27d4019e81ed4b5c31db347c4a3a4d85048d7093cf16753d7b0d15e078f5c7a5205dc2f87e330a1f716738dce1c6180e9d02869b5546f1c4d2748f8c90d9693cba4e0079297d22fd61402dea32ff0eb69ebd65a5d0b687d87e3a8b2c42b648aa723c7c7daf37abcc4bb85caea2ee8f55bec20e913b3324ab8f5c3304f820d42ad1b9f2ffc1a3af9927136b4419e1e579ab4c2ae3c776d293d397d575df181e6cae0a4ada5d67ecea171cca3288d57c7bbdaee3befe745fb7d634f70386d873b90c4d6c6596bb65af68f9e5121e67ebf0d89d3c909ceedfb32ce9575a7758ff080724e1ab5d5f43074ecb53a479af21ed03d7b6899c36631c0166f9d47e5e1d4528a5d3d3f744029c4b1c190cbfbad06f5f83f7ad0429fa9a2719c56ffe3783460e166de2d8>",
|
||||
"<b 0xc7000000010809a1ed4edbbe7615000044d017a61a0d774f04290f119e701ef0035df2b0ed571b0b575e6a07246b856eb6ec036fef07f1e07b861251ad737abeb67e64be714c1dcd865312b1b6c35c089c997aeb5c18f808696fe97289513945d84ca846467603e94e44224877f2c1d3261e4ac18740be4bd064369c94fc08978d99b54bf615250998639010c1284248e1d73004b81fcb20b559d8a17eced7eab3964b5b88ca7a3b8579fc8c1c934189e77143b4ac434138114b1048651b56545b87acbef0952763538f3ddeb37cfc6d58b4881c3b719d7ff78f6ee1324a2914a32381c05a64c700466d280be007253bb030d179c4f1b3dc221e1974e2ee6d6e2b9e8d709159b5ef22e1783dbba845c20ca1c83b066c73835920ad70b806df0aee0351e3fc9ab1e42e8b2a30fe235ff0612eee19744949cecee0463b76514ad90c1f7ceaa557c18586ab561d49482e73c85d0143785da14a441bf82f78783b61cccd44aecb1947516e79b5ca5a6b3a8aed6040fae0eeabdc55a88dc19ade832d99fca90c7a629cacc07192d7e47e3c6a271b95b0ea3392562a06a1cab79f40ea92916ebee197b7b5f14b251824e1ed20ff2ca80b1f03a43e45157589bc61b978e97851025b3b7ccc17d291e1cb60fe48a5c26829dce11dd23c2e73265a9ebf8617c985e4fee4681e863f990061f4dea465a7d2524bd0edcf4b48d4b8f25fc359b15babd2637284a4774077dca60091f1a781cfee1bef9713dd5943a579d7470bc5970542fbb27fdf77880a8d8751b1f642c7a3f019a05ab94bf63d3525ef34e9290b5c8d477f2714e6d6e3e4d35c1983f5e16fda57fcdf071b513f8f088dbe8d5a97577d17a5383a496c3f313adfdd47c962bbaebd6aa13b46439eb742622c29ca067db0ec1853064c3cbbffe0a215a19fce47d49703ed58ebbd89721172d256d1cf30188106fb2f863186511401fad54d087aa2fb3d1b85768db386bd7102e8060ac157bac011acdcdae2799b9aee1467c3424013455bd028fcaacdc3c77d28ea199967d617ea7d0d0815f3cc407934a76d1293dccba210d1709a13e5dd67c9ba47cd113f5bdd740358eff13164159fd09bc2f7ec6cfa64d9df7e2e2f88706b0ff3a92ccf6f078456cfe0bdd89292cfe2680badc1eac9f7d36efe8eb6912c7b164508d13e6c0911c15f73c233cbe4fc70ff2ade1e1be4bbb738e0939159e2078a9438f05b756a003371f4861481c38f1cdd2d7b06deb62869e9fe79a8abaa920646fa2e8fa28f0d80c136376c7b56046bae4c05c0cdf64efb8c47bbfc5a1a4c0b045061ef0d71618e0d206a1d7f245fd5c03191b152673ba8dff8e1b8de7c50234a93cba91e3888adb228cc02beded4b1c0946797d3ef02dec2edb6ad0ac21f89f4be364c317da7c22440e9f358d512203f4b7ab20388af68b8915d0152db2c8a0687bfaea870f7529bb92a22b35bd79bc6d490591406346ecd78342ee3563c4883a8251679691c2d4e963397e24653520795511b018915374c954bddb940a9d7a16d1c8bd798fc7dbfb0599a7074e13f87e14efa8d511bb2579ec029b1bda18fe971b30fbe19e986ff2686a69bf3f1bb929de93ae70345ebca998b11e0a2b41890cba628d8f6e7c4e94790735e5299b4ff07cd3080f7d53c9cbe1911d2cd5925b3213e033c272506a87886cf761a283a779564d3241e3c28f632e166b5d756e1786ce077614c4444e3f2aed5decb3613b925ea3e558c21d4faf8ba54edd0f3a5d4>",
|
||||
@@ -324,7 +316,6 @@ let i3 = to_hex(generate_extra_cps());
|
||||
let i4 = to_hex(generate_extra_cps());
|
||||
let i5 = to_hex(generate_extra_cps());
|
||||
|
||||
// 7. Output result
|
||||
write_json({
|
||||
success: true,
|
||||
local_address: response_data.config.interface.addresses.v4 + "/32 " + response_data.config.interface.addresses.v6 + "/128",
|
||||
|
||||
@@ -821,7 +821,6 @@ function ai_heal_subnet_cache() {
|
||||
// /tmp file missing or empty, restore from persistent storage
|
||||
let content = fs.readfile(etc_path);
|
||||
if (content == null || content == "") continue;
|
||||
// Ensure /tmp dir exists
|
||||
command_success_from_args(["mkdir", "-p", tmp_dir]);
|
||||
if (fs.writefile(tmp_path, content) != null) {
|
||||
push(restored, entry);
|
||||
@@ -1072,15 +1071,13 @@ function ai_heal_dns_loop() {
|
||||
let detour_section = common.option(cfg, "dns_detour_section", "");
|
||||
if (detour_section == "") return;
|
||||
|
||||
// Check if DNS is working
|
||||
if (controller.is_dns_working()) {
|
||||
// DNS works — if we were in recovery, try to restore
|
||||
let recovery = read_dns_recovery_state();
|
||||
if (recovery.phase == "detour_disabled") {
|
||||
let reenable_cooldown = recovery.ts ? (now - int(recovery.ts)) : 0;
|
||||
if (reenable_cooldown < 300) return;
|
||||
let test_dns = controller.is_dns_working();
|
||||
if (test_dns) {
|
||||
if (controller.is_dns_working()) {
|
||||
log_message("DNS loop recovery: DNS works, re-enabling DNS detour section", "info");
|
||||
system("/sbin/uci set tachyon.settings.dns_detour_enabled='1' >/dev/null 2>&1");
|
||||
system("/sbin/uci commit tachyon >/dev/null 2>&1");
|
||||
@@ -1108,7 +1105,6 @@ function ai_heal_dns_loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if detour section is empty
|
||||
let cache_path = "/var/run/tachyon/section-cache/" + detour_section + ".json";
|
||||
let cache = common.object_or_empty(common.read_json_file(cache_path));
|
||||
let servers = common.array_or_empty(cache.servers);
|
||||
@@ -1683,7 +1679,6 @@ function worker() {
|
||||
ai_heal_subnet_cache();
|
||||
run_zero_rtt_prefetching();
|
||||
|
||||
// Ensure /etc/tachyon exists for persistent smart detect
|
||||
// Throws when it already exists, which is the expected case on every boot
|
||||
// after the first.
|
||||
try { fs.mkdir("/etc/tachyon"); } catch(e) {}
|
||||
|
||||
@@ -126,7 +126,6 @@ function parse_text_domains(text) {
|
||||
// Strip inline comments
|
||||
line = replace(line, /[ \t]+\/\/.*$/, "");
|
||||
line = replace(line, /[ \t]+#.*$/, "");
|
||||
// Split by comma or whitespace for multi-domain lines
|
||||
for (let token in split(line, /[,\s]+/)) {
|
||||
token = trim(token);
|
||||
// Strip leading dots (domain_suffix style) and wildcards
|
||||
|
||||
@@ -50,7 +50,6 @@ let url_query_params = runtime_url.query_params;
|
||||
|
||||
const CONFIG_NAME = "tachyon";
|
||||
|
||||
// Convert a UCI value that may be a binary buffer (<b 0x...>) to a plain hex string.
|
||||
// UCI stores values written as '<b 0x...>' strings as binary, which when
|
||||
// serialized back to JSON appear as '<b 0x...>' — invalid for sing-box.
|
||||
function uci_bin_to_hex(val) {
|
||||
@@ -445,7 +444,6 @@ function base_config(settings, service_address, runtime_context) {
|
||||
}
|
||||
}
|
||||
|
||||
// Read remote hosts lists cache file
|
||||
let hosts_cache_st = fs.stat(HOSTS_CACHE_FILE);
|
||||
if (hosts_cache_st != null && int(hosts_cache_st.size) > 0) {
|
||||
let fh = null;
|
||||
|
||||
@@ -977,7 +977,7 @@ function add_subscription_source_with_state(config, section, source_index, sourc
|
||||
let added = 0;
|
||||
for (let i = 0; i < length(prepared); i++) {
|
||||
let outbound = prepared[i];
|
||||
let is_group = group_flags[i] === true;
|
||||
let is_group = group_flags[i];
|
||||
if (is_group && length(array_or_empty(outbound.outbounds)) == 0) {
|
||||
warn("skipped empty subscription group for rule '", section_name, "': ", as_string(display_names[i] || outbound.tag || "unknown"), "\n");
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user