From 8bfcfdf9ec3f66dd7990c073a33f61d497f751a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=AF=E8=AF=BA=E4=BC=8A=E6=9B=BC?= Date: Tue, 25 Aug 2026 22:28:35 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20daemon=20=E5=9B=A0=20?= =?UTF-8?q?=5Fstop=20=E5=91=BD=E5=90=8D=E5=86=B2=E7=AA=81=E7=A7=92?= =?UTF-8?q?=E9=80=80=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 全局停机标志被同名函数盖掉,main 立刻 return 0,Supervisor 循环拉起。 --- sandbox-image/apemind_computerd.py | 20 ++++++++++---------- sandbox-image/test_apemind_computerd.py | 7 ++++++- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/sandbox-image/apemind_computerd.py b/sandbox-image/apemind_computerd.py index 7047138..0f4aa4b 100644 --- a/sandbox-image/apemind_computerd.py +++ b/sandbox-image/apemind_computerd.py @@ -27,7 +27,7 @@ _children: dict[str, subprocess.Popen] = {} _ports: dict[str, int] = {} _crash_until: dict[str, float] = {} -_stop = False +_shutdown = False def _log(msg: str) -> None: @@ -125,7 +125,7 @@ def _start(agent: dict) -> None: _log(f"started {agent_id} on 127.0.0.1:{port}") -def _stop(agent_id: str) -> None: +def _stop_agent(agent_id: str) -> None: proc = _children.pop(agent_id, None) _ports.pop(agent_id, None) if proc is None: @@ -184,8 +184,8 @@ def _observe(known_ids: set[str] | None = None) -> list[dict]: def _handle_stop(_signum, _frame) -> None: - global _stop - _stop = True + global _shutdown + _shutdown = True def main() -> int: @@ -195,13 +195,13 @@ def main() -> int: token = os.environ.get("APEMIND_JOIN_TOKEN", "").strip() if not base or not token: _log("APEMIND_URL and APEMIND_JOIN_TOKEN are required; idle") - while not _stop: + while not _shutdown: time.sleep(POLL_SECONDS) return 0 session = "" delay = POLL_SECONDS applied_rev: dict[str, int] = {} - while not _stop: + while not _shutdown: try: if not session: state = _join(base, token) @@ -218,11 +218,11 @@ def main() -> int: _start(agent) applied_rev[agent["id"]] = rev else: - _stop(agent["id"]) + _stop_agent(agent["id"]) applied_rev[agent["id"]] = rev for agent_id in list(_children): if agent_id not in want_ids: - _stop(agent_id) + _stop_agent(agent_id) _api( base, "/api/v2/computer-control/observed", @@ -240,10 +240,10 @@ def main() -> int: _log(f"loop error: {exc}") delay = _next_backoff(delay) deadline = time.time() + delay - while not _stop and time.time() < deadline: + while not _shutdown and time.time() < deadline: time.sleep(min(1.0, max(0.0, deadline - time.time()))) for agent_id in list(_children): - _stop(agent_id) + _stop_agent(agent_id) return 0 diff --git a/sandbox-image/test_apemind_computerd.py b/sandbox-image/test_apemind_computerd.py index 830f4a1..7bca8ea 100644 --- a/sandbox-image/test_apemind_computerd.py +++ b/sandbox-image/test_apemind_computerd.py @@ -28,6 +28,11 @@ def _popen(cmd, cwd, env, stdout, stderr): assert daemon._children["agt-a"] is fake +def test_shutdown_flag_is_not_the_stop_function(): + assert daemon._shutdown is False + assert callable(daemon._stop_agent) + + def test_next_backoff_doubles_then_caps(): assert daemon._next_backoff(5) == 10 assert daemon._next_backoff(40) == 60 @@ -47,7 +52,7 @@ def test_observe_reports_stopped_after_stop(): proc = SimpleNamespace(poll=lambda: None, terminate=lambda: None, wait=lambda timeout: None, kill=lambda: None) daemon._children["agt-b"] = proc daemon._ports["agt-b"] = 3081 - daemon._stop("agt-b") + daemon._stop_agent("agt-b") rows = daemon._observe({"agt-b"}) assert rows == [ { From da8faf09d931eaf8eea6a07f6e3c0d985d2913b1 Mon Sep 17 00:00:00 2001 From: earayu Date: Tue, 25 Aug 2026 07:30:12 -0700 Subject: [PATCH 2/2] =?UTF-8?q?=E5=90=AF=E5=8A=A8=20dsh=20=E6=97=B6?= =?UTF-8?q?=E5=86=99=E5=85=A5=20ApeMind=20=E7=94=A8=E6=88=B7=E8=BA=AB?= =?UTF-8?q?=E4=BB=BD=20(#7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 启动 dsh 时写入 ApeMind 用户身份。 从 desired 的 owner_user_id 写入 APEMIND_USER_ID 和 $HOME/.apemind/identity。 Refs apecloud/aperag-enterprise#4829 * 修复 daemon 因 _stop 命名冲突秒退。 全局停机标志被同名函数盖掉,main 立刻 return 0,Supervisor 循环拉起。 --------- Co-authored-by: 冯诺伊曼 --- sandbox-image/apemind_computerd.py | 9 +++++++++ sandbox-image/test_apemind_computerd.py | 6 +++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/sandbox-image/apemind_computerd.py b/sandbox-image/apemind_computerd.py index 0f4aa4b..86a74d2 100644 --- a/sandbox-image/apemind_computerd.py +++ b/sandbox-image/apemind_computerd.py @@ -113,6 +113,15 @@ def _start(agent: dict) -> None: env["HOME"] = str(work) env["XDG_CONFIG_HOME"] = str(work / ".config") env["XDG_DATA_HOME"] = str(work / ".local" / "share") + owner = str(agent.get("owner_user_id") or "") + if owner: + env["APEMIND_USER_ID"] = owner + ident = work / ".apemind" / "identity" + ident.parent.mkdir(parents=True, exist_ok=True) + tmp = ident.with_name(ident.name + ".tmp") + tmp.write_text(json.dumps({"user_id": owner})) + tmp.chmod(0o600) + tmp.replace(ident) proc = subprocess.Popen( ["dsh", "web", "--no-open", "--port", str(port)], cwd=str(work), diff --git a/sandbox-image/test_apemind_computerd.py b/sandbox-image/test_apemind_computerd.py index 7bca8ea..27835d3 100644 --- a/sandbox-image/test_apemind_computerd.py +++ b/sandbox-image/test_apemind_computerd.py @@ -11,15 +11,19 @@ def test_start_sets_private_dsh_home(tmp_path): daemon._children.clear() daemon._ports.clear() - agent = {"id": "agt-a", "work_dir": str(tmp_path / "a")} + agent = {"id": "agt-a", "work_dir": str(tmp_path / "a"), "owner_user_id": "user-1"} fake = SimpleNamespace(poll=lambda: None) def _popen(cmd, cwd, env, stdout, stderr): assert env["DSH_HOME"] == str(Path(cwd) / ".dsh") assert env["HOME"] == cwd assert env["XDG_CONFIG_HOME"] == str(Path(cwd) / ".config") + assert env["APEMIND_USER_ID"] == "user-1" assert env["DSH_HOME"] != os.path.expanduser("~/.dsh") assert Path(env["DSH_HOME"]).is_dir() + ident = Path(cwd) / ".apemind" / "identity" + assert ident.read_text() == '{"user_id": "user-1"}' + assert ident.stat().st_mode & 0o777 == 0o600 return fake with patch("apemind_computerd.subprocess.Popen", side_effect=_popen):