Skip to content

Commit 01d83ec

Browse files
committed
Break clipboard callback cycles while retaining queued ownership
1 parent 990c073 commit 01d83ec

17 files changed

Lines changed: 157 additions & 26 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ it shipped into a `## [x.y.z] - date` section of their own; the tag's
1313

1414
## Unreleased
1515

16+
- Remove the GUI clipboard callback's strong reference to its own viewer.
17+
Disconnect no longer leaves that viewer and its native Qt panel waiting for
18+
cyclic GC. Registration binds the original peer weakly once; queued delivery
19+
still retains its original peer/authority for ownership checks. Public API,
20+
wire data and explicit clipboard behavior remain compatible.
21+
1622
- Wayland native key/button/pointer input stops on portal refusal/timeout,
1723
incomplete handshake, device pause or revocation instead of implicitly replaying
1824
through CLI. Forced `libei` also refuses optional absence. Auto mode retains

‎Progress.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@
1515
`WIP` — 改版仍缺以下內容;原生平台與正式環境驗收缺口另列於後。
1616
計畫 D 仍缺 `wrapper/capabilities.py` 的無副作用公開探測、獨立輸入/擷取診斷與
1717
SDK/AC/MCP/Diagnostics GUI 介面,以及 portal 關閉/恢復與合成器重啟驗收。
18-
D2 的原生 EI 釋放驗證與 D3 的錄製/全域停止/原生崩潰證據仍缺,詳見計畫與下方生命週期待辦。
18+
D2 的原生 EI 釋放驗證與 D3 的錄製/全域停止/Windows 原生崩潰證據仍缺,詳見計畫與下方生命週期待辦。
1919
計畫 E(Android/iOS)、G(MCP 逐步揭露)、H(型別深化與總驗收)尚未開始。F 還缺:
2020

2121
- **Remote Desktop 與 Script Builder 仍在啟動時建立**(預設開啟),啟動時間 2.6–2.7 秒裡大半是它們與門面匯入。
2222
- **F1 的遺留面板釋放**:擁有者直接建立的核心表單與 Remote Desktop 在關閉時仍保留 widget;需隨 F3 遷移直接控制項引用、執行中工作與連線生命週期,再啟用銷毀。
2323
- **F2 其餘**:計畫指定的 `WorkspaceShell` 介面、右側可收合詳情,以及各面板的空白/錯誤/permission states 還沒全面交付。
2424
- **F3**(共用 worker、取消、關閉時不碰已銷毀物件、`webrtc_panel.py` 拆分)與 **F4**(啟動/記憶體基準、mixed-DPI、
2525
功能對等測試)尚未開始。
26-
- **F3 原生釋放驗收**:`test/unit_test/headless/test_remote_desktop_host_lifecycle.py:118,160` 的 Linux segfault 與 `test_remote_desktop_host_id.py` 的 Windows freed-thread-state abort 尚待 Linux GDB artifact 驗收、Windows 原生堆疊與根因修正;需驗證 Qt/背景工作生命週期。
26+
- **F3 原生釋放驗收**:`test/unit_test/headless/test_remote_desktop_host_lifecycle.py:118,160` 的 Linux Shiboken `runDeletionInMainThread` SIGSEGV 與 `test_remote_desktop_host_id.py` 的 Windows freed-thread-state abort 尚待回呼生命週期修正後的 CI 驗證與根因確認;Windows 原生堆疊仍缺。
2727

2828
**只記未完成的事。** 完成的工作記在 [docs/updates/](docs/updates/README.md)(每月一個批次檔,
2929
索引與查詢指令在它的 README),相容性變更寫進 [CHANGELOG.md](CHANGELOG.md);完成的項目

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ Native `FileTransferSender.send`, viewer `send_file` and host `push_file` now ac
279279

280280
GUI reconnect retains one in-memory folder baseline per panel. After reconnecting, press Start sync again: the same authenticated host fingerprint, absolute watch path and local principal resume failed/offline edits through a new authorised sender. A missing/changed host fingerprint, different root or principal starts a fresh baseline; a still-running old worker blocks handoff until it exits. Explicitly disabling folder sync resets the baseline, while stopping/reconnecting the viewer retains it. Closing the application clears it. The fingerprint follows the existing authenticated host-metadata trust model, not a new cryptographic identity guarantee. SDK callers can stop a FolderSyncEngine, export checkpoint() after actual worker retirement and pass that immutable FolderSyncCheckpoint to a replacement engine with the same path/recursion mode and an explicitly authorised sender. The baseline includes initially ignored files and received bytes; it is not a list of remote acknowledgements.
281281

