diff --git a/honk/Makefile b/honk/Makefile index f6759b767..1bb90fc2c 100644 --- a/honk/Makefile +++ b/honk/Makefile @@ -5,11 +5,11 @@ include $(TOPDIR)/rules.mk PKG_NAME:=honk -PKG_VERSION:=2026.11.12~a09d3bf -PKG_RELEASE:=35 +PKG_VERSION:=2026.11.13~f8bbf4f +PKG_RELEASE:=36 HONK_SRC_URL:=https://github.com/daeuniverse/honk.git -HONK_SRC_VERSION:=a09d3bfa272f3670d6606373452be41606bdc29e +HONK_SRC_VERSION:=f8bbf4f4b4ab1cdf673c440df316c3c642a31f9c PKG_LICENSE:=AGPL-3.0-only PKG_LICENSE_FILES:=LICENSE diff --git a/kai/Makefile b/kai/Makefile index 10539622d..6760bf00e 100644 --- a/kai/Makefile +++ b/kai/Makefile @@ -11,7 +11,7 @@ PKG_ARCH_kai:=$(ARCH) PKG_NAME:=kai PKG_VERSION:=0.0.28 -PKG_RELEASE:=13 +PKG_RELEASE:=14 PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/kai-runtime-v$(PKG_VERSION)/ PKG_HASH:=skip @@ -27,7 +27,7 @@ define Package/$(PKG_NAME) CATEGORY:=System SUBMENU:= TITLE:=KAI - DEPENDS:=+kai_session +kai-agent +luci-lib-linkeaseauth +linkease-app-entry + DEPENDS:=+kai_session +kai-agent +luci-lib-linkeaseauth URL:=https://github.com/linkease/istore-ai-helper endef @@ -41,13 +41,15 @@ endef define Package/$(PKG_NAME)/postinst #!/bin/sh -# Compatibility entry for desktops which only scan the old directory. +# KaiPlus owns the desktop entry. Retire only links created by KAI. root="$${IPKG_INSTROOT:-}" -legacy="$${root}/usr/share/linkeasefull/desktop-apps.d/05-kai-plugin.json" -if [ ! -e "$${legacy}" ] && [ ! -L "$${legacy}" ]; then - mkdir -p "$${legacy%/*}" - ln -s /usr/share/kai/kai-plugin.json "$${legacy}" || exit $$? -fi +for directory in /usr/share/linkease/apps.d /usr/share/linkeasefull/desktop-apps.d; do + registration="$${root}$${directory}/05-kai-plugin.json" + if [ "$$(readlink "$${registration}" 2>/dev/null)" = "/usr/share/kai/kai-plugin.json" ]; then + rm -f "$${registration}" + fi +done +rmdir "$${root}/usr/share/linkeasefull/desktop-apps.d" 2>/dev/null || true exit 0 endef @@ -71,14 +73,11 @@ define Build/Compile endef define Package/$(PKG_NAME)/install - $(INSTALL_DIR) $(1)/usr/sbin $(1)/usr/libexec $(1)/etc/init.d $(1)/etc/config $(1)/usr/share/kai/www $(1)/usr/share/linkease/apps.d + $(INSTALL_DIR) $(1)/usr/sbin $(1)/usr/libexec $(1)/etc/init.d $(1)/etc/config $(INSTALL_BIN) $(PKG_BUILD_DIR)/kai.$(PKG_ARCH_kai) $(1)/usr/sbin/kai_bin $(INSTALL_BIN) ./files/kai-session-launch $(1)/usr/libexec/kai-session-launch $(INSTALL_BIN) ./files/kai.init $(1)/etc/init.d/kai $(INSTALL_CONF) ./files/kai.config $(1)/etc/config/kai - $(INSTALL_DATA) ./files/kai-plugin.json $(1)/usr/share/kai/kai-plugin.json - $(INSTALL_DATA) ./files/logo.png $(1)/usr/share/kai/www/logo.png - ln -sf /usr/share/kai/kai-plugin.json $(1)/usr/share/linkease/apps.d/05-kai-plugin.json endef $(eval $(call BuildPackage,$(PKG_NAME))) diff --git a/kai/files/kai-plugin.json b/kai/files/kai-plugin.json deleted file mode 100644 index c0d30d58b..000000000 --- a/kai/files/kai-plugin.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "schemaVersion": 1, - "id": "kai", - "name": "KAI", - "icon": "logo.png", - "staticRoot": "/usr/share/kai/www", - "desktop": { - "mode": "iframe", - "target": { - "scheme": "http", - "hostMode": "request-host", - "port": { - "default": 8197, - "keys": { - "uci": "kai.@kai[0].port" - } - }, - "path": "/apps/kai/" - }, - "access": { - "scope": "lan", - "blockedTitle": "请在局域网内打开 KAI", - "blockedMessage": "KAI 运行在设备的独立服务端口,请连接设备所在局域网后再打开。" - } - }, - "standalone": { - "basePath": "/apps/kai/", - "externalOpen": { - "enabled": true, - "label": "在浏览器中打开 KAI" - } - }, - "config": { - "providerOrder": ["uci"], - "providers": { - "uci": { - "type": "uci" - } - } - }, - "backend": { - "type": "http", - "transport": "tcp", - "scheme": "http", - "host": "127.0.0.1", - "portFromUci": "kai.@kai[0].port", - "defaultPort": 8197, - "upstreamBasePath": "/apps/kai/", - "pathMode": "preserve", - "proxyMode": "app-base" - }, - "window": { - "width": 1180, - "height": 760, - "singleton": true - }, - "capabilities": ["assistant", "chat"], - "categories": ["other"] -} diff --git a/kai/files/logo.png b/kai/files/logo.png deleted file mode 100644 index 4a7fa7b63..000000000 Binary files a/kai/files/logo.png and /dev/null differ diff --git a/luci-app-fchomo/htdocs/luci-static/resources/fchomo.js b/luci-app-fchomo/htdocs/luci-static/resources/fchomo.js index aa2088745..5db48a9da 100644 --- a/luci-app-fchomo/htdocs/luci-static/resources/fchomo.js +++ b/luci-app-fchomo/htdocs/luci-static/resources/fchomo.js @@ -1312,7 +1312,7 @@ function loadModalTitle(title, addtitle, section_id) { return label ? title + ' » ' + label : addtitle; } -function loadLabel(preadds, section_id) { +function loadChoices(preadds, section_id) { delete this.keylist; delete this.vallist; @@ -1322,7 +1322,7 @@ function loadLabel(preadds, section_id) { return this.super('load', section_id); } -function loadLabelValues(uciconfig, sectiontype, options = {}) { +function loadChoicesGetMap(uciconfig, sectiontype, options = {}) { const values = []; switch (sectiontype) { @@ -2106,8 +2106,8 @@ return baseclass.extend({ // load loadDefaultLabel, loadModalTitle, - loadLabel, - loadLabelValues, + loadChoices, + loadChoicesGetMap, // render renderStatus, updateStatus, diff --git a/luci-app-fchomo/htdocs/luci-static/resources/fchomo/listeners.js b/luci-app-fchomo/htdocs/luci-static/resources/fchomo/listeners.js index 9a262cc2c..41eeb6f06 100644 --- a/luci-app-fchomo/htdocs/luci-static/resources/fchomo/listeners.js +++ b/luci-app-fchomo/htdocs/luci-static/resources/fchomo/listeners.js @@ -627,9 +627,9 @@ function renderListeners(s, uciconfig, isClient) { _('Name of the Sub rule used for inbound matching.')); o.value.apply(o, ['', _('none')]); o.load = function(section_id) { - return hm.loadLabel.call(this, [ + return hm.loadChoices.call(this, [ ['', _('none')], - ...hm.loadLabelValues(this.config, 'subrule-group') + ...hm.loadChoicesGetMap(this.config, 'subrule-group') ], section_id); } o.modalonly = false; @@ -642,9 +642,9 @@ function renderListeners(s, uciconfig, isClient) { o.value.apply(o, res); }) o.load = function(section_id) { - return hm.loadLabel.call(this, [ + return hm.loadChoices.call(this, [ ...hm.preset_outbound.direct, - ...hm.loadLabelValues(this.config, 'proxy_group') + ...hm.loadChoicesGetMap(this.config, 'proxy_group') ], section_id); } o.modalonly = false; @@ -789,9 +789,9 @@ function renderListeners(s, uciconfig, isClient) { o.value.apply(o, res); }) o.load = function(section_id) { - return hm.loadLabel.call(this, [ + return hm.loadChoices.call(this, [ ...hm.preset_outbound.direct, - ...hm.loadLabelValues(this.config, 'proxy_group') + ...hm.loadChoicesGetMap(this.config, 'proxy_group') ], section_id); } o.depends('tls_reality', '1'); @@ -1016,9 +1016,9 @@ function renderListeners(s, uciconfig, isClient) { o.value.apply(o, res); }) o.load = function(section_id) { - return hm.loadLabel.call(this, [ + return hm.loadChoices.call(this, [ ...hm.preset_outbound.direct, - ...hm.loadLabelValues(this.config, 'proxy_group') + ...hm.loadChoicesGetMap(this.config, 'proxy_group') ], section_id); } o.depends('hysteria2_realm', '1'); diff --git a/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/client.js b/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/client.js index d0c853f37..071093c73 100644 --- a/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/client.js +++ b/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/client.js @@ -609,9 +609,9 @@ function renderPayload(s, total, uciconfig) { initPayload(o, n, 'factor'); const inname_old_load = o.load; o.load = function(section_id) { - hm.loadLabel.call(this, [ + hm.loadChoices.call(this, [ ['', _('-- Please choose --')], - ...hm.loadLabelValues(this.config, 'inbound') + ...hm.loadChoicesGetMap(this.config, 'inbound') ], section_id); return inname_old_load.apply(this, arguments); @@ -624,9 +624,9 @@ function renderPayload(s, total, uciconfig) { initPayload(o, n, 'factor'); const rematchname_old_load = o.load; o.load = function(section_id) { - hm.loadLabel.call(this, [ + hm.loadChoices.call(this, [ ['', _('-- Please choose --')], - ...hm.loadLabelValues(this.config, 'rematch-name') + ...hm.loadChoicesGetMap(this.config, 'rematch-name') ], section_id); return rematchname_old_load.apply(this, arguments); @@ -661,7 +661,7 @@ function renderPayload(s, total, uciconfig) { initPayload(o, n, 'factor'); const rule_set_old_load = o.load; o.load = function(section_id) { - hm.loadLabel.call(this, hm.loadLabelValues(this.config, 'ruleset'), section_id); + hm.loadChoices.call(this, hm.loadChoicesGetMap(this.config, 'ruleset'), section_id); return rule_set_old_load.apply(this, arguments); } @@ -712,13 +712,13 @@ function renderPayload(s, total, uciconfig) { initDynamicPayload(o, n, 'factor'); const fused_old_load = o.load; o.load = function(section_id) { - hm.loadLabel.call(this, [ + hm.loadChoices.call(this, [ ...hm.inbound_type.map(e => [e, _('IN-TYPE')]), // Multi-values are not supported - ...hm.loadLabelValues(this.config, 'inbound').map(e => [e, _('IN-NAME')]), - ...hm.loadLabelValues(this.config, 'rematch-name').map(e => [e, _('REMATCH-NAME')]), + ...hm.loadChoicesGetMap(this.config, 'inbound').map(e => [e, _('IN-NAME')]), + ...hm.loadChoicesGetMap(this.config, 'rematch-name').map(e => [e, _('REMATCH-NAME')]), [['udp', _('UDP')], _('NETWORK')], [['tcp', _('TCP')], _('NETWORK')], - ...hm.loadLabelValues(this.config, 'ruleset').map(e => [e, _('RULE-SET')]) + ...hm.loadChoicesGetMap(this.config, 'ruleset').map(e => [e, _('RULE-SET')]) ], section_id); return fused_old_load.apply(this, arguments); @@ -823,9 +823,9 @@ function renderRules(s, uciconfig) { initEntryOption(o, 'detour'); const detour_old_load = o.load; o.load = function(section_id) { - hm.loadLabel.call(this, [ + hm.loadChoices.call(this, [ ...hm.preset_outbound.full, - ...hm.loadLabelValues(this.config, 'proxy_group') + ...hm.loadChoicesGetMap(this.config, 'proxy_group') ], section_id); return detour_old_load.apply(this, arguments); @@ -878,9 +878,9 @@ function renderPolicies(s, uciconfig) { _('Match rule set.')); o.value('', _('-- Please choose --')); o.load = function(section_id) { - return hm.loadLabel.call(this, [ + return hm.loadChoices.call(this, [ ['', _('-- Please choose --')], - ...hm.loadLabelValues(this.config, 'ruleset', {behaviors: ['domain', 'classical']}) + ...hm.loadChoicesGetMap(this.config, 'ruleset', {behaviors: ['domain', 'classical']}) ], section_id); } o.depends('type', 'rule_set'); @@ -909,9 +909,9 @@ function renderPolicies(s, uciconfig) { o.value.apply(o, res); }) o.load = function(section_id) { - return hm.loadLabel.call(this, [ + return hm.loadChoices.call(this, [ ...hm.preset_outbound.direct, - ...hm.loadLabelValues(this.config, 'proxy_group') + ...hm.loadChoicesGetMap(this.config, 'proxy_group') ], section_id); } o.editable = true; @@ -1068,9 +1068,9 @@ return view.extend({ }) so.keep_order = true; so.load = function(section_id) { - return hm.loadLabel.call(this, [ + return hm.loadChoices.call(this, [ ...hm.preset_outbound.proxy, - ...hm.loadLabelValues(this.config, 'proxy_group') + ...hm.loadChoicesGetMap(this.config, 'proxy_group') ], section_id); } so.editable = true; @@ -1079,9 +1079,9 @@ return view.extend({ so.value('', _('-- Please choose --')); so.keep_order = true; so.load = function(section_id) { - return hm.loadLabel.call(this, [ + return hm.loadChoices.call(this, [ ['', _('-- Please choose --')], - ...hm.loadLabelValues(this.config, 'node') + ...hm.loadChoicesGetMap(this.config, 'node') ], section_id); } so.validate = function(section_id, value) { @@ -1099,9 +1099,9 @@ return view.extend({ so.value('', _('-- Please choose --')); so.keep_order = true; so.load = function(section_id) { - return hm.loadLabel.call(this, [ + return hm.loadChoices.call(this, [ ['', _('-- Please choose --')], - ...hm.loadLabelValues(this.config, 'provider') + ...hm.loadChoicesGetMap(this.config, 'provider') ], section_id); } so.validate = function(section_id, value) { @@ -1136,10 +1136,10 @@ return view.extend({ so.value.apply(so, res); }) so.load = function(section_id) { - return hm.loadLabel.call(this, [ + return hm.loadChoices.call(this, [ ...hm.preset_outbound.proxy, ['NODE', _('-- PROXY-NODE --')], - ...hm.loadLabelValues(this.config, 'node') + ...hm.loadChoicesGetMap(this.config, 'node') ], section_id); } so.textvalue = hm.textvalue2Value; @@ -1201,12 +1201,12 @@ return view.extend({ so.value.apply(so, res); }) so.load = function(section_id) { - return hm.loadLabel.call(this, [ + return hm.loadChoices.call(this, [ ...hm.preset_outbound.proxy, ['GROUP', _('-- PROXY-GROUP --')], - ...hm.loadLabelValues(this.config, 'proxy_group'), + ...hm.loadChoicesGetMap(this.config, 'proxy_group'), ['NODE', _('-- PROXY-NODE --')], - ...hm.loadLabelValues(this.config, 'node') + ...hm.loadChoicesGetMap(this.config, 'node') ], section_id); } so.depends('type', 'select'); @@ -1359,9 +1359,9 @@ return view.extend({ so = ss.option(form.ListValue, 'SUB-RULE', _('SUB-RULE')); so.load = function(section_id) { - hm.loadLabel.call(this, [ + hm.loadChoices.call(this, [ ['', _('-- Please choose --')], - ...hm.loadLabelValues(this.config, 'subrule-group') + ...hm.loadChoicesGetMap(this.config, 'subrule-group') ], section_id); return this.section.getOption('entry').instget(section_id).subrule || ''; @@ -1646,9 +1646,9 @@ return view.extend({ ); const detour_old_load = so.load; so.load = function(section_id) { - hm.loadLabel.call(this, [ + hm.loadChoices.call(this, [ ...hm.preset_outbound.dns, - ...hm.loadLabelValues(this.config, 'proxy_group') + ...hm.loadChoicesGetMap(this.config, 'proxy_group') ], section_id); return detour_old_load.apply(this, arguments); diff --git a/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/node.js b/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/node.js index 6f56fa495..928a2a7aa 100644 --- a/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/node.js +++ b/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/node.js @@ -301,9 +301,9 @@ return view.extend({ so = ss.taboption('field_general', form.ListValue, 'target_sub_rule', _('Use sub rule')); so.load = function(section_id) { - return hm.loadLabel.call(this, [ + return hm.loadChoices.call(this, [ ['', _('-- Please choose --')], - ...hm.loadLabelValues(this.config, 'subrule-group') + ...hm.loadChoicesGetMap(this.config, 'subrule-group') ], section_id); } so.validate = function(section_id, value) { @@ -2426,9 +2426,9 @@ return view.extend({ so.value.apply(so, res); }) so.load = function(section_id) { - return hm.loadLabel.call(this, [ + return hm.loadChoices.call(this, [ ...hm.preset_outbound.direct, - ...hm.loadLabelValues(this.config, 'proxy_group') + ...hm.loadChoicesGetMap(this.config, 'proxy_group') ], section_id); } so.textvalue = hm.textvalue2Value; @@ -2751,9 +2751,9 @@ return view.extend({ so = ss.option(form.ListValue, 'chain_head_sub', _('Destination provider')); so.load = function(section_id) { - return hm.loadLabel.call(this, [ + return hm.loadChoices.call(this, [ ['', _('-- Please choose --')], - ...hm.loadLabelValues(this.config, 'provider') + ...hm.loadChoicesGetMap(this.config, 'provider') ], section_id); } so.rmempty = false; @@ -2762,9 +2762,9 @@ return view.extend({ so = ss.option(form.ListValue, 'chain_head', _('Destination proxy node')); so.load = function(section_id) { - return hm.loadLabel.call(this, [ + return hm.loadChoices.call(this, [ ['', _('-- Please choose --')], - ...hm.loadLabelValues(this.config, 'node') + ...hm.loadChoicesGetMap(this.config, 'node') ], section_id); } so.rmempty = false; @@ -2781,9 +2781,9 @@ return view.extend({ so = ss.option(form.ListValue, 'chain_tail_group', _('Transit proxy group')); so.load = function(section_id) { - return hm.loadLabel.call(this, [ + return hm.loadChoices.call(this, [ ['', _('-- Please choose --')], - ...hm.loadLabelValues(this.config, 'proxy_group') + ...hm.loadChoicesGetMap(this.config, 'proxy_group') ], section_id); } so.rmempty = false; @@ -2792,9 +2792,9 @@ return view.extend({ so = ss.option(form.ListValue, 'chain_tail', _('Transit proxy node')); so.load = function(section_id) { - return hm.loadLabel.call(this, [ + return hm.loadChoices.call(this, [ ['', _('-- Please choose --')], - ...hm.loadLabelValues(this.config, 'node') + ...hm.loadChoicesGetMap(this.config, 'node') ], section_id); } so.rmempty = false; diff --git a/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/ruleset.js b/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/ruleset.js index 9758d8eef..fb869091d 100644 --- a/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/ruleset.js +++ b/luci-app-fchomo/htdocs/luci-static/resources/view/fchomo/ruleset.js @@ -418,9 +418,9 @@ return view.extend({ o.value.apply(o, res); }) o.load = function(section_id) { - return hm.loadLabel.call(this, [ + return hm.loadChoices.call(this, [ ...hm.preset_outbound.direct, - ...hm.loadLabelValues(this.config, 'proxy_group') + ...hm.loadChoicesGetMap(this.config, 'proxy_group') ], section_id); } o.textvalue = hm.textvalue2Value; diff --git a/luci-app-kaiplus/Makefile b/luci-app-kaiplus/Makefile index da53c1f5d..1163de695 100644 --- a/luci-app-kaiplus/Makefile +++ b/luci-app-kaiplus/Makefile @@ -7,8 +7,8 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=KaiPlus -PKG_VERSION:=1.0.11-r1 -PKG_RELEASE:=16 +PKG_VERSION:=1.0.11-r2 +PKG_RELEASE:=17 LUCI_DEPENDS:=+kaiplus +luci-compat +luci-lib-taskd +luci-lib-linkeaseauth +linkease-app-entry LUCI_MINIFY_CSS:=0 LUCI_MINIFY_JS:=0 diff --git a/luci-app-kaiplus/luasrc/model/kaiplus/classic.lua b/luci-app-kaiplus/luasrc/model/kaiplus/classic.lua index 77eb689c7..04ad17999 100644 --- a/luci-app-kaiplus/luasrc/model/kaiplus/classic.lua +++ b/luci-app-kaiplus/luasrc/model/kaiplus/classic.lua @@ -9,14 +9,16 @@ function classic.installed() and fs.access("/usr/sbin/kai_session", "x") and fs.access("/etc/init.d/kai", "x") and fs.access("/etc/config/kai") - and fs.access("/usr/share/kai/kai-plugin.json") ) end function classic.status() local installed = classic.installed() + local port = tonumber(require("luci.model.uci").cursor():get_first("kai", "kai", "port") or "8197") + if not port or port < 1 or port > 65535 or port % 1 ~= 0 then port = nil end return { installed = installed, + port = port, running = installed and sys.call("pidof kai_bin >/dev/null") == 0 } end diff --git a/luci-app-kaiplus/luasrc/view/kaiplus/classic_status.htm b/luci-app-kaiplus/luasrc/view/kaiplus/classic_status.htm index dd11c1d29..d2c91d3d9 100644 --- a/luci-app-kaiplus/luasrc/view/kaiplus/classic_status.htm +++ b/luci-app-kaiplus/luasrc/view/kaiplus/classic_status.htm @@ -22,7 +22,8 @@ var classicPageInstalled = <%= installed and "true" or "false" %>; var classicInstallUrl = '<%=url("admin/services/kaiplus/install_kai")%>'; var classicUpdateUrl = '<%=url("admin/services/kaiplus/update_kai")%>'; -var classicOpenUrl = '<%=url("admin/services/linkease_apps/open")%>?id=kai'; +var classicAuthUrl = '<%=url("admin/services/linkease_auth/auth")%>'; +var classicOpenUrl = ''; function installClassicKai() { classicPackageTask(false); @@ -83,7 +84,11 @@ XHR.poll(5, '<%=url("admin/services/kaiplus/classic_status")%>', null, function( installButton.style.display = st.installed ? 'none' : ''; updateButton.style.display = st.installed ? '' : 'none'; updateHint.style.display = st.installed ? '' : 'none'; - openButton.style.display = st.installed && st.running ? '' : 'none'; + var port = Number(st.port); + classicOpenUrl = port >= 1 && port <= 65535 && port === Math.floor(port) + ? classicAuthUrl + '?return=' + encodeURIComponent('http://' + window.location.hostname + ':' + port + '/apps/kai/') + : ''; + openButton.style.display = st.installed && st.running && classicOpenUrl ? '' : 'none'; }); //]]> diff --git a/luci-app-kaiplus/tests/classic_kai_browser.cjs b/luci-app-kaiplus/tests/classic_kai_browser.cjs index 76a26d22e..9a58462ff 100644 --- a/luci-app-kaiplus/tests/classic_kai_browser.cjs +++ b/luci-app-kaiplus/tests/classic_kai_browser.cjs @@ -1,6 +1,6 @@ const assert = require('node:assert/strict'); const vm = require('node:vm'); -function page(installed) { +function page(installed, hostname = '192.168.30.7') { const elements = Object.fromEntries(['classic-kai-status', 'classic-kai-install', 'classic-kai-update', 'classic-kai-update-hint', 'classic-kai-open'] .map(id => [id, { style: {}, disabled: false }])); const state = { elements, alerts: [], logs: [], reloads: 0 }; @@ -11,7 +11,7 @@ function page(installed) { XHR, document: { getElementById: id => elements[id] }, window: { confirm: () => true, alert: message => state.alerts.push(message), - location: { reload: () => state.reloads++ }, + location: { hostname, reload: () => state.reloads++ }, taskd: { csrfToken: 'test-token', show_log: id => state.logs.push(id) } } }; @@ -46,13 +46,21 @@ state.context.installClassicKai(); state.post.callback({ responseText: '{"ok":true,"installed":true}' }); assert.equal(state.reloads, 1); state = page(true); -state.poll(null, { installed: true, running: true }); +state.poll(null, { installed: true, running: true, port: 8197 }); assert.equal(state.elements['classic-kai-install'].style.display, 'none'); assert.equal(state.elements['classic-kai-update'].style.display, ''); assert.equal(state.elements['classic-kai-open'].style.display, ''); assert.equal(state.elements['classic-kai-status'].style.color, 'green'); assert.equal(state.reloads, 0); -assert.equal(state.context.classicOpenUrl, '/cgi-bin/luci/admin/services/linkease_apps/open?id=kai'); +assert.equal(state.context.classicOpenUrl, '/cgi-bin/luci/admin/services/linkease_auth/auth?return=' + encodeURIComponent('http://192.168.30.7:8197/apps/kai/')); +const ipv6 = page(true, '[fd00::7]'); +ipv6.poll(null, { installed: true, running: true, port: 8201 }); +assert.equal(ipv6.context.classicOpenUrl, '/cgi-bin/luci/admin/services/linkease_auth/auth?return=' + encodeURIComponent('http://[fd00::7]:8201/apps/kai/')); +for (const port of [0, 65536, 1.5, 'invalid', undefined]) { + ipv6.poll(null, { installed: true, running: true, port }); + assert.equal(ipv6.context.classicOpenUrl, ''); + assert.equal(ipv6.elements['classic-kai-open'].style.display, 'none'); +} state.poll(null, { installed: true, running: false }); assert.equal(state.elements['classic-kai-open'].style.display, 'none'); assert.equal(state.elements['classic-kai-update'].style.display, ''); diff --git a/luci-app-kaiplus/tests/classic_kai_runtime.lua b/luci-app-kaiplus/tests/classic_kai_runtime.lua index b3e3b996a..49f714b11 100644 --- a/luci-app-kaiplus/tests/classic_kai_runtime.lua +++ b/luci-app-kaiplus/tests/classic_kai_runtime.lua @@ -5,7 +5,7 @@ local function check(value, message) assert(value, message) checks = checks + 1 end -local runtime_files = { "/usr/sbin/kai_bin", "/usr/sbin/kai_session", "/etc/init.d/kai", "/etc/config/kai", "/usr/share/kai/kai-plugin.json" } +local runtime_files = { "/usr/sbin/kai_bin", "/usr/sbin/kai_session", "/etc/init.d/kai", "/etc/config/kai" } local function fixture(o) o = o or {} local f = { calls = {}, routes = {} } @@ -22,6 +22,12 @@ local function fixture(o) exec = function(command) f.task_query = command; return "task status fixture" end } package.loaded["nixio.fs"], package.loaded["luci.sys"] = fs, sys + package.loaded["luci.model.uci"] = { cursor = function() return { + get_first = function(_, config, section, option) + check(config == "kai" and section == "kai" and option == "port", "read classic port") + return o.port + end + } end } package.loaded["luci.jsonc"] = { parse = function() return o.task_status or {} end } package.loaded["luci.util"] = { shellquote = function(v) return "'" .. v:gsub("'", "'\\''") .. "'" end } package.loaded["luci.http"] = { @@ -87,7 +93,18 @@ f.controller.classic_status() check(f.reply.installed == false and f.reply.running == false, "missing KAI status") f = fixture({ installed = true, running = true }) f.controller.classic_status() -check(f.reply.installed and f.reply.running, "runtime must work without luci-app-kai") +check(f.reply.installed and f.reply.running, "runtime must work without luci-app-kai or a desktop manifest") +check(f.reply.port == 8197, "default classic port") +for _, port in ipairs({ "8201", "80", "65535" }) do + local configured = fixture({ installed = true, port = port }) + configured.controller.classic_status() + check(configured.reply.port == tonumber(port), "custom classic port") +end +for _, port in ipairs({ "0", "65536", "8197.5", "invalid" }) do + local configured = fixture({ installed = true, port = port }) + configured.controller.classic_status() + check(configured.reply.port == nil, "invalid ports must not produce a launch target") +end check(f.calls[1] == "pidof kai_bin >/dev/null", "status must use classic process") for _, path in ipairs(runtime_files) do f = fixture({ installed = true, missing = path }) diff --git a/luci-app-open-box/Makefile b/luci-app-open-box/Makefile index 39db5b991..b436b247d 100644 --- a/luci-app-open-box/Makefile +++ b/luci-app-open-box/Makefile @@ -1,8 +1,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-open-box -PKG_VERSION:=0.1.429 -PKG_RELEASE:=75 +PKG_VERSION:=0.1.432 +PKG_RELEASE:=76 PKG_MAINTAINER:=liandu2024 PKG_LICENSE:=GPL-3.0-or-later diff --git a/luci-app-quickstart/Makefile b/luci-app-quickstart/Makefile index bfd2f5e00..c06aa51b7 100644 --- a/luci-app-quickstart/Makefile +++ b/luci-app-quickstart/Makefile @@ -9,10 +9,14 @@ LUCI_TITLE:=LuCI support for quickstart LUCI_DEPENDS:=+quickstart +luci-app-ttyd +luci-i18n-ttyd-zh-cn LUCI_PKGARCH:=all -PKG_VERSION:=0.17.1-rc20261028-r1 +PKG_VERSION:=0.17.1-rc20261028-r2 # PKG_RELEASE MUST be empty for luci.mk PKG_RELEASE:= +ifeq ($(CONFIG_USE_APK),y) +PKG_VERSION:=$(subst -rc,_rc,$(PKG_VERSION)) +endif + LUCI_MINIFY_CSS:=0 LUCI_MINIFY_JS:=0 diff --git a/luci-app-ssr-plus/Makefile b/luci-app-ssr-plus/Makefile index b337d9254..b41aeae9c 100644 --- a/luci-app-ssr-plus/Makefile +++ b/luci-app-ssr-plus/Makefile @@ -4,7 +4,7 @@ LUCI_TITLE:=luci-app-ssr-plus LUCI_PKGARCH:=all PKG_NAME:=luci-app-ssr-plus PKG_VERSION:=198 -PKG_RELEASE:=102 +PKG_RELEASE:=103 PKG_CONFIG_DEPENDS:= \ CONFIG_PACKAGE_$(PKG_NAME)_Iptables_Transparent_Proxy \ @@ -33,7 +33,7 @@ LUCI_PKGARCH:=all LUCI_DEPENDS:= \ +libuci-lua +lua +luci-compat +dns2tcp +dnsmasq-full \ +ip-full +lua-neturl +libuci-lua +microsocks +ipt2socks +lyaml \ - +resolveip +curl +nping \ + +resolveip +curl +nping +coreutils-timeout \ +PACKAGE_$(PKG_NAME)_INCLUDE_Xray:xray-core \ +PACKAGE_$(PKG_NAME)_INCLUDE_Xray:coreutils-timeout \ +PACKAGE_$(PKG_NAME)_INCLUDE_Http_Proxy:3proxy \ @@ -109,7 +109,7 @@ endchoice choice prompt "V2ray-core Selection" - default PACKAGE_$(PKG_NAME)_INCLUDE_NONE_V2RAY + default PACKAGE_$(PKG_NAME)_INCLUDE_Xray config PACKAGE_$(PKG_NAME)_INCLUDE_NONE_V2RAY bool "None" diff --git a/luci-app-trafficctl/Makefile b/luci-app-trafficctl/Makefile index 7966594d9..0bbaf1e99 100644 --- a/luci-app-trafficctl/Makefile +++ b/luci-app-trafficctl/Makefile @@ -1,8 +1,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-trafficctl -PKG_VERSION:=1.30.3 -PKG_RELEASE:=30 +PKG_VERSION:=1.30.4 +PKG_RELEASE:=31 PKG_LICENSE:=Apache-2.0 PKG_MAINTAINER:=Denis Iusupov diff --git a/luci-app-wwand/htdocs/luci-static/resources/wwand/modemopts.js b/luci-app-wwand/htdocs/luci-static/resources/wwand/modemopts.js index 646155c56..422ffdd5e 100644 --- a/luci-app-wwand/htdocs/luci-static/resources/wwand/modemopts.js +++ b/luci-app-wwand/htdocs/luci-static/resources/wwand/modemopts.js @@ -554,6 +554,20 @@ return baseclass.extend({ o.value('cdma', 'CDMA'); bind(o); + /* NSA vs SA is a switch of its own, not part of "Radio technology": + the modem's vendor AT setting, which wwand knows only for the + Foxconn T99W175 and the Quectel RG50xQ/RM5xxQ (wwand atcmd.uc + nr5g_mode_commands); elsewhere the daemon reports it as not + applied */ + o = s.taboption(tab, form.ListValue, 'nr5g_mode', _('5G NR mode'), + _('Which kind of 5G the modem may use: non-standalone (5G on an LTE anchor), standalone (5G core), or both. Separate from "Radio technology", which only allows 5G at all. Set over QMI on Qualcomm modems (QMI, or MBIM with the QMI passthrough), otherwise over AT on the Foxconn T99W175 and Quectel RG50xQ/RM5xxQ. With "5G NR" alone, "NSA only" finds no network, because NSA needs LTE.')); + o.value('', _('Leave modem setting unchanged')); + o.value('both', _('NSA and SA')); + o.value('nsa', _('NSA only')); + o.value('sa', _('SA only')); + o.optional = true; + bind(o); + /* the names are config_check / nas.uc RAT_ORDER_IDS; the daemon drops an unknown or repeated one with a warning, so it is refused here before it gets that far */ diff --git a/luci-proto-wwand/Makefile b/luci-proto-wwand/Makefile index ecc4fed0c..be427dcf4 100644 --- a/luci-proto-wwand/Makefile +++ b/luci-proto-wwand/Makefile @@ -20,12 +20,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-proto-wwand -PKG_VERSION:=1.7.0_pre1 -PKG_RELEASE:=18 +PKG_VERSION:=1.8.0_pre2 +PKG_RELEASE:=19 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://github.com/ddimension/luci-proto-wwand.git -PKG_SOURCE_VERSION:=fb547fef6576e16ca0e83bd8379f82c27a040613 +PKG_SOURCE_VERSION:=c451947e1355487391964dea79d73da2e58738a4 PKG_MIRROR_HASH:=skip PKG_LICENSE:=GPL-2.0-only diff --git a/quickstart-netpolicy/Makefile b/quickstart-netpolicy/Makefile new file mode 100644 index 000000000..d6d8bf17c --- /dev/null +++ b/quickstart-netpolicy/Makefile @@ -0,0 +1,74 @@ +# +# Copyright (C) 2026 Quickstart NetPolicy Contributors +# +# This is free software, licensed under the GNU General Public License v3. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=quickstart-netpolicy +PKG_VERSION:=fastnet-runtime-v0.8.9 +PKG_RELEASE:=1 +NETPOLICY_TARGET_x86_64:=x86_64-unknown-linux-musl +NETPOLICY_TARGET_aarch64:=aarch64-unknown-linux-musl +NETPOLICY_TARGET:=$(NETPOLICY_TARGET_$(ARCH)) +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(NETPOLICY_TARGET).tar.gz +PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/quickstart-runtime-v0.17.1-rc20261028/ +PKG_HASH_x86_64:=39861c6d508a900d691315b6443f274ea1e283bc3f8c9f9fa91b4e992814a372 +PKG_HASH_aarch64:=ef2dd2c67ce02b945e89c9fbff66650586fb1c1410717b9306e583c4ad53e480 +PKG_HASH:=skip +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION) + +ifeq ($(CONFIG_USE_APK),y) +PKG_VERSION:=fastnet-runtime-v0.8.9 +endif + +PKG_BUILD_PARALLEL:=1 +PKG_USE_MIPS16:=0 + +include $(INCLUDE_DIR)/package.mk + +# The packaged ELF must match binarySha256 in the runtime manifest. +RSTRIP:=: + +define Package/$(PKG_NAME) + SECTION:=net + CATEGORY:=Network + SUBMENU:=Web Servers/Proxies + TITLE:=Quickstart native network policy engine + URL:=https://github.com/istoreos/quickstart-netpolicy + DEPENDS:=@(x86_64||aarch64) +ca-bundle +endef + +define Package/$(PKG_NAME)/description + Independent eBPF network-policy engine for Quickstart device rate limits. + It uses its own binary, port, data files and kernel maps and does not replace Bandix. +endef + +define Package/$(PKG_NAME)/conffiles +/etc/config/quickstart-netpolicy +endef + +define Build/Prepare + rm -rf $(PKG_BUILD_DIR) + $(INSTALL_DIR) $(PKG_BUILD_DIR) + gzip -dc $(DL_DIR)/$(PKG_SOURCE) | tar -C $(PKG_BUILD_DIR) -xf - +endef + +define Build/Configure +endef + +define Build/Compile +endef + +define Package/$(PKG_NAME)/install + $(INSTALL_DIR) $(1)/usr/bin $(1)/etc/init.d $(1)/etc/config $(1)/usr/share/quickstart-netpolicy + $(INSTALL_BIN) $(PKG_BUILD_DIR)/payload/quickstart-netpolicy $(1)/usr/bin/quickstart-netpolicy + $(INSTALL_BIN) ./files/quickstart-netpolicy.init $(1)/etc/init.d/quickstart-netpolicy + $(INSTALL_CONF) ./files/quickstart-netpolicy.config $(1)/etc/config/quickstart-netpolicy + $(INSTALL_DATA) $(PKG_BUILD_DIR)/manifest.json $(1)/usr/share/quickstart-netpolicy/manifest.json + $(INSTALL_DATA) $(PKG_BUILD_DIR)/payload/LICENSE $(1)/usr/share/quickstart-netpolicy/LICENSE + $(INSTALL_DATA) $(PKG_BUILD_DIR)/payload/NOTICE $(1)/usr/share/quickstart-netpolicy/NOTICE +endef + +$(eval $(call BuildPackage,$(PKG_NAME))) diff --git a/quickstart-netpolicy/files/quickstart-netpolicy.config b/quickstart-netpolicy/files/quickstart-netpolicy.config new file mode 100644 index 000000000..cd0a703e9 --- /dev/null +++ b/quickstart-netpolicy/files/quickstart-netpolicy.config @@ -0,0 +1,18 @@ +# Legacy main keeps its original endpoint and data directory. +config quickstart-netpolicy 'main' + option network 'lan' + option enabled '0' + option interface 'br-lan' + option port '8765' + option data_dir '/usr/share/quickstart-netpolicy' + option log_level 'info' + option traffic_history '0' + option dns_monitoring '0' + +# Additional networks are opt-in and require explicit stable resources: +# config quickstart-netpolicy 'office' +# option enabled '0' +# option network 'office' +# option interface 'br-office' +# option port '8766' +# option data_dir '/usr/share/quickstart-netpolicy/networks/office' diff --git a/quickstart-netpolicy/files/quickstart-netpolicy.init b/quickstart-netpolicy/files/quickstart-netpolicy.init new file mode 100755 index 000000000..8e01cdf21 --- /dev/null +++ b/quickstart-netpolicy/files/quickstart-netpolicy.init @@ -0,0 +1,119 @@ +#!/bin/sh /etc/rc.common +# quickstart-scope-context: 1 + +START=91 +STOP=15 +USE_PROCD=1 +PROG=/usr/bin/quickstart-netpolicy + +service_triggers() { + procd_add_reload_trigger quickstart-netpolicy + config_load quickstart-netpolicy + config_foreach add_network_trigger quickstart-netpolicy +} + +add_network_trigger() { + local network enabled + config_get_bool enabled "$1" enabled 0 + [ "$enabled" -eq 1 ] || return 0 + config_get network "$1" network + [ "$1" != main ] || network="${network:-lan}" + [ -z "$network" ] || procd_add_reload_interface_trigger "$network" +} + +instance_error() { + logger -t quickstart-netpolicy "instance $1: $2" +} + +# Validate the whole enabled set before opening any instance. Duplicate resources +# invalidate both entries; configuration order must not choose the winning LAN. +collect_instance() { + local section="$1" enabled network interface port data_dir log_level history dns other + config_get_bool enabled "$section" enabled 0 + [ "$enabled" -eq 1 ] || return 0 + config_get network "$section" network + config_get interface "$section" interface + config_get port "$section" port + config_get data_dir "$section" data_dir + config_get log_level "$section" log_level info + config_get_bool history "$section" traffic_history 0 + config_get_bool dns "$section" dns_monitoring 0 + if [ "$section" = main ]; then + network="${network:-lan}" + interface="${interface:-br-lan}" + port="${port:-8765}" + data_dir="${data_dir:-/usr/share/quickstart-netpolicy}" + fi + case "$section" in ''|*[!A-Za-z0-9_-]*) instance_error "$section" 'invalid instance identity'; return 0;; esac + case "$network" in ''|*[!A-Za-z0-9_-]*) instance_error "$section" 'invalid network identity'; return 0;; esac + case "$interface" in ''|*[!A-Za-z0-9_.-]*) instance_error "$section" 'invalid interface'; return 0;; esac + case "$port" in ''|*[!0-9]*) instance_error "$section" 'port is required'; return 0;; esac + port="$(expr "$port" + 0)" + [ "$port" -gt 0 ] && [ "$port" -le 65535 ] || { instance_error "$section" 'invalid port'; return 0; } + case "$data_dir" in /*) ;; *) instance_error "$section" 'absolute data_dir is required'; return 0;; esac + case "$data_dir" in *'|'*|*' +'*) instance_error "$section" 'invalid data_dir'; return 0;; esac + case "$log_level" in trace|debug|info|warn|error) ;; *) instance_error "$section" 'invalid log level'; return 0;; esac + local canonical + canonical="$(readlink -f "$data_dir" 2>/dev/null)" + [ -z "$canonical" ] || data_dir="$canonical" + local IFS=' +' + for other in $instance_records; do + local old_section old_network old_interface old_port old_dir rest + old_section="${other%%|*}"; rest="${other#*|}" + old_network="${rest%%|*}"; rest="${rest#*|}" + old_interface="${rest%%|*}"; rest="${rest#*|}" + old_port="${rest%%|*}"; rest="${rest#*|}" + old_dir="${rest%%|*}" + if [ "$network" = "$old_network" ] || [ "$interface" = "$old_interface" ] || [ "$port" = "$old_port" ] || [ "$data_dir" = "$old_dir" ]; then + invalid_instances="$invalid_instances $section $old_section" + instance_error "$section" "duplicate network/interface/port/data_dir with $old_section" + fi + done + instance_records="${instance_records}${instance_records:+ +}$section|$network|$interface|$port|$data_dir|$log_level|$history|$dns" +} + +start_instance() { + local section="$1" network interface port data_dir log_level history dns status actual + case " $invalid_instances " in *" $section "*) return 0;; esac + config_get network "$section" network lan + config_get interface "$section" interface br-lan + config_get port "$section" port 8765 + config_get data_dir "$section" data_dir /usr/share/quickstart-netpolicy + config_get log_level "$section" log_level info + config_get_bool history "$section" traffic_history 0 + config_get_bool dns "$section" dns_monitoring 0 + status="$(ubus -S call "network.interface.$network" status 2>/dev/null)" || { instance_error "$section" 'network unavailable'; return 0; } + actual="$(printf '%s' "$status" | jsonfilter -e '@.l3_device')" + [ "$actual" = "$interface" ] || { instance_error "$section" 'interface does not match logical network'; return 0; } + [ -d "/sys/class/net/$interface" ] || { instance_error "$section" 'interface unavailable'; return 0; } + procd_open_instance "$section" + procd_set_param command "$binary" --iface "$interface" --port "$port" --data-dir "$data_dir" --network-id "$network" --instance-id "$section" --log-level "$log_level" --enable-traffic + [ "$history" -eq 1 ] && procd_append_param command --traffic-enable-storage + [ "$dns" -eq 1 ] && procd_append_param command --enable-dns + procd_set_param netdev "$interface" + procd_set_param respawn 3600 5 3 + procd_set_param term_timeout 10 + procd_set_param stdout 1 + procd_set_param stderr 1 + procd_set_param limits core="0 0" + procd_close_instance +} + +start_service() { + local instance_records='' invalid_instances='' record binary="$PROG" + [ -x "$binary" ] || { instance_error main "missing executable $binary"; return 1; } + config_load quickstart-netpolicy + config_foreach collect_instance quickstart-netpolicy + local IFS=' +' + for record in $instance_records; do start_instance "${record%%|*}"; done +} + +# procd compares named instance commands, so unchanged networks keep their +# processes and TC links. Do not implement reload as stop/start. +reload_service() { + rc_procd start_service +} diff --git a/quickstart/Makefile b/quickstart/Makefile index d47b5d5a6..ee192d6d3 100644 --- a/quickstart/Makefile +++ b/quickstart/Makefile @@ -10,19 +10,26 @@ include $(TOPDIR)/rules.mk PKG_ARCH_quickstart:=$(ARCH) PKG_NAME:=quickstart -PKG_VERSION:=0.13.1 -PKG_RELEASE:=6 +PKG_VERSION:=0.17.1-rc20261028 +PKG_RELEASE:=7 PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz -PKG_SOURCE_URL:=https://github.com/linkease/istore-packages/releases/download/prebuilt/ +PKG_SOURCE_URL:=https://github.com/istoreos/quickstart/releases/download/v$(PKG_VERSION)/ PKG_HASH:=skip PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-binary-$(PKG_VERSION) +ifeq ($(CONFIG_USE_APK),y) +PKG_VERSION:=$(subst -rc,_rc,$(PKG_VERSION)) +endif + PKG_BUILD_PARALLEL:=1 PKG_USE_MIPS16:=0 include $(INCLUDE_DIR)/package.mk +# Runtime manifests cover these prebuilt bytes; do not strip them again. +RSTRIP:=: + define Package/$(PKG_NAME) SECTION:=net CATEGORY:=Network @@ -39,6 +46,23 @@ endef define Package/$(PKG_NAME)/conffiles /etc/config/quickstart +/etc/quickstart/home-layout.json +/etc/quickstart/home-layout.json.bak +/etc/quickstart/home-layout.json.damaged +/etc/quickstart/home-layout.json.bak.damaged +/etc/quickstart/home-launcher.json +/etc/quickstart/home-launcher.json.bak +/etc/quickstart/home-launcher.json.damaged +/etc/quickstart/home-launcher.json.bak.damaged +/etc/quickstart/device-classifications.json +/etc/quickstart/device-profiles.json +/etc/quickstart/manual-devices.json +/etc/quickstart/device-groups.json +/etc/quickstart/traffic-insights.json +/etc/quickstart/device-policy-effects-v1.json +/etc/quickstart/task-transactions-v1.json +/etc/quickstart/lan-device-model-v1.json +/etc/quickstart/network-audit.json endef define Package/$(PKG_NAME)/postinst @@ -71,7 +95,10 @@ define Package/$(PKG_NAME)/install $(INSTALL_DIR) $(1)/etc/hotplug.d/iface $(INSTALL_BIN) ./files/startdhns.hotplug $(1)/etc/hotplug.d/iface/21-startdhns $(INSTALL_DIR) $(1)/usr/share/quickstart + $(INSTALL_DATA) $(PKG_BUILD_DIR)/MANIFEST $(1)/usr/share/quickstart/MANIFEST $(INSTALL_DATA) ./files/manuf $(1)/usr/share/quickstart/manuf + $(INSTALL_DIR) $(1)/lib/upgrade/keep.d + $(INSTALL_DATA) ./files/quickstart.keep $(1)/lib/upgrade/keep.d/quickstart endef $(eval $(call BuildPackage,$(PKG_NAME))) diff --git a/quickstart/files/quickstart.keep b/quickstart/files/quickstart.keep new file mode 100644 index 000000000..a9d52ecea --- /dev/null +++ b/quickstart/files/quickstart.keep @@ -0,0 +1,8 @@ +/etc/quickstart/home-layout.json +/etc/quickstart/home-layout.json.bak +/etc/quickstart/home-layout.json.damaged +/etc/quickstart/home-layout.json.bak.damaged +/etc/quickstart/home-launcher.json +/etc/quickstart/home-launcher.json.bak +/etc/quickstart/home-launcher.json.damaged +/etc/quickstart/home-launcher.json.bak.damaged diff --git a/v2raya-core/Makefile b/v2raya-core/Makefile index 3710b2b2a..cacbb7ff4 100644 --- a/v2raya-core/Makefile +++ b/v2raya-core/Makefile @@ -10,8 +10,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=v2raya-core -PKG_VERSION:=2.5.14 -PKG_RELEASE:=1 +PKG_VERSION:=2.5.15 +PKG_RELEASE:=2 PKG_SOURCE:=v2rayA-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/v2rayA/v2rayA/tar.gz/v$(PKG_VERSION)? diff --git a/v2raya/Makefile b/v2raya/Makefile index 409d3415e..512fc8ade 100644 --- a/v2raya/Makefile +++ b/v2raya/Makefile @@ -13,8 +13,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=v2raya -PKG_VERSION:=2.5.14 -PKG_RELEASE:=8 +PKG_VERSION:=2.5.15 +PKG_RELEASE:=9 PKG_SOURCE:=v2rayA-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/v2rayA/v2rayA/tar.gz/v$(PKG_VERSION)? diff --git a/wwand/Makefile b/wwand/Makefile index e63e01886..db13fccbd 100644 --- a/wwand/Makefile +++ b/wwand/Makefile @@ -50,12 +50,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=wwand -PKG_VERSION:=1.9.0_pre14 -PKG_RELEASE:=92 +PKG_VERSION:=1.9.0_pre20 +PKG_RELEASE:=93 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://github.com/ddimension/wwand.git -PKG_SOURCE_VERSION:=4f3c54b1eeb65d898246545435ca2300e57ac30e +PKG_SOURCE_VERSION:=b646c9ec93928e8a8e57356551cfa7717d7171b6 PKG_MIRROR_HASH:=skip PKG_LICENSE:=GPL-2.0-only