Sync 2026-09-03 23:42:31

This commit is contained in:
github-actions[bot]
2026-09-03 23:42:31 +08:00
parent 58f9300b4a
commit 92c3e430e5
17 changed files with 395 additions and 22 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ AGENTFLOW_HASH_aarch64:=6d733ebc49b095bf8670a1b655a13bb8ad98c90ced19701b67d5cdd4
PKG_NAME:=agentflow
PKG_VERSION:=0.3.0
PKG_RELEASE:=5
PKG_RELEASE:=6
AGENTFLOW_URL_FILE:=agentflow-linux-$(AGENTFLOW_ARCH)
AGENTFLOW_DOWNLOAD:=$(AGENTFLOW_URL_FILE)-$(AGENTFLOW_HASH_$(ARCH))
@@ -0,0 +1,84 @@
# AgentFlow 共享 Runtime HOME 实施里程碑
## 目标架构
AgentFlow 的应用数据和设备级开发运行时分离:
- AgentFlow 数据目录:`<quickstart.conf_dir>/AgentFlow`
- 共享 Runtime HOME`<quickstart.conf_dir>/Runtime/home`
共享 Runtime HOME 由 `mise` 包提供公共 helper 初始化,AgentFlow 和后续运行时感知应用复用同一套 `HOME`、XDG、`MISE_*``PATH`
## Milestone 1:公共 Runtime HOME 合约
目标:
- `mise` 包安装 `/etc/config/mise`
- `mise` 包安装 `/lib/functions/istore_runtime.sh`
- helper 自动从 `quickstart.main.conf_dir` 推导 `<conf_dir>/Runtime/home`
- helper 支持 `ISTORE_RUNTIME_CONF_DIR` 兜底,便于应用从自身 Configs 目录派生 Runtime。
- helper 统一导出 `HOME``XDG_DATA_HOME``XDG_CACHE_HOME``XDG_CONFIG_HOME``XDG_STATE_HOME``MISE_DATA_DIR``MISE_CACHE_DIR``MISE_CONFIG_DIR``MISE_STATE_DIR``PATH`
验收:
- 新安装 `mise` 后,存在 `/etc/config/mise``/lib/functions/istore_runtime.sh`
- 已配置 quickstart 时,初始化路径为 `<quickstart.conf_dir>/Runtime/home`
- 未配置 quickstart 但应用提供 `ISTORE_RUNTIME_CONF_DIR` 时,初始化路径为 `$ISTORE_RUNTIME_CONF_DIR/Runtime/home`
## Milestone 2AgentFlow 接入共享 Runtime HOME
目标:
- AgentFlow 不再使用 `$data_dir/global` 作为 `HOME` 或 mise shim 根。
- AgentFlow 启动时调用 `istore_runtime_export_env`
- `AGENT_FLOW_DATA` 继续固定在 AgentFlow 数据目录下的 `data` 子目录。
- AgentFlow 服务进程及其子进程继承共享 Runtime HOME。
验收:
- `/etc/init.d/agentflow start` 后,AgentFlow 进程环境中 `HOME=<conf_dir>/Runtime/home`
- `MISE_DATA_DIR=$HOME/.local/share/mise`
- `PATH``$MISE_DATA_DIR/shims:$HOME/.local/bin` 开头。
- AgentFlow 数据库仍写入 `<conf_dir>/AgentFlow/data/db/db.sqlite`
## Milestone 3LuCI 可见性
目标:
- AgentFlow LuCI 页面继续配置 AgentFlow 数据目录。
- 页面展示推导出的共享 Runtime HOME。
- 如果 `mise.runtime_dir` 被显式设置,页面展示其对应的 `home`
验收:
- quickstart `conf_dir=/mnt/vio3-1/Configs` 时,页面显示 `/mnt/vio3-1/Configs/Runtime/home`
- AgentFlow 数据目录仍显示 `/mnt/vio3-1/Configs/AgentFlow`
## Milestone 4:迁移与兼容
目标:
- 新安装默认使用共享 Runtime HOME。
- 老安装中已有 `$data_dir/global/.local/share/mise` 时,不自动移动大目录。
- 后续提供显式迁移命令或 LuCI 操作。
验收:
- 升级不会删除或搬移旧 runtime 数据。
- 用户确认迁移后,旧 mise data 可迁移到 `<conf_dir>/Runtime/home/.local/share/mise`
- 迁移失败不会影响 AgentFlow 私有数据。
## Milestone 5AgentFlow 后端项目环境集成
目标:
- OpenWrt 层面完成共享 HOME 后,再在 AgentFlow 后端引入项目环境 wrapper。
- coding agent 进程使用 `mise exec -- <agent> ...` 应用项目 `mise.toml`
- 运行期设置 `MISE_EXEC_AUTO_INSTALL=0`,缺失工具只在准备阶段安装。
验收:
- 单 repo 项目中,agent 看到项目声明的 Node/Python/Go 版本。
- 没有隐式运行期下载。
- Codex/Claude/Kimi 等 agent 凭据继续来自共享 Runtime HOME。
- 取消任务时 wrapper 和 agent 子进程都能退出。
+31 -8
View File
@@ -10,11 +10,20 @@ get_config() {
config_get port "$1" port "9000"
}
set_mise_env() {
procd_set_param env \
"$@" \
HOME="$data_dir/global" \
PATH="$data_dir/global/.local/share/mise/shims:$PATH"
load_runtime_env() {
local conf_dir
[ -n "$data_dir" ] || return 1
conf_dir="$(dirname "$data_dir")"
export ISTORE_RUNTIME_CONF_DIR="$conf_dir"
if [ -r /lib/functions/istore_runtime.sh ]; then
. /lib/functions/istore_runtime.sh
istore_runtime_export_env
return $?
fi
return 1
}
start_service() {
@@ -26,15 +35,29 @@ start_service() {
logger -t agentflow "missing executable: /usr/sbin/agentflow"
return 1
}
mkdir -p "$data_dir" "$data_dir/global" || return 1
mkdir -p "$data_dir" "$data_dir/data" || return 1
if ! load_runtime_env; then
logger -t agentflow "shared runtime home is not configured"
return 1
fi
logger -t agentflow "starting AgentFlow on $host:$port"
procd_open_instance
procd_set_param command /usr/sbin/agentflow
set_mise_env \
procd_set_param env \
"AGENT_FLOW_DATA=$data_dir/data" \
"AGENT_FLOW_HOST=$host" \
"AGENT_FLOW_PORT=$port"
"AGENT_FLOW_PORT=$port" \
"HOME=$HOME" \
"XDG_DATA_HOME=$XDG_DATA_HOME" \
"XDG_CACHE_HOME=$XDG_CACHE_HOME" \
"XDG_CONFIG_HOME=$XDG_CONFIG_HOME" \
"XDG_STATE_HOME=$XDG_STATE_HOME" \
"MISE_DATA_DIR=$MISE_DATA_DIR" \
"MISE_CACHE_DIR=$MISE_CACHE_DIR" \
"MISE_CONFIG_DIR=$MISE_CONFIG_DIR" \
"MISE_STATE_DIR=$MISE_STATE_DIR" \
"PATH=$PATH"
procd_set_param stdout 1
procd_set_param stderr 1
procd_set_param respawn
@@ -0,0 +1,65 @@
from pathlib import Path
import unittest
ROOT = Path(__file__).resolve().parents[3]
APPLICATIONS = ROOT / "applications"
def read(relative):
return (APPLICATIONS / relative).read_text()
class MiseRuntimeContractTest(unittest.TestCase):
def test_mise_package_installs_shared_runtime_contract(self):
makefile = read("mise/Makefile")
config = read("mise/files/mise.config")
defaults = read("mise/files/mise.uci-default")
helper = read("mise/files/istore_runtime.sh")
self.assertIn("/etc/config/mise", makefile)
self.assertIn("/lib/functions/istore_runtime.sh", makefile)
self.assertIn("$(INSTALL_BIN) ./files/mise.uci-default", makefile)
self.assertIn("[ -f /etc/uci-defaults/mise ]", makefile)
self.assertIn("config mise 'main'", config)
self.assertIn("option runtime_dir ''", config)
self.assertIn("option auto_discover '1'", config)
self.assertIn(". /lib/functions/istore_runtime.sh", defaults)
self.assertIn("istore_runtime_init", defaults)
self.assertIn("config_get conf_dir main conf_dir", helper)
self.assertIn('"$conf_dir" "$ISTORE_RUNTIME_DEFAULT_SUBDIR"', helper)
self.assertIn("ISTORE_RUNTIME_HOME_SUBDIR=\"home\"", helper)
self.assertIn("ISTORE_RUNTIME_CONF_DIR", helper)
self.assertIn('export HOME="$runtime_home"', helper)
self.assertIn('export XDG_DATA_HOME="$HOME/.local/share"', helper)
self.assertIn('export MISE_DATA_DIR="$XDG_DATA_HOME/mise"', helper)
self.assertIn('export PATH="$MISE_DATA_DIR/shims:$HOME/.local/bin:$PATH"', helper)
def test_agentflow_consumes_shared_runtime_home(self):
init = read("agentflow/files/agentflow.init")
cbi = read("luci-app-agentflow/luasrc/model/cbi/agentflow.lua")
model = read("luci-app-agentflow/luasrc/model/agentflow.lua")
translations = read("luci-app-agentflow/po/zh-cn/agentflow.po")
self.assertIn(". /lib/functions/istore_runtime.sh", init)
self.assertIn("istore_runtime_export_env", init)
self.assertIn('AGENT_FLOW_DATA=$data_dir/data', init)
self.assertIn('HOME=$HOME', init)
self.assertIn('MISE_DATA_DIR=$MISE_DATA_DIR', init)
self.assertNotIn("$data_dir/global", init)
self.assertNotIn(".local/share/mise/shims:$PATH", init)
self.assertIn('translate("Shared runtime home")', cbi)
self.assertIn("agentflow_model.runtime_home", cbi)
self.assertIn('return runtime_dir .. "/home"', model)
self.assertIn('return conf_dir .. "/Runtime"', model)
self.assertIn('msgid "Shared runtime home"', translations)
self.assertIn('msgstr "共享运行时 HOME"', translations)
if __name__ == "__main__":
unittest.main()
+2 -2
View File
@@ -1,8 +1,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=beszel-agent
PKG_VERSION:=0.18.8
PKG_RELEASE:=8
PKG_VERSION:=0.19.0
PKG_RELEASE:=9
PKG_SOURCE:=$(PKG_NAME)_linux_$(or $(BESZEL_GOARCH),arm64).tar.gz
PKG_SOURCE_URL:=https://github.com/henrygd/beszel/releases/download/v$(PKG_VERSION)/
+2 -2
View File
@@ -12,7 +12,7 @@ PKG_ARCH_LINKEASE:=$(ARCH)
PKG_NAME:=linkease-common-bin
# use PKG_SOURCE_DATE instead of PKG_VERSION for compitable
PKG_SOURCE_DATE:=1.7.5
PKG_RELEASE:=21
PKG_RELEASE:=22
ARCH_HEXCODE:=
ifeq ($(ARCH),x86_64)
@@ -28,7 +28,7 @@ else ifeq ($(ARCH),mipsel)
ARCH_HEXCODE=1b0c
endif
PKG_SOURCE_VERSION:=68f013896321ab995075e3daa11681977ab05f93
PKG_SOURCE_VERSION:=f5227a7df2160100b478dba7880891aed1663eb4
PKG_SOURCE:=linkease-common-bin-$(PKG_SOURCE_DATE)-linux-$(PKG_ARCH_LINKEASE).tar.gz
PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/linkease-runtime-v$(PKG_SOURCE_DATE)/
PKG_BUILD_DIR:=$(BUILD_DIR)/linkease-common-bin-$(PKG_SOURCE_DATE)-linux-$(PKG_ARCH_LINKEASE)
+2 -2
View File
@@ -12,7 +12,7 @@ PKG_ARCH_LINKEASE:=$(ARCH)
PKG_NAME:=linkease
# use PKG_SOURCE_DATE instead of PKG_VERSION for compitable
PKG_SOURCE_DATE:=1.7.5
PKG_RELEASE:=24
PKG_RELEASE:=25
ARCH_HEXCODE:=
ifeq ($(ARCH),x86_64)
@@ -28,7 +28,7 @@ else ifeq ($(ARCH),mipsel)
ARCH_HEXCODE=1b0c
endif
PKG_SOURCE_VERSION:=68f013896321ab995075e3daa11681977ab05f93
PKG_SOURCE_VERSION:=f5227a7df2160100b478dba7880891aed1663eb4
PKG_SOURCE:=linkease-bin-$(PKG_SOURCE_DATE)-linux-$(PKG_ARCH_LINKEASE).tar.gz
PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/linkease-runtime-v$(PKG_SOURCE_DATE)/
PKG_BUILD_DIR:=$(BUILD_DIR)/linkease-bin-$(PKG_SOURCE_DATE)-linux-$(PKG_ARCH_LINKEASE)
+2 -2
View File
@@ -11,7 +11,7 @@ PKG_ARCH_LINKEASE:=$(ARCH)
PKG_NAME:=linkeasefull
# use PKG_SOURCE_DATE instead of PKG_VERSION for compitable
PKG_SOURCE_DATE:=3.0.17
PKG_RELEASE:=23
PKG_RELEASE:=24
ARCH_HEXCODE:=
ifeq ($(ARCH),x86_64)
@@ -24,7 +24,7 @@ LINKEASE_RUNTIME_ARCH:=arm64
PKG_HASH:=skip
endif
PKG_SOURCE_VERSION:=68f013896321ab995075e3daa11681977ab05f93
PKG_SOURCE_VERSION:=f5227a7df2160100b478dba7880891aed1663eb4
PKG_SOURCE:=linkease-runtime-$(PKG_SOURCE_DATE)-linux-$(LINKEASE_RUNTIME_ARCH).tar.gz
PKG_SOURCE_URL:=https://github.com/istoreos/istoreos-app-hub/releases/download/linkeasefull-runtime-v$(PKG_SOURCE_DATE)/
PKG_BUILD_DIR:=$(BUILD_DIR)/linkease-runtime-$(PKG_SOURCE_DATE)-linux-$(LINKEASE_RUNTIME_ARCH)
+1 -1
View File
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
LUCI_TITLE:=AgentFlow
PKG_VERSION:=1.0.0
PKG_RELEASE:=2
PKG_RELEASE:=3
LUCI_DEPENDS:=+agentflow +luci-compat
LUCI_MINIFY_CSS:=0
LUCI_MINIFY_JS:=0
@@ -45,4 +45,37 @@ agentflow.find_paths = function(blocks, home_dirs)
return paths, default_path
end
local dirname = function(path)
path = (path or ""):match("^%s*(.-)%s*$")
path = path:gsub("/+$", "")
return path:match("^(.*)/[^/]+$") or ""
end
agentflow.runtime_dir = function(data_dir, home_dirs)
local uci = require "luci.model.uci".cursor()
local configured = uci:get_first("mise", "mise", "runtime_dir", "")
if configured ~= nil and configured ~= "" then
return configured
end
local conf_dir = dirname(data_dir)
if conf_dir == "" then
conf_dir = home_dirs["Configs"]
end
if conf_dir == nil or conf_dir == "" then
return ""
end
return conf_dir .. "/Runtime"
end
agentflow.runtime_home = function(data_dir, home_dirs)
local runtime_dir = agentflow.runtime_dir(data_dir, home_dirs)
if runtime_dir == nil or runtime_dir == "" then
return ""
end
return runtime_dir .. "/home"
end
return agentflow
@@ -34,6 +34,17 @@ for _, val in pairs(paths) do
end
data_dir.default = default_path
local runtime_home = s:option(DummyValue, "_runtime_home", translate("Shared runtime home"))
runtime_home.description = translate("AgentFlow and other runtime-aware applications share this HOME. It is derived from the selected Configs directory unless mise has an explicit runtime directory.")
function runtime_home.cfgvalue(self, section)
local selected_data_dir = m.uci:get("agentflow", section, "data_dir") or default_path
local path = agentflow_model.runtime_home(selected_data_dir, home)
if path == "" then
return translate("Not configured")
end
return path
end
local port = s:option(Value, "port", translate("Listen port"))
port.default = "9000"
port.rmempty = false
+9
View File
@@ -37,6 +37,15 @@ msgstr "请选择硬盘作为数据目录"
msgid "Required. AgentFlow stores its configuration, database and workspace data under this directory."
msgstr "必需。AgentFlow 在此目录中保存配置、数据库和工作区数据。"
msgid "Shared runtime home"
msgstr "共享运行时 HOME"
msgid "AgentFlow and other runtime-aware applications share this HOME. It is derived from the selected Configs directory unless mise has an explicit runtime directory."
msgstr "AgentFlow 和其他运行时感知应用共用此 HOME。除非 mise 显式设置了运行时目录,否则它会从所选 Configs 目录推导。"
msgid "Not configured"
msgstr "未配置"
msgid "Status"
msgstr "状态"
+17 -2
View File
@@ -15,7 +15,7 @@ MISE_HASH_aarch64:=06186cfbfe947049b21d58575fb0ea800cc26ed1375f20f4b678cb3a9d679
PKG_NAME:=mise
PKG_VERSION:=2026.9.1
PKG_RELEASE:=5
PKG_RELEASE:=6
PKG_SOURCE:=$(PKG_NAME)-v$(PKG_VERSION)-linux-$(MISE_ARCH)-musl.tar.gz
PKG_SOURCE_URL:=https://github.com/jdx/mise/releases/download/v$(PKG_VERSION)/
PKG_HASH:=skip
@@ -38,6 +38,18 @@ define Package/$(PKG_NAME)/description
mise manages development tool versions, environment variables, and tasks.
endef
define Package/$(PKG_NAME)/conffiles
/etc/config/mise
endef
define Package/$(PKG_NAME)/postinst
#!/bin/sh
if [ -z "$${IPKG_INSTROOT}" ]; then
[ -f /etc/uci-defaults/mise ] && /etc/uci-defaults/mise && rm -f /etc/uci-defaults/mise
exit 0
fi
endef
define Build/Configure
endef
@@ -45,8 +57,11 @@ define Build/Compile
endef
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_DIR) $(1)/usr/bin $(1)/etc/config $(1)/etc/uci-defaults $(1)/lib/functions
$(INSTALL_BIN) $(PKG_BUILD_DIR)/bin/mise $(1)/usr/bin/mise
$(INSTALL_CONF) ./files/mise.config $(1)/etc/config/mise
$(INSTALL_BIN) ./files/mise.uci-default $(1)/etc/uci-defaults/mise
$(INSTALL_BIN) ./files/istore_runtime.sh $(1)/lib/functions/istore_runtime.sh
endef
$(eval $(call BuildPackage,$(PKG_NAME)))
+121
View File
@@ -0,0 +1,121 @@
#!/bin/sh
. /lib/functions.sh
ISTORE_RUNTIME_DEFAULT_SUBDIR="Runtime"
ISTORE_RUNTIME_HOME_SUBDIR="home"
istore_runtime_uci_section() {
if uci -q get mise.main >/dev/null 2>&1; then
printf 'main\n'
return 0
fi
printf ''
return 1
}
istore_runtime_quickstart_conf_dir() {
local main_dir conf_dir
config_load quickstart >/dev/null 2>&1 || return 1
config_get main_dir main main_dir ""
[ -n "$main_dir" ] || return 1
config_get conf_dir main conf_dir "$main_dir/Configs"
[ -n "$conf_dir" ] || return 1
printf '%s\n' "$conf_dir"
}
istore_runtime_linkease_conf_dir() {
local local_home
local_home="$(uci -q get linkease.@linkease[0].local_home 2>/dev/null || true)"
[ -n "$local_home" ] || return 1
printf '%s/Configs\n' "$local_home"
}
istore_runtime_env_conf_dir() {
[ -n "$ISTORE_RUNTIME_CONF_DIR" ] || return 1
printf '%s\n' "$ISTORE_RUNTIME_CONF_DIR"
}
istore_runtime_discover_dir() {
local conf_dir
conf_dir="$(istore_runtime_env_conf_dir 2>/dev/null || true)"
if [ -z "$conf_dir" ]; then
conf_dir="$(istore_runtime_quickstart_conf_dir 2>/dev/null || true)"
fi
if [ -z "$conf_dir" ]; then
conf_dir="$(istore_runtime_linkease_conf_dir 2>/dev/null || true)"
fi
[ -n "$conf_dir" ] || return 1
printf '%s/%s\n' "$conf_dir" "$ISTORE_RUNTIME_DEFAULT_SUBDIR"
}
istore_runtime_dir() {
local section runtime_dir auto_discover
section="$(istore_runtime_uci_section 2>/dev/null || true)"
if [ -n "$section" ]; then
config_load mise >/dev/null 2>&1 || true
config_get runtime_dir "$section" runtime_dir ""
config_get_bool auto_discover "$section" auto_discover 1
if [ -n "$runtime_dir" ]; then
printf '%s\n' "$runtime_dir"
return 0
fi
[ "$auto_discover" = "1" ] || return 1
fi
istore_runtime_discover_dir
}
istore_runtime_home() {
local runtime_dir
runtime_dir="$(istore_runtime_dir)" || return 1
printf '%s/%s\n' "$runtime_dir" "$ISTORE_RUNTIME_HOME_SUBDIR"
}
istore_runtime_init() {
local runtime_dir runtime_home section
runtime_dir="$(istore_runtime_dir)" || return 1
runtime_home="$runtime_dir/$ISTORE_RUNTIME_HOME_SUBDIR"
mkdir -p \
"$runtime_home/.local/share/mise" \
"$runtime_home/.cache/mise" \
"$runtime_home/.config/mise" \
"$runtime_home/.local/state/mise" \
"$runtime_home/.local/bin" || return 1
section="$(istore_runtime_uci_section 2>/dev/null || true)"
if [ -n "$section" ]; then
uci -q set "mise.$section.runtime_dir=$runtime_dir" >/dev/null 2>&1 || true
uci -q commit mise >/dev/null 2>&1 || true
fi
printf '%s\n' "$runtime_home"
}
istore_runtime_export_env() {
local runtime_home
runtime_home="$(istore_runtime_init)" || return 1
export HOME="$runtime_home"
export XDG_DATA_HOME="$HOME/.local/share"
export XDG_CACHE_HOME="$HOME/.cache"
export XDG_CONFIG_HOME="$HOME/.config"
export XDG_STATE_HOME="$HOME/.local/state"
export MISE_DATA_DIR="$XDG_DATA_HOME/mise"
export MISE_CACHE_DIR="$XDG_CACHE_HOME/mise"
export MISE_CONFIG_DIR="$XDG_CONFIG_HOME/mise"
export MISE_STATE_DIR="$XDG_STATE_HOME/mise"
export PATH="$MISE_DATA_DIR/shims:$HOME/.local/bin:$PATH"
}
+4
View File
@@ -0,0 +1,4 @@
config mise 'main'
option enabled '1'
option runtime_dir ''
option auto_discover '1'
+8
View File
@@ -0,0 +1,8 @@
#!/bin/sh
if [ -r /lib/functions/istore_runtime.sh ]; then
. /lib/functions/istore_runtime.sh
istore_runtime_init >/dev/null 2>&1 || true
fi
exit 0
+2 -2
View File
@@ -43,12 +43,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=wwand
PKG_RELEASE:=20
PKG_RELEASE:=21
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/ddimension/wwand.git
PKG_SOURCE_VERSION:=6ed882e3b53544c9e42f2cc681780d99205141eb
PKG_SOURCE_DATE:=2026-09-02
PKG_SOURCE_DATE:=2026-09-03
PKG_MIRROR_HASH:=skip
PKG_LICENSE:=GPL-2.0-only