282-
TCP/WebSocket clipboard transfers use the existing explicit SDK methods and GUI Push clipboard button. Received text/images do not automatically send or broadcast onward; deliberate repeated sends of identical content remain valid. Buffered viewer/host reads recheck the original connection, authentication and full-control permission before applying clipboard data. Viewer sends pin the original channel before encoding and refuse replacement. Queued GUI deliveries retain their concrete viewer and connecting user; permissions and ownership are refreshed before native clipboard writes, before explicit clipboard reads and again before sending. Stale/revoked work is discarded or reports its permission error. Ordinary provider/callback errors stay contained; original cancellation and hard-control objects propagate. Text/image JSON envelopes and existing APIs remain unchanged. Controlled local protocol, loopback and offscreen tests replace native clipboard providers; real-device and paid-API acceptance remain deferred.
282+
TCP/WebSocket clipboard transfers use the existing explicit SDK methods and GUI Push clipboard button. Received text/images do not automatically send or broadcast onward; deliberate repeated sends of identical content remain valid. Buffered viewer/host reads recheck the original connection, authentication and full-control permission before applying clipboard data. Viewer sends pin the original channel before encoding and refuse replacement. Queued GUI deliveries retain their concrete viewer and connecting user; permissions and ownership are refreshed before native clipboard writes, before explicit clipboard reads and again before sending. Stale/revoked work is discarded or reports its permission error. Ordinary provider/callback errors stay contained; original cancellation and hard-control objects propagate. Text/image JSON envelopes and existing APIs remain unchanged. Callback registration binds the original viewer weakly once, so a disconnected viewer does not keep itself and its Qt panel alive until cyclic GC. Actual queued deliveries still retain the original peer and authority until consumed or discarded. Controlled local protocol, loopback and offscreen tests replace native clipboard providers; real-device and paid-API acceptance remain deferred.
283283

284284
Experimental referenced assets use `SyncAssetBlob`, `SyncAssetReference`, `SyncAssetStore` and `SyncAssetHTTPClient`. Explicit `SyncBindings.assets` aliases freeze selected files into immutable SHA-256 references; asset-only changes create new causal versions. `SyncWorkspace` uploads verified chunks before preparing new definition requests, resumes interrupted transfers and downloads referenced bytes before complete inert publication. Existing private AC/MCP/Builder workspace/profile commands share these fields. `asset_cache_path` selects the client cache; signaling `--asset-store` / `AUTOCONTROL_SYNC_ASSET_STORE` select the separate server database. Authenticated version-one asset routes use 256-KiB chunks, a 64-MiB per-file bound and default per-account limits of 256 MiB, 8,192 chunks and 4,096 blob receipts. Quota exhaustion fails explicitly; immutable history has no automatic garbage collection. Paths in native output are relative to the generation root. `python -m examples.sync_referenced_assets` sends a real PNG and script through temporary loopback HTTP with no execution or active workspace change.
285285

‎README/README_zh-CN.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ python -c "import je_auto_control; je_auto_control.start_autocontrol_gui()"
267267

268268
GUI 重连时会为每个面板保留一份内存中的文件夹基准。重新连接后再按开始同步:相同的已认证 host 指纹、绝对监控路径与本机用户,会通过新授权的 sender 接续失败/离线修改。缺少或更换 host 指纹、路径或用户时建立新基准;旧工作仍在运行时拒绝交接,待它退出后才能重试。明确关闭文件夹同步会重置基准;停止/重连 viewer 则保留,关闭应用后清除。指纹沿用既有已认证 host 元数据的信任模型,没有新增密码学身份保证。SDK 可先停止 FolderSyncEngine,待工作确实退出后导出 checkpoint(),将不可变的 FolderSyncCheckpoint 传给路径/递归设置相同且 sender 已明确授权的新引擎。基准包含初始忽略文件与收到的内容,并非远端确认清单。
269269

270-
TCP/WebSocket 剪贴板传输沿用明确的 SDK 调用与 GUI 推送剪贴板按钮。收到的文字/图片不会自动传回或广播;用户仍可明确重复发送相同内容。Viewer/host 在读到缓冲消息后,会重新检查原始连接、认证及完整控制权限,再应用剪贴板数据。Viewer 会在编码前固定原通道,拒绝改发至替换后的连接。GUI 排队中的传输保留具体 viewer 与连接发起用户,写入本机剪贴板、明确读取及发送前都会重新检查权限与所有权。过期或撤销的工作会被忽略或报告权限错误;普通 provider/callback 错误仍受控,原始取消与强制中断对象继续传播。文字/图片 JSON 格式及既有 API 不变。受控本机协议、loopback 与 offscreen 测试替换原生剪贴板 provider;真机与付费 API 验收仍暂缓。
270+
TCP/WebSocket 剪贴板传输沿用明确的 SDK 调用与 GUI 推送剪贴板按钮。收到的文字/图片不会自动传回或广播;用户仍可明确重复发送相同内容。Viewer/host 在读到缓冲消息后,会重新检查原始连接、认证及完整控制权限,再应用剪贴板数据。Viewer 会在编码前固定原通道,拒绝改发至替换后的连接。GUI 排队中的传输保留具体 viewer 与连接发起用户,写入本机剪贴板、明确读取及发送前都会重新检查权限与所有权。过期或撤销的工作会被忽略或报告权限错误;普通 provider/callback 错误仍受控,原始取消与强制中断对象继续传播。文字/图片 JSON 格式及既有 API 不变。Callback 注册仅以弱引用绑定一次原始 viewer,避免断线 viewer 自己保留自己及 Qt 面板直到循环垃圾回收;实际排队数据仍保留原始 peer 与权限,直到处理或丢弃。受控本机协议、loopback 与 offscreen 测试替换原生剪贴板 provider;真机与付费 API 验收仍暂缓。
271271

