dsh-cindy-host 0.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/CHANGELOG.md +462 -0
  2. package/LICENSE +21 -0
  3. package/README.md +178 -0
  4. package/cordis.patch.yml +6 -0
  5. package/doc/architecture.md +34 -0
  6. package/doc/cindy-android-verify.md +362 -0
  7. package/doc/cindy-mobile-freeze-handoff.md +124 -0
  8. package/doc/cindy-phone-link.md +2902 -0
  9. package/doc/desktop-runtime-fix-20260930.md +69 -0
  10. package/doc/development-plan.md +19 -0
  11. package/doc/file-peer-apply-recovery-20260921.md +35 -0
  12. package/doc/file-peer-installed-hotfix-20260921.md +53 -0
  13. package/doc/file-peer-minimal-apply-20260921.md +78 -0
  14. package/doc/file-peer-original-contract-alignment-20260921.md +119 -0
  15. package/doc/file-peer-phone-export-observation-20260921.md +75 -0
  16. package/doc/file-peer-product-contract-audit-20260921.md +106 -0
  17. package/doc/file-peer-root-cause-20260921.md +53 -0
  18. package/doc/file-transfer.md +50 -0
  19. package/doc/host-performance-20260930.md +91 -0
  20. package/doc/neonabyss-apk-file-peer-diagnosis-20260921.md +87 -0
  21. package/doc/new-session-sync-fix-20260930.md +59 -0
  22. package/doc/patches/cindy-peer-silence-probe.patch +1112 -0
  23. package/doc/projection-read-probe.md +77 -0
  24. package/doc/publishing.md +259 -0
  25. package/doc/releasing.md +149 -0
  26. package/doc/research.md +24 -0
  27. package/doc/session-discovery-2026-09-20.md +67 -0
  28. package/lib/client.js +763 -0
  29. package/package.json +84 -0
  30. package/src/auth-session.js +189 -0
  31. package/src/authorization-policy.js +59 -0
  32. package/src/cindy-channels.js +1707 -0
  33. package/src/cindy-login-flow.js +159 -0
  34. package/src/cindy-login.js +80 -0
  35. package/src/cindy-message-row.js +432 -0
  36. package/src/cindy-session-row.js +171 -0
  37. package/src/console-sink.js +6 -0
  38. package/src/contracts.js +26 -0
  39. package/src/credential-store.js +128 -0
  40. package/src/demo.js +9 -0
  41. package/src/dsh-active-sessions.js +59 -0
  42. package/src/dsh-host-source.js +62 -0
  43. package/src/dsh-message-fold.js +608 -0
  44. package/src/dsh-plugin.js +1932 -0
  45. package/src/dsh-session-source.js +529 -0
  46. package/src/file-peer-protocol.js +7 -0
  47. package/src/file-peer-rtc.js +41 -0
  48. package/src/fixture-source.js +13 -0
  49. package/src/host-approvals.js +329 -0
  50. package/src/host-attachments.js +249 -0
  51. package/src/host-authorization.js +51 -0
  52. package/src/host-demo.js +3 -0
  53. package/src/host-export-jobs.js +356 -0
  54. package/src/host-export-owner.js +62 -0
  55. package/src/host-file-browser.js +400 -0
  56. package/src/host-file-ice.js +63 -0
  57. package/src/host-file-peer.js +289 -0
  58. package/src/host-file-reference.js +16 -0
  59. package/src/host-files.js +148 -0
  60. package/src/host-frame-budget.js +310 -0
  61. package/src/host-goals.js +284 -0
  62. package/src/host-history-view-limits.js +22 -0
  63. package/src/host-history-view.js +417 -0
  64. package/src/host-input-queue.js +505 -0
  65. package/src/host-media-fetch.js +511 -0
  66. package/src/host-media.js +678 -0
  67. package/src/host-models.js +231 -0
  68. package/src/host-palette.js +111 -0
  69. package/src/host-push-channels.js +37 -0
  70. package/src/host-reconnect.js +54 -0
  71. package/src/host-routes.js +266 -0
  72. package/src/host-settings.js +41 -0
  73. package/src/host-status.js +216 -0
  74. package/src/host-transfer-diagnostics.js +50 -0
  75. package/src/host.js +2497 -0
  76. package/src/login.js +31 -0
  77. package/src/logout.js +3 -0
  78. package/src/projection-read-model-sink.js +6 -0
  79. package/src/projection.js +43 -0
  80. package/src/session-flags.js +160 -0
  81. package/src/session-publisher.js +123 -0
  82. package/src/session-read-model.js +6 -0
@@ -0,0 +1,69 @@
1
+ # Desktop 0.2 Cindy connection hotfix — 2026-09-30
2
+
3
+ ## Scope and authoritative copies
4
+
5
+ - Desktop: DSH 0.2.0-rc.2, profile `C:\Users\chany\.dsh\profiles\desktop`, URL `http://127.0.0.1:19387`.
6
+ - Installed package: `node_modules/dsh-cindy-host-demo`, version 0.1.16 with this local hotfix (not a published release).
7
+ - Migration source: `G:\Projects\DSH-cindy-host-020`, Git worktree branch `feat/dsh-0.2.0-rc.2`.
8
+ - `G:\Projects\DSH-cindy-host` is the older main worktree, NOT the source to overwrite the desktop installation from. Existing unrelated dirty work in both worktrees was preserved.
9
+
10
+ ## Observed causes
11
+
12
+ 1. Profile patch already stored `transportEnabled: true` and `remoteControlEnabled: true`, but live Host remained disconnected with `login.authenticated=false`, `login.required=false`, no host/device identity and no relay attempts. The 0.1.16 compatibility scope copied `apply(ctx, config)` once and assumed future settings changes would remount the plugin. DSH 0.2 volatile Config edits deliberately do not remount it. Settings service source and live Inspect confirm `settings/document-updated(ns, revision)` and `settings.describe()` are the re-read contract.
13
+ 2. Installed keytar 7.9.0 JavaScript package existed, but `build/Release/keytar.node` was absent. Its lazy require failure was cached as null and `loadSession` returned null, conflating inaccessible credentials with absent credentials. The existing OS credential remained readable through the development installation. No credentials were cleared, printed, or refreshed during diagnosis.
14
+ 3. Client ConfigFormController returns false for rejected writes. The switch ignored that result; it also wrote the two flags separately. A successful authenticated state did not display account information/logout except when the connection failed.
15
+
16
+ The settings namespace remains `dsh-cindy-host`, NOT `include:dsh-cindy-host`: the latter is the Loader inspection id. Runtime SettingsForms describe/write use `entry.options.id`. ConfigForms was not present in the Client Inspect catalog; its exact methods were inspected in the packaged desktop implementation instead of guessed.
17
+
18
+ ## Implemented changes
19
+
20
+ - Host settings scope re-reads resolved settings after document updates, detaches nested snapshots, suppresses duplicates and catches sync/async watcher errors.
21
+ - Runtime reconciles latest settings after asynchronous startup; early boundary error reporting handles an undefined runtime safely.
22
+ - Missing credential storage reports `CREDENTIAL_STORE_UNAVAILABLE` and a failed connection, not a request for another login.
23
+ - Client writes both flags atomically through `mutate` when available; legacy `set` remains supported and explicit false is rejected.
24
+ - Status read errors are displayed rather than silently becoming disconnected; authenticated UI shows a masked identifier and logout in waiting/connected states too.
25
+
26
+ ## Local deployment
27
+
28
+ Four files were backed up and copied from migration source to the desktop installed package, with SHA256 equality checks:
29
+
30
+ - `src/dsh-plugin.js`
31
+ - `src/auth-session.js`
32
+ - `src/host.js`
33
+ - `lib/client.js` (handwritten served bundle; no build step in this package)
34
+
35
+ Backup: `C:\Users\chany\.dsh\profiles\desktop\cindy-fix-backup-20260930` (source files only, no credential values).
36
+
37
+ The missing installed keytar binary was restored from the same-version local development installation:
38
+
39
+ - Source: `G:\Projects\DSH-cindy-host-020\node_modules\keytar\build\Release\keytar.node`
40
+ - Destination: `C:\Users\chany\.dsh\profiles\desktop\node_modules\keytar\build\Release\keytar.node`
41
+ - Windows x64, N-API v3, 707584 bytes.
42
+ - SHA256: `90e35de89ab5e5f9290e4ff1bbadcf221a82b2aa0d9b922187dc980adff3c831`.
43
+ - A fresh Node process anchored to the installed plugin successfully loaded the store and confirmed only presence booleans for saved credentials. This is NOT an Electron-in-process or authentication-server verification.
44
+
45
+ No install/build scripts were run. A future reinstall may overwrite the hotfix or omit keytar's binary again; release packaging still needs a supported prebuild/build installation path with explicit build-script permission.
46
+
47
+ ## Verification and remaining boundary
48
+
49
+ - Focused host settings/plugin/boundary tests: 46 passed.
50
+ - Focused auth/runtime tests: 48 passed.
51
+ - Focused client tests: 23 passed.
52
+ - Full initial regression: 695 passed, 1 skipped, 0 failed. Final all-changes regression: **702 passed, 1 skipped, 0 failed** (703 total, approximately 30 seconds).
53
+ - Channel audit: 196 named invoke channels, 52 served, 144 deliberately declined, 0 unclassified; push classification also 0 unclassified. This is coverage accounting, not proof every DSH 0.2 session API works live.
54
+ - Test logs: `G:\Projects\DSH-cindy-host\artifacts\desktop-runtime-inspect\migration-tests.log` and `migration-tests-final.log`.
55
+
56
+ After file deployment, the existing live process still reports disconnected and its diagnostic boundary list lacks `settings-document-updated`, proving that it is still executing the old Host module. No whole-desktop restart or plugin enable/disable was performed. Do not claim the desktop connection is restored until the following are verified:
57
+
58
+ 1. User fully exits and restarts Desktop (not just closes a window to tray), then reopens this conversation.
59
+ 2. Read the same URL's Cindy status. New boundary list should include `settings-document-updated`; enabled settings should trigger credential restore and relay connection.
60
+ 3. Check whether normal stored-session refresh succeeds. Stored access-token expiry metadata was past; refresh-token validity was deliberately not tested independently. Do not request fresh login unless the real restored runtime reports it necessary.
61
+ 4. Open Settings → Cindy Phone Link and confirm account/status rendering, switch on/off behavior, then attach the phone and verify device listing and session reads.
62
+
63
+ No client automatic reload is promised, and a separate replacement server would not update this desktop.
64
+
65
+ ## Post-restart observation (2026-09-30 13:20 UTC)
66
+
67
+ Live status now reports `connected`, authenticated=true, login.required=false, 7 known devices and 149 projected sessions. Diagnostic boundaries include `settings-document-updated`; the repaired Host module is active. The user confirms phone synchronization with existing running sessions works. Device count includes discovered devices, not seven simultaneous controlling phones.
68
+
69
+ The user separately reports unstable newly created sessions, including `No DSH session 02df58ff-6ec6-43fd-bb74-b3c79e5d0efa`. The ID has a real local projection-cache record; refusals include `local-db:sessions:get` and `local-db:messages:view`. This is a new-session lookup/visibility investigation, not evidence that credential or relay repair failed. Do not clear credentials or phone data.
@@ -0,0 +1,19 @@
1
+ # Development Plan
2
+
3
+ ## Slice 0 — documentation and contracts
4
+ Create this research record, architecture contract, repository guide, and a project plan before code.
5
+
6
+ ## Slice 1 — pure projection core
7
+ Implement DTOs, allowlisted activity projector, in-memory model, fixture source, console sink, and unit tests. No DSH package import is needed for this slice.
8
+
9
+ ## Slice 2 — DSH host mount
10
+ Create the profile-bundle adapter that receives `ctx.apiProxy`, uses `InProcessApiClient`, opens events streams, and supplies the pure projection core. Pin public `@deepseek-ai/dsh-host-apiproxy@0.1.1-rc.2` (and any other imported public DSH packages) in the host bundle; never resolve DSH's nested dependency directory. Add a live-host smoke test only in an authenticated DSH process.
11
+
12
+ ## Slice 3 — separately approved Cindy transport
13
+ Only after the demo is validated, design the Cindy device registration, authentication, topic schema, and Android compatibility integration. It is intentionally not part of the current demo.
14
+
15
+ ## Quality gates
16
+ - Node test suite passes.
17
+ - Demo has no write/command/file/credential imports.
18
+ - Fixtures and console output are free of sensitive DSH payload fields.
19
+ - Stream failure produces a generic stale state and rebaseline; it never logs raw error or frame payload.
@@ -0,0 +1,35 @@
1
+ # file-peer 中断后核验:未应用,漂移停手
2
+
3
+ 核验时间:2026-09-21 16:05(+08)。**安装版仍为15秒;未完成本补丁应用。已发生宿主重启,但不能把它当作补丁生效。按授权中的PID/环境漂移停止条件,未继续edit或再次重启。**
4
+
5
+ ## 当前事实
6
+
7
+ - 3080监听PID **41128**,启动 **15:53:31.409**;原授权预检目标是PID40916/原cwd C:/Users/chany。
8
+ - web profile解析入口仍为 `C:/Users/chany/.dsh/profiles/web/node_modules/dsh-cindy-host-demo/src/dsh-plugin.js`,安装包版本 **0.1.10**。
9
+ - 安装目标 `C:/Users/chany/.dsh/profiles/web/node_modules/dsh-cindy-host-demo/src/host-file-peer.js:185` 仍为 `15_000`,12465字节,最后写入仍是2026-09-20 18:54:14。
10
+ - 目标SHA256:`bad5bf48fbe711e7248a71d9902d1fb688dd6d2a2df89232a532deba7525df6e`,等于before,**不是**预期after `c9d44db98c630a6c0d863aef40721e7f4606b7fd9e803b5db4cc0c7ebc5484fa`。
11
+ - 已有原字节备份:`G:/Projects/DSH-cindy-host/.sandbox/offer-hotfix-2026-09-21T07-29-56.561Z/host-file-peer.js.before`;恢复时重新核磁盘SHA256,精确等于before。保留,不覆盖。
12
+ - 本会话中断前只完成PID/before预检、restart dry-run、原安装文件备份及52运行文件的内存hash快照;**未执行目标edit,也未提交--apply**。内存快照不视为重启后仍可用的持久基线。
13
+
14
+ ## 实际重启审计
15
+
16
+ 只读 `C:/Users/chany/.agents/logs/dsh-restart/host-restart.log:34–39`(未读含token的dsh-web.log):
17
+
18
+ | 日志时间(换算+08) | 事实 |
19
+ |---|---|
20
+ | 15:29:25–15:29:27 | 记录60秒重启请求,target40916,args=[web],cwd=C:/Users/chany |
21
+ | 15:30:28.779 | started pid41144 |
22
+ | 15:52:28–15:52:31 | 又记录60秒重启请求,target41144,args=[web],**cwd=G:/Projects/claw-kit** |
23
+ | 15:53:31.500 | started pid41128;与当前监听进程相符 |
24
+
25
+ 日志证明两次重启,**不证明本补丁被写入,也不在此猜测由哪个会话发起**。最后一次启动目录已与原应用方案不一致;不重放旧PID/旧cwd,亦不擅自把新目标纳入原授权。
26
+
27
+ ## 最小只读状态与阻碍
28
+
29
+ 16:05:40 `GET http://127.0.0.1:3080/api/dsh-cindy-host/status`:installed=true,status.state=connected;peer调用/拒绝计数字段未出现、peer拒绝环为空。该状态仅说明Host在线,不代表25秒代码已加载、peer已建立或手机接收成功。
30
+
31
+ **阻碍:原PID守卫已不匹配,且最新启动cwd改变。** 原任务2仍未完成,任务3尚不能验收。原claw计划转等待,保留备份/检查事实,绝不标作修复完成。未碰question/index/session-bridge,不重发APK、不加Android构建前置,不修改生产。
32
+
33
+ ## 下一步
34
+
35
+ 由主助手协调并确认以哪个现宿主及cwd继续;若再次授权应用,先重新核实时PID/cwd、profile、before hash和其他运行文件基线,再按原单文件/备份/after hash/60秒重启约束执行,不能沿用本报告PID作为未来盲操作目标。没有成功应用的证据前,不要求用户做手机修复后验收。应用后才按原报告验证新PID/安装hash/静态导入链,再等用户一次原Export入口非敏感、未缓存>64KiB小文件的安全观测。
@@ -0,0 +1,53 @@
1
+ # file-peer:重启后静态装载已核验,手机peer待验
2
+
3
+ **2026-09-21 16:41:05 +08 已实际完成安装副本单字节修复;本轮没有提交任何重启。此前报告的未应用状态已被本次事实更新。**
4
+
5
+ ## 16:41落盘证据(历史)
6
+
7
+ - 当前3080:**PID25112**,启动16:21:19.852,实际cwd **G:/Projects/claw-kit/**(只读current-directory字段核实)。已不同于上一轮41128;本次按最新“目标profile/链正确则落盘、不单独重启”授权推进。
8
+ - 当前进程参数含web,无--profile覆盖,工具继承DSH_HOME为空;web profile清单为dsh-profile-web,包含dsh-cindy-host-demo@**0.1.10**;该profile解析入口仍是安装根下src/dsh-plugin.js。cwd变化不改变该绝对profile安装目标。
9
+ - 唯一修改artifact:`C:/Users/chany/.dsh/profiles/web/node_modules/dsh-cindy-host-demo/src/host-file-peer.js:185`,`15_000 → 25_000`;mtime **16:41:05.0839112**。版本号不变。
10
+ - 独立原字节备份:`G:/Projects/DSH-cindy-host/.sandbox/offer-hotfix-2026-09-21T08-41-05.056Z/host-file-peer.js.before`。旧15:29备份也完好,本次未覆盖它。
11
+ - 两文件均12465字节;逐字节比较只有偏移**8952**变化:ASCII **49→50(1→2)**。
12
+ - 修改前后对同一安装包52个运行文件重新计算SHA256:只有host-file-peer.js变化,其余**51文件完全不变**,包含host-approvals.js、host.js、包manifest及bundle patch。未改question、bridge、索引、profile配置或其他生产文件。
13
+
14
+ ```text
15
+ before / backup SHA256
16
+ bad5bf48fbe711e7248a71d9902d1fb688dd6d2a2df89232a532deba7525df6e
17
+ after / current installed SHA256
18
+ c9d44db98c630a6c0d863aef40721e7f4606b7fd9e803b5db4cc0c7ebc5484fa
19
+ ```
20
+
21
+ ## 合并重启前交接(历史,结果见下)
22
+
23
+ 1. 当前PID25112未变且启动早于文件替换,**落盘不等于运行中已加载25秒**。不为本补丁单独重启,等待你与claw-kit下一次必要重启合并。
24
+ 2. claw-kit安装/更新若涉及profile依赖重装,可能覆盖此本地hotfix。**所有其他准备完成之后、合并重启之前,最后复核上述绝对目标的after hash及web profile解析入口**。若hash变回before或出现其他值,先报告/核原因,不再空重启,也不全目录复制dirty树。
25
+ 3. 当前实际cwd是G:/Projects/claw-kit,不要机械复用旧C:/Users/chany或旧PID。由主助手在合并时按实时端口所有者/启动参数/cwd保留正确宿主、web profile与3080;本会话没有发起任何restart命令。
26
+ 4. 合并后最小装载核验:新PID启动晚于16:41:05(并晚于最后文件校验)、after hash仍正确、同一profile解析及静态导入链dsh-plugin.js→host.js→host-file-peer.js;安全status只看installed/state。API没有offer预算字段,不将caps/connected冒充独立25秒计时验证。
27
+
28
+ ## 未完成边界/恢复
29
+
30
+ - 重启后25秒代码静态装载核验已通过(见下);**手机peer建立或1.315GB APK成功仍无证据**。未重跑53测试/黑洞,未增加Android包前置,未传APK。
31
+ - 装载确认后再等用户从原Export入口试一次允许分享的、非敏感、未缓存>64KiB小文件;现有GET /api/dsh-cindy-host/status只观察同手机peer调用/安全拒绝与时间。现环没有action/起始耗时;小文件可能OSS回退,分享成功或计数增长不独立证明peer成功。
32
+ - 回滚仅在当前hash仍为after时恢复本次独立备份原字节,核before,再交主助手统一重载;若目标已被第三方改变则先核差异,不盲覆盖。
33
+ - 落盘与重启后静态核验均无阻碍;唯一需要用户手机操作的验收由主助手保留为真实待办,本轮不触发。
34
+
35
+ ## 17:24重启后只读核验(当前结论)
36
+
37
+ - 3080监听新PID **48208**,启动 **2026-09-21 17:09:37.1594804 +08**,晚于目标文件写入 **16:41:05.0839112**。
38
+ - 安装目标仍12465字节、mtime未变;SHA256仍为上述after **c9d44db9…c5484fa**(完整值见上),第185行明确为 **25_000**。
39
+ - 当前进程参数web,无--profile覆盖;工具继承DSH_HOME为空。`C:/Users/chany/.dsh/profiles/web/package.json`仍为dsh-profile-web,声明并装有dsh-cindy-host-demo@0.1.10。
40
+ - 该profile解析入口:`C:/Users/chany/.dsh/profiles/web/node_modules/dsh-cindy-host-demo/src/dsh-plugin.js`。已读安装副本确认静态链:dsh-plugin.js:12导入host.js;host.js:41导入host-file-peer.js、411–416构造manager;host-file-peer.js:185设置25秒offer截止。
41
+ - **新进程 + 补丁写入早于启动 + after哈希未变 + 正确profile静态导入链,构成这次约定的装载核验证据。** 没有读取内存timer,也没有现场制造25秒超时;API不存在offerTimeoutMs字段,不能拿caps当计时读数。
42
+ - 17:24:06.862的既有GET状态:installed=true、status.state=connected;file-peer invokeTotals/refusalTotals条目缺省,recentInvokes/recentRefusals中peer记录均为空。计数确为channel→count对象(host.js:2267–2268)。只记“尚无本机环中手机peer观测”,不是验收成功。
43
+ - 本轮只读生产、只更新此报告及工作流;未改生产文件、未重启、未发任何selftest/offer/open、未触发手机或传APK。
44
+
45
+ ## 交主助手保留的唯一用户待办(未执行)
46
+
47
+ **在现有手机的原项目文件浏览器,选一个允许分享的、非敏感且未缓存的>64KiB小文件(例如约128KiB),点一次原“导出/分享”,保持前台等结果,回报点按时刻和原始提示/是否出现分享结果。不要选1.315GB APK,也不要连续重试。** 无需Android新包或诊断包。
48
+
49
+ 主助手配合,不额外要求用户做诊断:
50
+ 1. 操作前后及操作窗口内短时有限采样现有 `GET http://127.0.0.1:3080/api/dsh-cindy-host/status`,只保留同手机src关联后的peer at/channel/ok/code、调用/拒绝计数差值;报告对src用别名。若来源不能对应该手机,明确无法归因。不要输出preview、SDP、凭据。
51
+ 2. 记录用户操作窗口及原提示。若有FILE_PEER_TIMEOUT或其他安全拒绝码,保留原码和完成时间;现环无peer action/请求起始/耗时,不能将点按至提示的总时间伪装成精确offer耗时。
52
+ 3. 无peer记录就保持“无peer证据”;小文件分享成功或peer计数增长仍可能含OSS回退,**不能独立宣布DataChannel建立或peer完成**。记录实际成功/失败/无法确定阶段即可;主助手在没有可归因peer证据时不得勾掉该真实待办。
53
+ 4. 本轮没有启动这次采样或手机动作;静态装载通过与手机验收是两项结论,完整APK传输更是后续独立选择。
@@ -0,0 +1,78 @@
1
+ # file-peer:现宿主最小应用交接
2
+
3
+ **准备完成,未应用/重启。只改安装副本一个字面量;保持现配置不变时需经用户确认重启原DSH。未重跑53测试或黑洞复现。**
4
+
5
+ ## 实际目标与排除项
6
+
7
+ - 3080当前PID **40916**,启动2026-09-21 11:10:11;Node=`D:/Program Files/nodejs/node.exe`,入口=`C:/Users/chany/AppData/Roaming/npm/node_modules/@deepseek-ai/dsh/lib/bin.js`,参数=`web`。
8
+ - **实际进程cwd为 `C:/Users/chany/`**,只读进程current-directory字段核实,未读取环境/凭据缓冲。不要把本会话工作树当成原启动目录。
9
+ - `C:/Users/chany/.dsh/profiles/web/package.json`:依赖 **dsh-cindy-host-demo@0.1.10**,patchReload=live。该profile的模块解析指向独立安装根 `C:/Users/chany/.dsh/profiles/web/node_modules/dsh-cindy-host-demo`,不是工作树链接,realpath相同。
10
+ - **唯一应用目标T**:`C:/Users/chany/.dsh/profiles/web/node_modules/dsh-cindy-host-demo/src/host-file-peer.js`。原文件12465字节,写入2026-09-20 18:54:14,早于进程启动。安装静态链:`dsh-plugin.js:12 → host.js:41,411 → host-file-peer.js`。
11
+
12
+ 比较安装与工作树的src/*.js、lib/*.js、package.json、cordis.patch.yml共52文件,无新增运行JS;只有3项不同:
13
+
14
+ | 文件 | 差异 | 本次 |
15
+ |---|---|---|
16
+ | src/host-file-peer.js | 15→25秒及工作树4行解释注释 | 仅替换安装版截止字面量 |
17
+ | src/host-approvals.js | questionObservers、提问/撤销通知、未决问题回放和observeUserQuestions | **排除4961…问答改动** |
18
+ | src/host.js | 暴露observeUserQuestions及注释 | **排除** |
19
+
20
+ 其余49文件字节一致。dirty相对Git HEAD不等于相对安装版差异;**不复制整个树、不打包dirty树、不npm update/link,不改profile/lockfile/client/Cindy**。安装版号仍为0.1.10,此次是可回滚本地hotfix,非已发布新版本。
21
+
22
+ ## 为什么不能只切开关/重新connect
23
+
24
+ 实际全局DSH代码:
25
+ - `dsh-base/cordis.patch.yml:19–25` 的源码HMR行默认disabled=true;web profile的cordis.yml/cordis.patch.yml未发现hmr覆盖。
26
+ - `lib/profile-boot-Dk-7KqJc.js:321–337` 的live配置监听后备HMR使用 **root: []**,不监听插件JS。
27
+ - `cordis-plugin-loader/lib/index.js:270–279,466`:同名配置重载复用旧callback;重新import同一URL仍受ESM缓存约束。
28
+ - `cordis-plugin-hmr/lib/index.js:353–388` 才清模块图缓存;默认忽略node_modules(441–445)。已装plugin-inventory是只读投影,不是源码reload接口。
29
+
30
+ **未发现现配置下受支持的单Host源码reload入口。** 浏览器刷新、Cindy reconnect、配置toggle/重建实例不能证明静态依赖更新。临时配置源码HMR或注入内部loadCache会扩大变更边界,不纳入此方案。无需清磁盘缓存或重建Web;新进程重新加载ESM链及Host/filePeer manager。
31
+
32
+ ## 执行与回滚:由主助手确认协调后执行
33
+
34
+ 1. 用户确认整宿主短暂停机,现有会话/任务到安全边界;也需协调其他插件所有者,整宿主重启会加载其已落盘变更,本轮只核了Cindy安装包。应用前复核3080 PID、profile、cwd、T哈希;漂移就停止重核。
35
+ 2. 将**现安装T原字节**单文件备份到一次性路径,如 `G:/Projects/DSH-cindy-host/.sandbox/offer-hotfix-20260921-<时间戳>/host-file-peer.js.before`。不得覆盖旧备份;记录路径、版本、PID、hash,验备份hash=before。不要从dirty树取备份。
36
+ 3. 先read T,再edit唯一一处:
37
+ - old:`c.offerTimer = setTimeout(() => close(c, 'FILE_PEER_TIMEOUT'), 15_000);`
38
+ - new:`c.offerTimer = setTimeout(() => close(c, 'FILE_PEER_TIMEOUT'), 25_000);`
39
+ 仅一个ASCII字节变化,其余字节保持;比较备份并核after hash。未携带源码的4行注释,因此after hash有别于工作树整文件hash。
40
+
41
+ ```text
42
+ before SHA256: bad5bf48fbe711e7248a71d9902d1fb688dd6d2a2df89232a532deba7525df6e
43
+ after SHA256: c9d44db98c630a6c0d863aef40721e7f4606b7fd9e803b5db4cc0c7ebc5484fa
44
+ ```
45
+
46
+ 4. 以下dry-run已实跑:PID40916、args=[web]、cwd=C:/Users/chany、DSH_HOME继承默认、grace60。操作时再核一次:
47
+
48
+ ```powershell
49
+ node 'C:/Users/chany/.agents/skills/dsh-restart/scripts/restart-dsh.mjs' --port 3080 --cwd 'C:/Users/chany'
50
+ # 仅确认、协调并预告后执行;本轮未执行:
51
+ node 'C:/Users/chany/.agents/skills/dsh-restart/scripts/restart-dsh.mjs' --port 3080 --cwd 'C:/Users/chany' --apply --grace 60
52
+ ```
53
+
54
+ 影响:该宿主所有活跃回合、手机链路/传输及内存实例中断;持久会话日志保留,不能承诺回合自动继续。旧Web认证链接可能失效,用户按本机启动提示重新打开,不回传token。失败可能停机,须人工恢复。**不并行另起替代server。** 仅原进程已退出且3080无人监听时,原入口人工恢复命令:
55
+
56
+ ```powershell
57
+ Set-Location 'C:/Users/chany'
58
+ & 'D:/Program Files/nodejs/node.exe' 'C:/Users/chany/AppData/Roaming/npm/node_modules/@deepseek-ai/dsh/lib/bin.js' web
59
+ ```
60
+
61
+ 回滚:T仍为after hash时,把同次备份原字节恢复到T,核before hash,再走相同确认/协调重启流程。若T已被别人更改,不自动覆盖。仅回滚T,不回滚整个包/profile/问答改动。后续包更新可能覆盖本地hotfix,须保留此记录。
62
+
63
+ ## 最小验收与现有只读观测
64
+
65
+ **A. 装载25秒代码**:T的after hash正确;3080新PID启动晚于修补;仍用原web profile、解析同一安装根;GET状态installed=true、status.state=connected。结合固定静态导入链证明新Host装载修正版。**API没有offerTimeoutMs或源码hash字段**;不把caps/connected冒充直接计时证明,不新造诊断参数、不再跑黑洞。
66
+
67
+ **B. 手机一次原入口**:用户用现有手机,在原项目文件浏览器点原“导出/分享”;优先选择允许分享的、未缓存、非敏感且**>64KiB的小文件**,不默认重传1.315GB APK。记点按/结果时刻,操作前后立即看:
68
+
69
+ - 现成HTTP只读诊断:`GET http://127.0.0.1:3080/api/dsh-cindy-host/status`(既有loopback路由)。
70
+ - 只选installed/status.state、diagnostics中file-peer的invokeTotals/refusalTotals;recentInvokes/recentRefusals过滤同一手机来源和channel,保留at/channel/ok/code,本地对齐src、报告用别名。不取preview、SDP、凭据。FILE_PEER_TIMEOUT经cindy-channels.js:1583–1585保留。
71
+ - 只读装载检查:Get-NetTCPConnection/Get-Process、T的Get-FileHash、profile模块解析。
72
+ - `POST /api/dsh-cindy-host/selftest` **不是通用只读入口**,会执行实际channel操作;已有channel=device-link:file-peer、args=[{action:'caps'}]仅查协议上限,不能证明25秒。本验收无需主动offer/open自测。
73
+
74
+ **观测边界**:现有环不记录peer action、请求开始或耗时(host.js:1662–1701,1743起),不能伪造精确“同次offer25秒”,点按时间只辅助关联。小文件可回退OSS,**分享成功/peer计数增长/无拒绝,均不能单独证明DataChannel建立或peer传输完成**。只报告已观测阶段;没有可归因建连/完成证据就明确未证实,不拿计数猜。用户另行选择继续原APK后再验传输,建连修复与整包成功分开;新Android APK、诊断包、整包重传都不是本修复前置。
75
+
76
+ 当前只读状态:installed=true、connected,peer调用/拒绝计数字段未出现,peer拒绝环为空;这不是手机验证成功,也不能恢复旧日志。本轮生产文件/配置未动,没有重启、另开server或提取凭据。
77
+
78
+ 已按key `file-peer-minimal-apply-preparation-report-20260921-01` 回主助手一次:receipt=`bed255b0774dd46cc89536baa0f63302d15e2bf7874efa1b75ae9a678afc4509`,request=`assistant-1732294b-f08a-49e0-9f32-7b9f8450521c`,admission=accepted,仅表示入队,不表示已消费或应用成功。
@@ -0,0 +1,119 @@
1
+ # 以Cindy原功能合同为准:大文件接入只读对照
2
+
3
+ ## 结论与撤回
4
+
5
+ **原Cindy已经规定:Mobile export/share复用统一文件读取,整文件按inline→peer→OSS选择,用户不必另找“P2P下载入口”。所以Export与P2P不是互斥概念。此前以“Export不应是P2P”判定产品混淆、建议新建/改造移动下载入口及将主修归给Cindy UI,均撤回为未经原合同支撑的结论。**
6
+
7
+ 本次找到的明确接入差距反而在**DSH电脑端fallback**:原Cindy允许最高2GiB、>64MiB磁盘流式上传OSS;安装DSH Host旧Export只有512MiB且整文件读内存。1,315,101,941字节落入原客户端允许范围,却被该Host fallback预先拒绝。它不解释peer为何失败,但能解释为何兼容回退无法继续。不能把该Host限制泛化成Cindy的“>512MiB应禁止fallback”规则。
8
+
9
+ “266KB导出完成”仍只是用户确认的原功能UI结果,不单独证明peer;这与Export本来就是合法peer消费入口并不矛盾。本轮不要求任何手机操作,不宣称大APK已成功。
10
+
11
+ ## 依据与证据边界
12
+
13
+ 路径简称:
14
+ - **R**:`G:/Projects/Cindy/docs/dev-rules/remote-desktop-connectivity.md`
15
+ - **M**:`G:/Projects/Cindy/apps/mobile`
16
+ - **D**:`G:/Projects/Cindy/apps/desktop/src`
17
+ - **S**:`G:/Projects/Cindy/packages/device-link/src`
18
+ - **H**:`C:/Users/chany/.dsh/profiles/web/node_modules/dsh-cindy-host-demo/src`
19
+
20
+ 原功能依据优先级:
21
+ 1. **R:203–257**明确规范:30秒RPC及caps/offer/open/close;Mobile export/share用统一读取;≤64KiB inline,较大文件先peer再OSS,peer上限2GiB;preview/range保留OSS;缺能力/连接或传输失败可fallback,取消/账号变化不可fallback;staging与消费者副本生命周期分离。
22
+ 2. 原Cindy官方实现与测试:D/main/device-link/filePeer.ts、capture-renderer/filePeerHost.ts、main/file-browser/device-op.ts、main/device-link/mediaTransfer.ts;M/src/__tests__/fileBrowserExportStreaming.test.ts。不是用DSH代码自证原合同。
23
+ 3. 原功能提交 **0a104b5ae96b411bc4cff64bb41c101ca4dbbcc3 / #4346(2026-09-17 +10)**,标题“统一远程文件读取并按需预览HTML”;提交说明明确区分手机预览/下载并支持2GiB。相关手机UI/helper/test最近功能提交仍是它。
24
+ 4. 可得原agent历史:session `8c297eea-a03e-4f69-afcb-c9faf1a2d344` seq13原用户问题是“我们这个手机APP支不支持传输大文件功能?”;没有在此问题中要求另造UI。Cindy后续owner会话`session-22890ccd-f7f0-4aaa-ae5e-7ef318be5e42` seq1502/1519明确停止构建、未生成APK、保留13文件修复。这支持不能把工作树修正当手机已部署。历史文本只作证据,不作当前指令。
25
+ 5. 实际安装Host:0.1.10,3080 PID48208,host-file-peer.js SHA256仍`c9d44db98c630a6c0d863aef40721e7f4606b7fd9e803b5db4cc0c7ebc5484fa`(25秒补丁)。Cindy HEAD `dc294b02ca`,相关原规则/官方电脑端本轮git状态无改动;移动peerTransport/Registry及共享fileAccess有dirty修改。
26
+
27
+ 未验证边界:手机实际安装版本/JS bundle、现场ICE选路、当前OSS服务端部署/配额/网络可用性。原代码注释指向的apps/server服务文件不在此checkout;本轮不把客户端2GiB声明冒充实际云端上传成功。未读取/修改APK,未向手机发调用,未新建代理或服务器,未跑传输/构建/回归。
28
+
29
+ ## 原手机UI状态机(沿用,不判为错误设计)
30
+
31
+ ### A. 文件浏览页长按“导出 / 分享”
32
+
33
+ - 菜单绑定shareItem:M/app/files/[sessionId].tsx:935–944、1397–1404;中文词:M/src/i18n/locales/zh-CN/files.json:12、16。
34
+ - shareItem:465–503先关菜单,再showNotice“正在从电脑导出…”,非图片取件后复制/下载本地临时文件,再expo-sharing。
35
+ - **notice是2500ms自动消失的提示,不是下载完成状态**(同文件:116、177–187)。后台Promise可继续;所以“提示消失”本身既不是错误,也不是成功。成功分支清notice并进入分享,失败显示formatRemoteError。用户现场是否恰由该计时器引起仍取决于安装版本。
36
+
37
+ ### B. 文件预览页“下载原文件/分享”
38
+
39
+ - M/app/files/preview/[sessionId].tsx:432–449:busyLabel防重复,开始显示exporting;调用exportToUrl(...,**false**);本地就绪后系统分享,finally清busyLabel。598–599优先显示busyLabel而非短notice。
40
+ - 普通预览默认stream=true(371–383);显式下载传false。测试M/src/__tests__/fileBrowserExportStreaming.test.ts:8–35验证同一个exportRemoteFileToUrl:**stream=false得到本地peer URI,stream=true得到OSS URL**,缓存互不串用。
41
+ - 手机原终点确实是本地临时副本+系统分享(M/src/session/remoteMediaDiskCacheExpo.ts:21–66),不是本次DSH接入新造的流程。原规则R:255–257允许短期staging由原cache/preview消费者复制持有;没有证据要求为接入另造Android Downloads任务或自动安装APK。
42
+
43
+ 因此通用“电脑导出”文案不是transport判别器,也不能证明“走错流程”;“导出完成”同样不带传输来源。无需先改原有文案才能解释适配。
44
+
45
+ ## 可按代码逐段验证的原交互流程图
46
+
47
+ ```text
48
+ [原文件浏览/预览入口选择已有APK]
49
+ ├─普通预览:按预览政策,不等同整文件下载
50
+ └─原导出/分享 或 下载原文件(整文件stream=false)
51
+ ↓ 原notice/busyLabel,非transport状态
52
+ exportRemoteFileToUrl
53
+ ├─按账号/设备/路径/mtime/读取意图命中缓存 → 既有结果
54
+ └─maker.fileBrowser.readBytes
55
+ ↓ 手机→所选PC:remote-op caps
56
+ ├─无fileRead → 旧两段式Export/OSS
57
+ └─有fileRead
58
+ ↓ 手机→PC:fileUrl(workdir,relPath)
59
+ ↓ 手机→PC:media:fetch(prepareOnly=true)
60
+ ├─≤64KiB inline(包括空文件)→ 手机物化
61
+ └─transferRequired元数据
62
+ ├─保留流式预览URL的策略 → OSS(先决定,不先收peer字节)
63
+ └─整文件≤2GiB:尝试可复用peer
64
+ ↓ 手机创建offer→DeviceLink RPC→PC
65
+ ↓ 原PC Main转本地accept命令→独立Chromium文件renderer
66
+ ↓ PC answer经RPC返回→手机应用answer并等DC open
67
+ ↓ 手机open(connection,url)→PC授权/只读原文件→ticket,size,mime
68
+ ↓ 手机credit/offset→PC 16KiB binary块→手机原生写盘
69
+ ↓ 精确长度/偏移/源stat检查+空binary EOF
70
+ ├─成功→手机短期peer文件→消费者临时副本
71
+ ├─缺能力/连接/传输失败→原OSS fallback
72
+ └─明确取消/账号失效→终止,不fallback
73
+
74
+ 原工作目录OSS fallback:
75
+ 手机exportFileStart→PC立刻回transferId,后台上传
76
+ PC原文件:≤64MiB缓冲、较大文件磁盘流式PUT,最大2GiB
77
+ 手机轮询exportFileStatus→done/key→presignGet→手机HTTP下载
78
+
79
+ 本地文件就绪→原Android系统分享/后续用户选择
80
+ (传输来源、文件完整性与分享UI结果是不同证据层)
81
+ ```
82
+
83
+ 代码锚点:M/src/session/fileBrowserExport.ts:25–77;M/src/device-link/mobileMakerTransport.ts:874–910、1198–1225;S/fileAccess.ts;D/main/file-browser/device-op.ts:400–405、551–559、633–705。
84
+
85
+ ### 角色、accept与直连
86
+
87
+ - 此场景手机是请求方/接收方,PC是文件供数方;不是“手机上传APK”。DeviceLink承载控制/SDP,不承载整APK。
88
+ - 原PC `requestFilePeer`把wire offer转成本机**accept**(D/main/device-link/filePeer.ts:159–172);capture-renderer/filePeerHost.ts:10–21调用共享runtime.accept。accept不是另一个对外文件RPC。
89
+ - DSH无Electron文件renderer,H/host-file-peer.js的werift offer处理器直接承担同一应答职责;外部仍caps/offer/open/close。open是PC读取原盘,不是PC先导出到OSS。
90
+ - R:205–210明确ICE可直连或TURN;peer不等于保证物理无中继。TURN承载RTC与OSS存储回退是不同分支。
91
+ - 原RPC30秒预算不等于整文件总时长;R:245–257规定16KiB/credit16、60秒空闲及所有权检查。
92
+
93
+ ## DSH适配逐项对照
94
+
95
+ | 项 | 原Cindy合同/实现 | 已安装DSH表现 | 分类 |
96
+ |---|---|---|---|
97
+ | 原手机入口、UI文案、分享终点 | 原Export/share消费统一读取 | Host不改手机UI,当前入口仍可消费它 | **保持;不能据文案判偏离** |
98
+ | caps.fileRead / fileUrl | D/device-op:400–405、551–559 | H/cindy-channels.js:1516–1521、host-file-reference.js | **核心保持**;原回size/mtime及URL maxBytes快照,DSH回ok/url,字段/快照差异待逐消费者确认 |
99
+ | prepareOnly/inline | 原mediaFetch:410–438,≤64KiB inline否则metadata | H/host-media-fetch.js:438–455同形 | **保持**(静态) |
100
+ | 信令/角色/2GiB | 原caps/offer/open/close,PC答复并供数 | H/file-peer-protocol.js及host-file-peer.js匹配 | **协议形状保持**,非现场互通成功证明 |
101
+ | PC RTC后端 | 原Chromium共享runtime.accept | DSH werift@0.24.4直接答复 | **实现替换**;ICE/DTLS/Android互通仍未知,不靠原文案判断 |
102
+ | 块/EOF/所有权/空闲 | 原16KiB、credit16、源stat、60s idle、每连接授权 | H/host-file-peer.js相应检查;既有空EOF兼容处理 | **静态保持**;未重新做Android实测 |
103
+ | 工作目录OSS fallback | 原exportFileStart→uploadLocalFile;2GiB、>64MiB磁盘流式,进度回调 | H/host.js:208、285–286 **512MiB预拒**;305–306 **整文件readFile再上传**;uploaded仅开始/结束 | **明确偏离/能力收窄**。本1.315GB会在该分支被拒,不是原Cindy512MiB限制 |
104
+ | 普通media:fetch OSS | 原mediaFetch最终复用2GiB uploadLocalFile(按调用者maxBytes再限) | H/host-media-fetch.js:46 **25MiB**,459–466整体读取 | **既有收窄差异**;不同于本workdir Export的512MiB,不混成同一阈值 |
105
+ | root安全 | 原fileUrl带baseDir/maxBytes并按来源授权 | DSH要求root、realpath并拒敏感路径 | **有意安全收紧**,不能为兼容而盲放开 |
106
+ | peer失败→fallback | 原R:233–240及HEAD helper明确允许(取消除外) | 安装Host提供旧fallback,但容量不足;手机dirty补丁另加>512MiB不回退 | **Host能力差异已证;dirty移动策略不是原合同,也未证已部署** |
107
+ | 真实手机成功 | 必须有相应操作/完整文件及transport证据 | 已知266KB UI完成;历史peer RPC及OVERSIZE未关联完整文件 | **未知/未验收**。不能推出1.3GB成功或特定peer根因 |
108
+
109
+ **关键大小证据**:D/main/device-link/mediaTransfer.ts:43–50定义64MiB分流、2GiB对象上限;384–405核大小;439–508大文件createReadStream/Transform/流式PUT。D/device-op.ts:633–667只有调用者显式maxBytes约束,再调用该上传器。DSH 512MiB是为了现整文件内存实现加的保护(H/host.js:204–208),不能简单删常量再把1.3GB读入内存。该差异不是25秒补丁新引入。
110
+
111
+ ## 旧结论校正与最小正确后续动作
112
+
113
+ 1. **撤回UI重构为前置、Export不应使用peer、必须独立P2P下载产品入口的判断。** 原文件功能已明确允许此入口自动选择传输,不应由适配研究重定义。此前报告仅可保留其低层代码路径事实,其产品/owner推断以本报告取代。
114
+ 2. **将确定修复归回DSH Host原适配owner**:下一项应是按原接口补齐大文件fallback的有界内存流式实现/2GiB范围及job语义,而不是盲升512MiB常量或改用户手机入口。本轮只提出最小修复对象,不改代码、不重启。普通media fallback的收窄作为相邻条目明确记录,避免与workdir链路混改。
115
+ 3. Cindy现有owner保留现有未部署修改,不由本会话回滚;但应重新审视“通用>512MiB禁fallback”假设——它会把Host特有限制扩散给原Cindy电脑端,偏离R:233的失败回退规则。安全错误归因可独立保留,不能擅自改变原兼容政策。
116
+ 4. **peer真实互通仍是独立未知项**:此前8.473秒节奏仅是手机answer/DC-open候选;DSH原15秒offer缺陷已修25秒,不能由此宣称Android建连成功或全部Host问题解决。继续由Host原owner对照原accept路径/既有证据查适配差异,不拿UI改名、重启或要求手机重测代替。
117
+ 5. 原功能业务验收允许按合同fallback完成文件取回;peer专项验收需要peer证据;小文件UI完成不是大APK能力验收。三者分开,不要求用户继续操作。
118
+
119
+ 本轮交付只是原合同研究、差异表及下一修复对象。生产Host/DSH/Android/Cindy代码均未修改,无APK读写/传输、构建、手机调用或测试。
@@ -0,0 +1,75 @@
1
+ # 手机Export:用户确认266KB导出UI完成,peer传输仍未归因
2
+
3
+ > 以原功能合同为准:Export/share本来就是Cindy统一文件读取(含peer)的合法入口,不应据名称判其错误。本文仅保留UI/通道观察,UI完成不能单独判传输方式;当前不再要求用户测试。此前产品改造推断已撤回,最新对照见 `file-peer-original-contract-alignment-20260921.md`。
4
+
5
+ ## 采样范围与来源
6
+
7
+ 仅三次 `GET http://127.0.0.1:3080/api/dsh-cindy-host/status`:**17:32:52.818、17:35:14.278、17:36:15.392(2026-09-21,+08)**,之后停止采样。初采3080为PID48208,Host connected。未触发任何手机操作、selftest、重启或APK传输,未写生产。
8
+
9
+ 窗口中全部保留的文件/peer事件来自同一 **Android-A**:原始src直接相等,SHA256(src)前12位=`4A2EA4FF62A9`。它与status.devices中**唯一在线Android controller**的deviceId精确匹配,匹配行数1;不输出原始设备ID/名称。第二次临时时间戳筛选未命中导致的sameSource=false不采用;已改用首快照原始src直接关联,第三次逐条均匹配。
10
+
11
+ ## 实际事件(完成时间换算+08;同一Android-A)
12
+
13
+ | 时间 | 通道 | Host结果 | 回复字节数 |
14
+ |---|---|---|---:|
15
+ | 17:32:39.166 | file-browser:remote-op | ok=true | 258 |
16
+ | 17:32:40.454 | file-browser:remote-op | ok=true | 178 |
17
+ | 17:32:40.517 | file-browser:remote-op | ok=true | 301 |
18
+ | 17:32:40.577 | device-link:media:fetch | ok=true | 244 |
19
+ | 17:32:40.636 | device-link:file-peer | ok=true | 186 |
20
+ | 17:32:41.195 | device-link:file-peer | ok=true | 1315 |
21
+ | 17:32:49.668 | device-link:file-peer | ok=true | 162 |
22
+ | **17:32:49.725** | **file-browser:remote-op** | **ok=false, code=OVERSIZE** | 249 |
23
+
24
+ OVERSIZE同时出现在成功/失败混合调用环和拒绝环,已去重为**一条拒绝**,不是两次。全部ask=null,detail为空;没有action、文件名/大小、请求起始时刻或手机原始异常。上述bytes是JSON回复大小,不是下载文件大小。
25
+
26
+ 三次快照的累计数完全不变:file-peer调用**6**、拒绝条目缺省(无已记录peer拒绝);media:fetch调用**2**;file-browser调用**20**、拒绝**1**。handlerErrors=0、phoneDiagnostics=0。没有记录到Host file-peer的FILE_PEER_TIMEOUT。初采调用尾环仅20条,只保留其中3条peer成功记录,另外3次的完整时序/来源不能从尾环恢复;6次通道调用不等于6次导出。
27
+
28
+ **不存在独立offer/accept/open/close/timeout计数接口**;不能将通道计数改名成这些动作计数。17:24前次基线无peer条目;本次有真实Android peer请求,不再是“手机完全没走到Host”的证据空白。
29
+
30
+ ## 事实与最小候选分开
31
+
32
+ **确定事实**:同一Android控制端收到多条成功peer RPC回复,之后文件浏览器被Host以OVERSIZE拒绝。UI“正在导出”消失不能据此判成功,更不能宣称DataChannel建立或收到文件。日志没有文件身份,无法把这条OVERSIZE直接绑定到用户口述的小文件,也不能擅自说用户操作的是1.315GB APK。
33
+
34
+ **有代码锚点的候选**:1315字节成功回复 → **8.473秒**后162字节成功回复 → **57毫秒**后OVERSIZE。大小/顺序与caps→offer答复→失败清理close→旧导出fallback吻合,但action未记录,因此仍是推断:
35
+ - 当前Cindy源码 `packages/device-link/src/filePeerRuntime.ts:134–160`:应用answer后等DataChannel open,**8000ms**截止。
36
+ - `apps/mobile/src/device-link/peerFileTransport.tsx:198–218`:Host offer返回后才执行本地command(answer),完成后才Host open;273–279失败清理会发Host close。
37
+ - 安装Host `host-file-peer.js:246–252`:caps返回version/maxBytes、offer返回answer、close返回ok:true,三种回复形状与本窗口大小相容,但不能替代动作日志。
38
+ - 安装Host `host.js:285–286`:旧文件导出路径存在512MiB OVERSIZE guard。若本条确为该fallback拒绝,则需核所选文件身份/大小;本采样没读取文件内容或路径。
39
+
40
+ 这将下一定位点收窄为**手机answer/ICE/DataChannel-open阶段或其失败清理**,而非直接判定Host25秒offer截止又失败。未获得手机实际版本/原始异常,不能将8.473秒的回复间隔等同于已证实的手机8000ms超时,也不能盲加超时。
41
+
42
+ ## 下一步(不要求重新点击)
43
+
44
+ 1. 主助手将本时序与用户已发生操作的时间、已有文件名/显示大小对齐;如需核对,只收已有描述/截图,不重试、不重传。明确保留“小文件”与OVERSIZE尚未关联的矛盾。
45
+ 2. 将8秒answer/open候选交Cindy现有改动所有者核对;优先查手机**现有安装/已有**安全错误或WebView/ICE状态记录是否能还原该窗口,不把新Android包设成前置。本Host现有API不能补出action/ICE状态/手机异常;不可伪造。
46
+ 3. Host25秒修复的静态装载结论保持;当时尚不能通过peer专项验收。这不否定用户随后补充的UI完成结果,最终分项判定见下。未改生产、未触发手机、未要求用户重复点击。
47
+
48
+ ## 用户确认266KB完成后的最终只读归因(17:41–17:42)
49
+
50
+ 用户补充:原Export的**266KB小文件已显示“导出完成”**。记录为用户确认的UI结果;手机确切显示时刻未提供,下面时间是Host采样时刻,不能混同。
51
+
52
+ 本轮仅两次既有GET,均为2026-09-21 +08:
53
+
54
+ | 采样时间 | peer calls/refusals | media calls | browser calls/refusals |
55
+ |---|---|---|---|
56
+ | 17:41:18.118 | 6 / 无拒绝条目 | 2 | 20 / 1 |
57
+ | 17:42:39.572 | 6 / 无拒绝条目 | 2 | 20 / 1 |
58
+
59
+ - 与17:36最后基线相比,以上计数**全部增量0**。PID仍48208,Host状态connected;这只是DeviceLink Host在线,不是peer.connected。
60
+ - 当前文件相关保留事件仅剩**17:32:49.725**的file-browser:remote-op、ok=false、code=OVERSIZE、ask=null、bytes=249,来源指纹仍 **4A2EA4FF62A9 / Android-A**,状态目录仍匹配android。此前peer成功事件已离开20条调用尾环,但已保存在本文前表;不能因现在看不到它们就否定曾有调用。
61
+ - handlerErrors从0变1,安全字段为**17:38:25.517,where=invoke:maker:list-active,timeout类别**;不属于peer/file-browser/media通道,不将其归因为本次文件失败。未输出原始message/stack。phoneDiagnostics仍0。
62
+ - 完成窗口没有新增同手机peer调用或已记录peer拒绝/FILE_PEER_TIMEOUT;接口本就没有独立offer/accept/connected/open/close/transfer-complete计数。
63
+ - **没有明确fallback字段或OSS标记**;不能从media调用2、旧OVERSIZE、UI完成或计数不变断言266KB走了OSS,也不能据此断言走了peer。现记录不含文件名/大小/动作,旧OVERSIZE仍不能强行绑定266KB文件。
64
+
65
+ ### 可决定任务状态的结论
66
+
67
+ | 子项 | 判定 | 证据层级 |
68
+ |---|---|---|
69
+ | Host25秒补丁落盘及重启后静态装载 | 已完成 | 前次PID48208、after hash及静态链核验 |
70
+ | 用户执行原Export,266KB显示导出完成 | **可完成此UI/用户动作子项** | 用户明确报告;不等同字节完整性校验 |
71
+ | 同一266KB文件经peer建立并完整接收 | **未证实,不能完成peer专项验收** | 有历史Android peer RPC,但无可归因connected/transfer-complete证据 |
72
+ | 266KB明确OSS fallback | **未证实** | 无直接fallback/ossKey标记,不靠时序猜测 |
73
+ | 原1.315GB APK传输成功 | **未验收** | 本轮未触发或传输 |
74
+
75
+ 下一步:主助手可勾掉“用户尚未进行手机操作/尚未见UI完成”,不要再让用户重复点击;保留**技术侧peer归因**为待办,由Cindy现有所有者检查同一操作已存在的安全阶段/完成记录,尤其此前8秒answer/DC-open候选。没有可关联记录就保持“未证实”,不要改称“peer成功”或“确定fallback”,也不以新Android包/重传APK为前提。本轮仅GET和已有快照分类,未写生产、未重启、未触发任何手机动作。
@@ -0,0 +1,106 @@
1
+ # (产品推断已撤回)Export、手机P2P下载与大APK
2
+
3
+ > **此报告的产品判断、UI改造建议和主修owner判断已撤回,不得据此实施。** 原Cindy规则明确Mobile export/share消费统一inline→peer→OSS策略;Export并不排斥P2P。且原OSS上限为2GiB流式上传,512MiB是DSH适配限制。以 `file-peer-original-contract-alignment-20260921.md` 的原功能合同、保持/偏离/未知对照及owner为准;下文仅保留历史取证过程。
4
+
5
+ ## 历史审查结论(已撤回为结论;参见上方纠正)
6
+
7
+ **当前是“手机取回电脑文件→临时文件→系统分享”的混合传输入口:底层可用peer,也可用旧电脑导出/OSS;上层统一叫导出。不是方向接反,也不是保证P2P、保证保存到手机的独立下载入口。** 用户指出的产品语义问题成立。此前让用户用原Export小文件验证P2P的建议撤回;266KB“导出完成”仅保留为用户观察,不计入大APK或peer验收,不再要求手机测试。
8
+
9
+ 但也必须纠正另一个可能推论:**出现“正在从电脑导出…”不证明调用了PC旧Export任务**,因为文案在传输选择之前就显示;真正的peer分支不调用exportFileStart。仅改文案也不能修好未证实的peer链路。
10
+
11
+ ## 范围、依据与版本界限
12
+
13
+ 用户目标明确:手机通过P2P下载电脑已有的大APK,不以“电脑导出/分享完成”代替。此处只复核已有实现,不替用户决定保存目录、自动安装或小文件是否允许云回退,不修改实现。由原实现者本人定向取证,未委派新代理。
14
+
15
+ 路径简称(以下锚点均已实际读取):
16
+ - **M** = `G:/Projects/Cindy/apps/mobile`
17
+ - **S** = `G:/Projects/Cindy/packages/device-link/src`
18
+ - **H** = `C:/Users/chany/.dsh/profiles/web/node_modules/dsh-cindy-host-demo/src`(实际安装Host,而非dirty工作树)
19
+
20
+ Cindy HEAD为`dc294b02ca`;本次只读git状态/diff确认 `peerFileRegistry.ts`、`peerFileTransport.tsx`、`S/fileAccess.ts`仍有未提交改动。尤其“大文件不再吞掉peer错误后回退OSS”的修正是工作树差异,**未验证用户手机装载这些代码**。未读/改APK。GitNexus注册表没有本Cindy checkout(另一个Cindy-markdown-preview不能替代),故以已知路径和受限搜索取证,未刷新索引。
21
+
22
+ ## 1. Android文案与入口:发生在传输选择之前
23
+
24
+ | 证据 | 代码实际行为 |
25
+ |---|---|
26
+ | M/src/i18n/locales/zh-CN/files.json:12,16 | 菜单“导出 / 分享”;进度“正在从电脑导出…” |
27
+ | M/app/files/[sessionId].tsx:935–944、1397–1404 | 菜单share绑定shareItem,不是独立P2P下载按钮 |
28
+ | 同文件:465–503 | **469行先显示通用exporting**;非图片再调用exportRemoteFileToUrl;494–497复制/下载到手机临时文件,调用expo-sharing系统分享单 |
29
+ | M/app/files/preview/[sessionId].tsx:432–449 | 同样先显示exporting,436行调用exportToUrl(..., **false**),随后同一个临时文件+shareAsync;finally清除busyLabel |
30
+ | 同文件:371–383、506、544 | 默认预览stream=true;显式下载/分享传false。“下载原文件”最终也是downloadAndShare,不是独立持久下载任务 |
31
+
32
+ 当前这两个handler没有“peer已连接”状态输入;进度不区分连接、收包、云上传、云下载、系统分享。当前files.json和这两个成功分支也没有自行弹“导出完成”的对应完成文案(浏览页清notice,预览页清busy)。用户看到的完成提示是实际观察,但不能仅凭当前源码认定它来自哪个安装版本或系统分享组件,更不能认作peer EOF。
33
+
34
+ ## 2. 调用合同与请求方向
35
+
36
+ 非图片APK、未命中缓存、整文件路径:
37
+
38
+ ```text
39
+ 手机shareItem / downloadAndShare
40
+ → exportRemoteFileToUrl(名字含export,但本身不等于PC Export任务)
41
+ → maker.fileBrowser.readBytes(..., stream=false)
42
+ → 手机向所选PC发送 file-browser:remote-op {op:caps}
43
+ → 支持fileRead时请求 {op:fileUrl, workdir, relPath}
44
+ → 手机请求PC device-link:media:fetch {url, prepareOnly:true}
45
+ → >64KiB返回transferRequired元数据,并未上传OSS或传输APK
46
+ → 统一读取策略选择peer;成功返回手机本地文件;不适用/失败可能走兼容fallback
47
+ → 手机临时副本 → 系统分享单
48
+ ```
49
+
50
+ 锚点:M/src/session/fileBrowserExport.ts:25–77;M/src/device-link/mobileMakerTransport.ts:862–870、874–910、1198–1225;H/host-media-fetch.js:438–455。
51
+
52
+ - 调用目标是`invoke(deviceId, channel,args)`的所选**电脑**;Android是请求方/文件接收方,PC是供数方。
53
+ - helper先按账号/设备/路径/mtime及stream模式查URL缓存(fileBrowserExport:31–34);命中可不再发peer调用。
54
+ - peer成功由`peerMediaUri(result)`得到手机file://;小文件也可能inline;OSS结果才调用presignGet(同文件:52–76)。不能仅从helper函数名判断传输。
55
+ - ≤64KiB可直接inline;旧Host可能忽略prepareOnly并返回普通OSS/inline。`S/fileAccess.ts:45–49`对transferRequired不为true直接返回,并非每次都建peer。
56
+
57
+ ## 3. peer供数路径:PC读原文件,不经过旧Export上传任务
58
+
59
+ 1. 手机WebView创建ordered `files-v1` DataChannel及SDP offer(S/filePeerRuntime.ts:73–82)。
60
+ 2. 手机经现有DeviceLink控制链路向PC发 `device-link:file-peer {action:offer,sdp}`(M/src/device-link/peerFileTransport.tsx:172–213)。
61
+ 3. H/host-file-peer.js:179–212在PC创建werift连接,接收offer、创建answer、返回`{connection,sdp}`;25秒只管此offer处理预算。**返回answer/内部ready=true不是DataChannel连接成功。**
62
+ 4. 手机应用answer并等待DataChannel open,当前共享运行时截止8000ms(S/filePeerRuntime.ts:134–160);成功后才向PC发`{action:open,connection,url}`(peerFileTransport:213–218)。
63
+ 5. PC解析文件引用、校验root/realpath/常规文件/上限;只读打开**电脑原始文件**并返回`{ticket,size,mimeType}`。H/host.js:223–224、411–416;H/host-media-fetch.js:296–346;H/host-file-peer.js:214–234。上限2GiB,不是旧Export的512MiB。
64
+ 6. 手机在DataChannel发`{ticket,offset,credit:16}`;PC每块最多16KiB从FileHandle.read读取并发binary,检查size/mtime等一致性,末尾必须空二进制EOF(H/host-file-peer.js:104–146;H/file-peer-protocol.js:2–7)。
65
+ 7. 手机WebView收块,经本机bridge写原生FileHandle;接收完成还检查offset=size后注册本地URI(S/filePeerRuntime.ts:163–229;peerFileTransport:221–258、350–370)。网络字节方向是**PC→手机**;WebView→原生的base64是手机内部桥接,不是把整APK塞进DeviceLink RPC。
66
+
67
+ ### offer / accept / connected不要混名
68
+
69
+ - Host文件协议仅caps/offer/open/close(H/host-file-peer.js:10–17、243–252),**没有独立accept RPC**。PC的应答动作实现在offer处理器内。
70
+ - 共享S/filePeerRuntime.ts:84–133另有`accept()`,是通用RTC应答方/供数模式;此次手机取PC文件调用的是offer→answer→receive,不调用它。DSH Host用werift做对应应答。
71
+ - DeviceLink openLink、控制端授权接受、Host.status.connected都属于控制链路;不能当成文件DataChannel.connected。
72
+ - WebRTC可经ICE/TURN中继;“使用peer通道”也不能在没有selected candidate证据时宣称物理网络一定无中继。它与把文件上传OSS是不同机制。
73
+
74
+ ## 4. 只有fallback才进入真正的旧电脑Export任务
75
+
76
+ **确定的旧路径**:`S/fileAccess.ts:85–121 exportDeviceFile`发exportFileStart并轮询exportFileStatus(最多30分钟,700ms轮询)。H/host.js:296–330创建job,**305行将完整文件readFile进内存、306行uploadMediaForExport上传OSS**,返回key;手机再presignGet→HTTP下载。不是打包/重建APK。512MiB guard在H/host.js:208、255–286,1,315,101,941字节不能走此旧路径。
77
+
78
+ 当前选择条件:
79
+ - PC `caps.fileRead`缺失:mobileMakerTransport:1209–1216直接走旧exportDeviceFile,根本不试peer。
80
+ - 已支持fileRead:prepare未要求传输则直接返回inline/OSS;否则按`S/fileAccess.ts`策略。
81
+ - stream=true且peer结果短时或音视频:为保留预览URL走fallback(48–49);**显式非图片整文件下载传false,所以不是APK必须走stream预览的理由**。
82
+ - peer provider未注册/不可用、caps.version不是1、超过2GiB等可能返回null(peerFileRegistry:87–93;peerFileTransport:179–184;mobileMakerTransport:905–909)。
83
+ - **HEAD基线**:手机peerFileTransport catch取消以外的异常后`return null`;共享fileAccess遇null直接fallback。ICE失败、空间不足等因此可被旧导出OVERSIZE掩盖(本次git diff精确显示这些被修改的旧行)。
84
+ - **尚在dirty工作树的修正**:peerFileTransport改为抛安全stage错误;fileAccess:54–67对>512MiB的peer失败/不可用保留错误,不再退到肯定超限的旧Export;≤512MiB仍可fallback。不能把该未核实手机部署的修正描述成已运行事实。
85
+
86
+ 因此“小文件导出完成”完全可能是兼容取件成功,也可能peer或缓存成功;**本轮不判定实际用了哪种,更不把它归为peer验收**。
87
+
88
+ ## 5. 历史改造建议与owner(已撤回,不得作为修复合同)
89
+
90
+ | 已确认缺口 | 最小修正方向(建议,未实施) | owner |
91
+ |---|---|---|
92
+ | 高:传输尚未选择就显示电脑导出;下载/分享/云回退共用文案,无法表达用户要的手机P2P下载 | 用明确“下载到手机”的产品动作及真实阶段;分享应是文件就绪后的后续动作,不用系统分享单完成代表下载传输完成 | **Cindy移动文件UI/下载功能owner**;M/app/files两入口及files.json |
93
+ | 高:旧策略把peer失败吞成null后进入必定拒绝1.3GB的PC Export,掩盖原始错误 | 在共享读取层收口完整文件与fallback合同;该APK不得隐式调exportFileStart。已有>512MiB错误保留修正可复用,但还须核部署,不盲补第二套传输 | **Cindy共享读取/移动peer owner**;S/fileAccess、mobileMakerTransport、peerFileTransport/Registry |
94
+ | 高:没有向业务/观测明确交付本次实际transport、连接/收包/EOF/落盘结果;拿通道RPC成功作验收会误判 | 从现有peer状态机和原生接收完成派生安全的阶段、已接收字节、结果来源;peer成功只在DC open且完整EOF/字节数/本地交付后成立,不由UI文案推断 | **Cindy peer接收及统一结果合同owner**,与Host原实现者对齐协议 |
95
+ | 中:当前终点是cache/remote-media-share临时副本+shareAsync,不是明确的下载保存终点 | 复用现有接收管线,明确手机保存/后续打开的产品终点;不要用“分享单关闭”代表大APK下载。具体保存位置/安装动作不是本轮擅定事项 | **Cindy移动下载/文件生命周期owner**;remoteMediaDiskCacheExpo及UI |
96
+
97
+ 正确归属首先是**Cindy移动下载+共享读取合同的现有负责人**(协调记录中的session-22890ccd-f7f0-4aaa-ae5e-7ef318be5e42正在改相关三文件;主助手统一协调),不是继续让用户点Export,也不是再单独重启/改Host25秒。DSH Host原实现者仍负责PC只读供数、offer答复和真实ICE/DTLS/块协议缺陷的定向配合,不能因数据流方向正确就宣称Host网络兼容性无缺陷。
98
+
99
+ **复用而非重造**:已有readBytes(stream=false)、peer源/接收器和OSS兼容分支应保留为单一读取实现;在共同合同中表达实际传输/回退政策,在UI中表达下载与分享的区别。不要为纠正文案再复制一套Host Export API或第二套RTC管线。对仍允许的小文件云兼容回退,要诚实区分,不能冒称P2P;是否自动/明确选择属产品决策,未实施。
100
+
101
+ ## 风险与未知 / 建议下一步
102
+
103
+ - 手机安装版本和新SDK修正是否部署未知;当前工作树不是用户手机的运行证明。
104
+ - 已保存的8.473秒时序只支持answer/DC-open失败候选,不证明具体ICE根因;本轮未做网络、APK或手机实验。
105
+ - 当前两入口没有下载阶段合同、旧日志无action/文件身份,因此无法从266KB完成或旧OVERSIZE逆推该次APK完整路线。
106
+ - **建议主助手交现有Cindy owner先形成上述最小合同修订和部署边界说明,Host原实现者只对齐供数/信令接口。停止手机测试和Export式peer验收。本轮结论是已完成只读追踪,不是已修复或已验收P2P下载大APK。**