272272
实验性引用资产使用 `SyncAssetBlob`、`SyncAssetReference`、`SyncAssetStore` 与 `SyncAssetHTTPClient`。明确的 `SyncBindings.assets` 别名将选定文件封存为不可变 SHA-256 引用;只改资产也会建立新的因果版本。`SyncWorkspace` 在准备新定义请求前上传已验证分块,支持中断续传,并在完整且未启用的发布前下载引用内容。现有私有 AC/MCP/Builder 工作区/profile 命令共用这些字段。`asset_cache_path` 选择 client cache;signaling 的 `--asset-store`/`AUTOCONTROL_SYNC_ASSET_STORE` 选择独立 server 数据库。带验证的第一版资产路由使用 256-KiB 分块、每文件 64 MiB 上限,每账户默认限制 256 MiB、8,192 块与 4,096 条 blob 收据。超过配额会明确失败;不可变历史不自动回收。原生输出路径相对于 generation 根目录。`python -m examples.sync_referenced_assets` 经临时 loopback HTTP 传送真实 PNG 与脚本,不执行脚本或改当前工作区。
273273

‎README/README_zh-TW.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ python -c "import je_auto_control; je_auto_control.start_autocontrol_gui()"
266266

267267
GUI 重連時會為每個面板保留一份記憶體內的資料夾基準。重新連線後再按開始同步:相同的已驗證 host 指紋、絕對監控路徑與本機使用者,會透過新授權的 sender 接續失敗/離線修改。缺少或更換 host 指紋、路徑或使用者時建立新基準;舊工作仍在執行時拒絕交接,待它退出後才能重試。明確關閉資料夾同步會重設基準;停止/重連 viewer 則保留,關閉應用程式後清除。指紋沿用既有已驗證 host 中繼資料的信任模型,沒有新增密碼學身份保證。SDK 可先停止 FolderSyncEngine,待工作確實退出後匯出 checkpoint(),將不可變的 FolderSyncCheckpoint 傳給路徑/遞迴設定相同且 sender 已明確授權的新引擎。基準包含初始略過檔案與收到的內容,並非遠端確認清單。
268268

269-
TCP/WebSocket 剪貼簿傳輸沿用明確的 SDK 呼叫與 GUI 推送剪貼簿按鈕。收到的文字/圖片不會自動傳回或廣播;使用者仍可明確重複傳送相同內容。Viewer/host 在讀到緩衝訊息後,會重新檢查原始連線、驗證及完整控制權限,再套用剪貼簿資料。Viewer 會在編碼前固定原通道,拒絕改送至替換後的連線。GUI 排隊中的傳輸保留具體 viewer 與連線發起使用者,寫入本機剪貼簿、明確讀取及送出前都會重新檢查權限與擁有權。過期或撤銷的工作會被略過或回報權限錯誤;一般 provider/callback 錯誤仍受控,原始取消與強制中斷物件繼續傳播。文字/圖片 JSON 格式及既有 API 不變。受控本機協定、loopback 與 offscreen 測試替換原生剪貼簿 provider;真機與付費 API 驗收仍暫緩。
269+
TCP/WebSocket 剪貼簿傳輸沿用明確的 SDK 呼叫與 GUI 推送剪貼簿按鈕。收到的文字/圖片不會自動傳回或廣播;使用者仍可明確重複傳送相同內容。Viewer/host 在讀到緩衝訊息後,會重新檢查原始連線、驗證及完整控制權限,再套用剪貼簿資料。Viewer 會在編碼前固定原通道,拒絕改送至替換後的連線。GUI 排隊中的傳輸保留具體 viewer 與連線發起使用者,寫入本機剪貼簿、明確讀取及送出前都會重新檢查權限與擁有權。過期或撤銷的工作會被略過或回報權限錯誤;一般 provider/callback 錯誤仍受控,原始取消與強制中斷物件繼續傳播。文字/圖片 JSON 格式及既有 API 不變。Callback 註冊只以弱引用綁定一次原始 viewer,避免斷線 viewer 自己保留自己及 Qt 面板直到循環垃圾回收;實際排隊資料仍保留原始 peer 與權限,直到處理或丟棄。受控本機協定、loopback 與 offscreen 測試替換原生剪貼簿 provider;真機與付費 API 驗收仍暫緩。
270270

271271
實驗性引用資產使用 `SyncAssetBlob`、`SyncAssetReference`、`SyncAssetStore` 與 `SyncAssetHTTPClient`。明確的 `SyncBindings.assets` 別名將選定檔案封存為不可變 SHA-256 參照;只改資產也會建立新的因果版本。`SyncWorkspace` 在準備新定義請求前上傳已驗證分塊,支援中斷續傳,並在完整且未啟用的發布前下載引用內容。既有私有 AC/MCP/Builder 工作區/profile 命令共用這些欄位。`asset_cache_path` 選取 client cache;signaling 的 `--asset-store`/`AUTOCONTROL_SYNC_ASSET_STORE` 選取獨立 server 資料庫。具驗證的第一版資產路由使用 256-KiB 分塊、每檔 64 MiB 上限,每帳號預設限制 256 MiB、8,192 塊與 4,096 筆 blob 收據。超過配額會明確失敗;不可變歷史不自動回收。原生輸出路徑相對於 generation 根目錄。`python -m examples.sync_referenced_assets` 經暫存 loopback HTTP 傳送真實 PNG 與腳本,不執行腳本或改目前工作區。
272272

‎architecture.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ safe source strings/references and masks resolved input strings/dynamic keys.
211211
| `je_auto_control/utils/executor/` | Execution core: `Executor.event_dict` (`AC_*` name → callable) in `action_executor.py`, block commands in `flow_control.py`, validation in `action_schema.py`. |
212212
| `je_auto_control/utils/` | Headless capability layer, one subpackage per feature, zero Qt imports. Grouped by theme in `architecture_explore.md` §5.4. |
213213
| `je_auto_control/utils/{socket_server,rest_api,mcp_server,pytest_plugin}/` | Server and integration surfaces (§3). |
214-
| `je_auto_control/utils/{remote_desktop,usb,usbip}/` | Remote desktop (TCP / WebSocket / WebRTC) and USB passthrough; file_sync.py owns content digests, explicit receive receipts and stopped folder workers; webrtc_file_write.py confirms bounded/cancellable channel writes, webrtc_file_commit.py owns weak channel/epoch bindings and authenticated publication confirmation, file_sync.py exports immutable retired-worker checkpoints and folder_sync_io.py binds explicit GUI resume to peer/root/principal, clipboard_delivery.py binds explicit TCP/WS clipboard work to concrete peers and refreshed local authority, webrtc_file_tasks.py owns queued inbox pushes, webrtc_file_receive.py binds inbound file lifetimes to the authenticated peer/channel, file_receipts.py freezes local publication metadata, webrtc_file_callbacks.py preserves native path/receipt/audit callback semantics, and GUI folder_sync_io.py binds jobs to authorization and peer identity. |
214+
| `je_auto_control/utils/{remote_desktop,usb,usbip}/` | Remote desktop (TCP / WebSocket / WebRTC) and USB passthrough; file_sync.py owns content digests, explicit receive receipts and stopped folder workers; webrtc_file_write.py confirms bounded/cancellable channel writes, webrtc_file_commit.py owns weak channel/epoch bindings and authenticated publication confirmation, file_sync.py exports immutable retired-worker checkpoints and folder_sync_io.py binds explicit GUI resume to peer/root/principal, clipboard_delivery.py binds explicit TCP/WS clipboard work to concrete peers and refreshed local authority, using once-bound weak registration to avoid self-owned viewers while queued deliveries retain original identity, webrtc_file_tasks.py owns queued inbox pushes, webrtc_file_receive.py binds inbound file lifetimes to the authenticated peer/channel, file_receipts.py freezes local publication metadata, webrtc_file_callbacks.py preserves native path/receipt/audit callback semantics, and GUI folder_sync_io.py binds jobs to authorization and peer identity. |
215215
| `je_auto_control/utils/webrunner_bridge/` | Optional bridge that runs WebRunner `WR_*` commands (§6). |
216216
| `je_auto_control/wrapper/` | Platform-neutral API (`auto_control_mouse/keyboard/screen/image/record/window.py`); `platform_wrapper.py` picks the backend; `backend_contract.py` types the seam; `window_backends/`. |
217217
| `je_auto_control/{windows,osx,linux_with_x11,linux_wayland}/` | Desktop OS backends; only the running OS's backend is imported. |

0 commit comments

Comments
 (0)