@marktrue/dsh-mobile-remote 3.2.1-alpha.1 → 3.2.1-alpha.2-app

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 (349) hide show
  1. package/DSH-Remote-v3.2.1.apk +4 -0
  2. package/README.md +6 -253
  3. package/manifest.json +21 -0
  4. package/package.json +10 -65
  5. package/CHANGELOG.md +0 -796
  6. package/CONTRIBUTING.md +0 -59
  7. package/FAQ.md +0 -111
  8. package/cordis.patch.yml +0 -14
  9. package/docs/00-/345/274/200/345/217/221/346/200/273/347/272/262.md +0 -122
  10. package/docs/01-PRD.md +0 -148
  11. package/docs/02-architecture.md +0 -249
  12. package/docs/03-api.md +0 -816
  13. package/docs/04-security.md +0 -96
  14. package/docs/05-test-cases.md +0 -326
  15. package/docs/06-install-run.md +0 -308
  16. package/docs/07-user-manual.md +0 -65
  17. package/docs/08-/346/211/251/345/261/225/345/274/200/345/217/221/346/214/207/345/215/227.md +0 -86
  18. package/docs/09-compatibility.md +0 -195
  19. package/docs/10-release.md +0 -927
  20. package/docs/screenshots/01-home.png +0 -0
  21. package/docs/screenshots/02-notifications.png +0 -0
  22. package/docs/screenshots/03-sessions.png +0 -0
  23. package/docs/screenshots/04-chat.png +0 -0
  24. package/docs/screenshots/05-settings.png +0 -0
  25. package/docs/screenshots/06-new-session.png +0 -0
  26. package/docs/screenshots/pgy-appstore.jpg +0 -0
  27. package/docs/screenshots/pgy-mobile-app.jpg +0 -0
  28. package/docs/screenshots/pgy-pc-client.jpg +0 -0
  29. package/lib/account-usage.js +0 -145
  30. package/lib/app-alignment.js +0 -112
  31. package/lib/app-carrier-store.js +0 -1972
  32. package/lib/app-carrier.js +0 -602
  33. package/lib/client.js +0 -706
  34. package/lib/desktop-form.js +0 -738
  35. package/lib/endpoint-registry.js +0 -162
  36. package/lib/git-read-bundled-provider.js +0 -255
  37. package/lib/git-read-service.js +0 -322
  38. package/lib/index.js +0 -9292
  39. package/lib/plugin-content-digest.js +0 -80
  40. package/lib/plugin-self-update.js +0 -758
  41. package/lib/plugin-update-controller.js +0 -95
  42. package/lib/plugin-update-transaction.js +0 -322
  43. package/lib/plugin-update.js +0 -309
  44. package/lib/runtime-dependency-snapshot.js +0 -345
  45. package/lib/session-title-cache.js +0 -123
  46. package/lib/session-title-refresh.js +0 -261
  47. package/lib/update-fetch.js +0 -24
  48. package/lib/update-lock-arbiter.js +0 -158
  49. package/lib/update-supervisor.js +0 -312
  50. package/node_modules/ansi-regex/index.d.ts +0 -37
  51. package/node_modules/ansi-regex/index.js +0 -10
  52. package/node_modules/ansi-regex/license +0 -9
  53. package/node_modules/ansi-regex/package.json +0 -55
  54. package/node_modules/ansi-regex/readme.md +0 -78
  55. package/node_modules/ansi-styles/index.d.ts +0 -345
  56. package/node_modules/ansi-styles/index.js +0 -163
  57. package/node_modules/ansi-styles/license +0 -9
  58. package/node_modules/ansi-styles/package.json +0 -56
  59. package/node_modules/ansi-styles/readme.md +0 -152
  60. package/node_modules/camelcase/index.d.ts +0 -63
  61. package/node_modules/camelcase/index.js +0 -76
  62. package/node_modules/camelcase/license +0 -9
  63. package/node_modules/camelcase/package.json +0 -43
  64. package/node_modules/camelcase/readme.md +0 -99
  65. package/node_modules/cliui/CHANGELOG.md +0 -76
  66. package/node_modules/cliui/LICENSE.txt +0 -14
  67. package/node_modules/cliui/README.md +0 -115
  68. package/node_modules/cliui/index.js +0 -354
  69. package/node_modules/cliui/package.json +0 -65
  70. package/node_modules/color-convert/CHANGELOG.md +0 -54
  71. package/node_modules/color-convert/LICENSE +0 -21
  72. package/node_modules/color-convert/README.md +0 -68
  73. package/node_modules/color-convert/conversions.js +0 -839
  74. package/node_modules/color-convert/index.js +0 -81
  75. package/node_modules/color-convert/package.json +0 -48
  76. package/node_modules/color-convert/route.js +0 -97
  77. package/node_modules/color-name/LICENSE +0 -8
  78. package/node_modules/color-name/README.md +0 -11
  79. package/node_modules/color-name/index.js +0 -152
  80. package/node_modules/color-name/package.json +0 -28
  81. package/node_modules/decamelize/index.js +0 -13
  82. package/node_modules/decamelize/license +0 -21
  83. package/node_modules/decamelize/package.json +0 -38
  84. package/node_modules/decamelize/readme.md +0 -48
  85. package/node_modules/dijkstrajs/.travis.yml +0 -4
  86. package/node_modules/dijkstrajs/CONTRIBUTING.md +0 -8
  87. package/node_modules/dijkstrajs/LICENSE.md +0 -19
  88. package/node_modules/dijkstrajs/README.md +0 -18
  89. package/node_modules/dijkstrajs/dijkstra.js +0 -165
  90. package/node_modules/dijkstrajs/package.json +0 -60
  91. package/node_modules/dijkstrajs/test/dijkstra.test.js +0 -96
  92. package/node_modules/emoji-regex/LICENSE-MIT.txt +0 -20
  93. package/node_modules/emoji-regex/README.md +0 -73
  94. package/node_modules/emoji-regex/es2015/index.js +0 -6
  95. package/node_modules/emoji-regex/es2015/text.js +0 -6
  96. package/node_modules/emoji-regex/index.d.ts +0 -23
  97. package/node_modules/emoji-regex/index.js +0 -6
  98. package/node_modules/emoji-regex/package.json +0 -50
  99. package/node_modules/emoji-regex/text.js +0 -6
  100. package/node_modules/find-up/index.d.ts +0 -137
  101. package/node_modules/find-up/index.js +0 -89
  102. package/node_modules/find-up/license +0 -9
  103. package/node_modules/find-up/package.json +0 -53
  104. package/node_modules/find-up/readme.md +0 -156
  105. package/node_modules/get-caller-file/LICENSE.md +0 -6
  106. package/node_modules/get-caller-file/README.md +0 -41
  107. package/node_modules/get-caller-file/index.d.ts +0 -2
  108. package/node_modules/get-caller-file/index.js +0 -22
  109. package/node_modules/get-caller-file/index.js.map +0 -1
  110. package/node_modules/get-caller-file/package.json +0 -42
  111. package/node_modules/is-fullwidth-code-point/index.d.ts +0 -17
  112. package/node_modules/is-fullwidth-code-point/index.js +0 -50
  113. package/node_modules/is-fullwidth-code-point/license +0 -9
  114. package/node_modules/is-fullwidth-code-point/package.json +0 -42
  115. package/node_modules/is-fullwidth-code-point/readme.md +0 -39
  116. package/node_modules/locate-path/index.d.ts +0 -83
  117. package/node_modules/locate-path/index.js +0 -65
  118. package/node_modules/locate-path/license +0 -9
  119. package/node_modules/locate-path/package.json +0 -45
  120. package/node_modules/locate-path/readme.md +0 -122
  121. package/node_modules/p-limit/index.d.ts +0 -38
  122. package/node_modules/p-limit/index.js +0 -57
  123. package/node_modules/p-limit/license +0 -9
  124. package/node_modules/p-limit/package.json +0 -52
  125. package/node_modules/p-limit/readme.md +0 -101
  126. package/node_modules/p-locate/index.d.ts +0 -64
  127. package/node_modules/p-locate/index.js +0 -52
  128. package/node_modules/p-locate/license +0 -9
  129. package/node_modules/p-locate/package.json +0 -53
  130. package/node_modules/p-locate/readme.md +0 -90
  131. package/node_modules/p-try/index.d.ts +0 -39
  132. package/node_modules/p-try/index.js +0 -9
  133. package/node_modules/p-try/license +0 -9
  134. package/node_modules/p-try/package.json +0 -42
  135. package/node_modules/p-try/readme.md +0 -58
  136. package/node_modules/path-exists/index.d.ts +0 -28
  137. package/node_modules/path-exists/index.js +0 -23
  138. package/node_modules/path-exists/license +0 -9
  139. package/node_modules/path-exists/package.json +0 -39
  140. package/node_modules/path-exists/readme.md +0 -52
  141. package/node_modules/pngjs/.eslintignore +0 -2
  142. package/node_modules/pngjs/.eslintrc.json +0 -19
  143. package/node_modules/pngjs/.prettierignore +0 -3
  144. package/node_modules/pngjs/LICENSE +0 -20
  145. package/node_modules/pngjs/README.md +0 -429
  146. package/node_modules/pngjs/browser.js +0 -16096
  147. package/node_modules/pngjs/coverage/lcov-report/base.css +0 -354
  148. package/node_modules/pngjs/coverage/lcov-report/bitmapper.js.html +0 -881
  149. package/node_modules/pngjs/coverage/lcov-report/bitpacker.js.html +0 -554
  150. package/node_modules/pngjs/coverage/lcov-report/block-navigation.js +0 -77
  151. package/node_modules/pngjs/coverage/lcov-report/chunkstream.js.html +0 -647
  152. package/node_modules/pngjs/coverage/lcov-report/constants.js.html +0 -176
  153. package/node_modules/pngjs/coverage/lcov-report/crc.js.html +0 -200
  154. package/node_modules/pngjs/coverage/lcov-report/favicon.png +0 -0
  155. package/node_modules/pngjs/coverage/lcov-report/filter-pack.js.html +0 -593
  156. package/node_modules/pngjs/coverage/lcov-report/filter-parse-async.js.html +0 -152
  157. package/node_modules/pngjs/coverage/lcov-report/filter-parse-sync.js.html +0 -143
  158. package/node_modules/pngjs/coverage/lcov-report/filter-parse.js.html +0 -611
  159. package/node_modules/pngjs/coverage/lcov-report/format-normaliser.js.html +0 -359
  160. package/node_modules/pngjs/coverage/lcov-report/index.html +0 -426
  161. package/node_modules/pngjs/coverage/lcov-report/interlace.js.html +0 -365
  162. package/node_modules/pngjs/coverage/lcov-report/packer-async.js.html +0 -230
  163. package/node_modules/pngjs/coverage/lcov-report/packer-sync.js.html +0 -248
  164. package/node_modules/pngjs/coverage/lcov-report/packer.js.html +0 -467
  165. package/node_modules/pngjs/coverage/lcov-report/paeth-predictor.js.html +0 -128
  166. package/node_modules/pngjs/coverage/lcov-report/parser-async.js.html +0 -575
  167. package/node_modules/pngjs/coverage/lcov-report/parser-sync.js.html +0 -404
  168. package/node_modules/pngjs/coverage/lcov-report/parser.js.html +0 -950
  169. package/node_modules/pngjs/coverage/lcov-report/png-sync.js.html +0 -116
  170. package/node_modules/pngjs/coverage/lcov-report/png.js.html +0 -662
  171. package/node_modules/pngjs/coverage/lcov-report/prettify.css +0 -101
  172. package/node_modules/pngjs/coverage/lcov-report/prettify.js +0 -1007
  173. package/node_modules/pngjs/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  174. package/node_modules/pngjs/coverage/lcov-report/sorter.js +0 -169
  175. package/node_modules/pngjs/coverage/lcov-report/sync-inflate.js.html +0 -584
  176. package/node_modules/pngjs/coverage/lcov-report/sync-reader.js.html +0 -215
  177. package/node_modules/pngjs/coverage/lcov.info +0 -2177
  178. package/node_modules/pngjs/lib/bitmapper.js +0 -267
  179. package/node_modules/pngjs/lib/bitpacker.js +0 -158
  180. package/node_modules/pngjs/lib/chunkstream.js +0 -189
  181. package/node_modules/pngjs/lib/constants.js +0 -32
  182. package/node_modules/pngjs/lib/crc.js +0 -40
  183. package/node_modules/pngjs/lib/filter-pack.js +0 -171
  184. package/node_modules/pngjs/lib/filter-parse-async.js +0 -24
  185. package/node_modules/pngjs/lib/filter-parse-sync.js +0 -21
  186. package/node_modules/pngjs/lib/filter-parse.js +0 -177
  187. package/node_modules/pngjs/lib/format-normaliser.js +0 -93
  188. package/node_modules/pngjs/lib/interlace.js +0 -95
  189. package/node_modules/pngjs/lib/packer-async.js +0 -50
  190. package/node_modules/pngjs/lib/packer-sync.js +0 -56
  191. package/node_modules/pngjs/lib/packer.js +0 -129
  192. package/node_modules/pngjs/lib/paeth-predictor.js +0 -16
  193. package/node_modules/pngjs/lib/parser-async.js +0 -165
  194. package/node_modules/pngjs/lib/parser-sync.js +0 -108
  195. package/node_modules/pngjs/lib/parser.js +0 -290
  196. package/node_modules/pngjs/lib/png-sync.js +0 -12
  197. package/node_modules/pngjs/lib/png.js +0 -194
  198. package/node_modules/pngjs/lib/sync-inflate.js +0 -168
  199. package/node_modules/pngjs/lib/sync-reader.js +0 -45
  200. package/node_modules/pngjs/package.json +0 -73
  201. package/node_modules/qrcode/README.md +0 -770
  202. package/node_modules/qrcode/bin/qrcode +0 -159
  203. package/node_modules/qrcode/helper/to-sjis-browser.js +0 -2
  204. package/node_modules/qrcode/helper/to-sjis.js +0 -105
  205. package/node_modules/qrcode/lib/browser.js +0 -76
  206. package/node_modules/qrcode/lib/can-promise.js +0 -7
  207. package/node_modules/qrcode/lib/core/alignment-pattern.js +0 -83
  208. package/node_modules/qrcode/lib/core/alphanumeric-data.js +0 -59
  209. package/node_modules/qrcode/lib/core/bit-buffer.js +0 -37
  210. package/node_modules/qrcode/lib/core/bit-matrix.js +0 -65
  211. package/node_modules/qrcode/lib/core/byte-data.js +0 -30
  212. package/node_modules/qrcode/lib/core/error-correction-code.js +0 -135
  213. package/node_modules/qrcode/lib/core/error-correction-level.js +0 -50
  214. package/node_modules/qrcode/lib/core/finder-pattern.js +0 -22
  215. package/node_modules/qrcode/lib/core/format-info.js +0 -29
  216. package/node_modules/qrcode/lib/core/galois-field.js +0 -69
  217. package/node_modules/qrcode/lib/core/kanji-data.js +0 -54
  218. package/node_modules/qrcode/lib/core/mask-pattern.js +0 -234
  219. package/node_modules/qrcode/lib/core/mode.js +0 -167
  220. package/node_modules/qrcode/lib/core/numeric-data.js +0 -43
  221. package/node_modules/qrcode/lib/core/polynomial.js +0 -62
  222. package/node_modules/qrcode/lib/core/qrcode.js +0 -495
  223. package/node_modules/qrcode/lib/core/reed-solomon-encoder.js +0 -56
  224. package/node_modules/qrcode/lib/core/regex.js +0 -31
  225. package/node_modules/qrcode/lib/core/segments.js +0 -330
  226. package/node_modules/qrcode/lib/core/utils.js +0 -63
  227. package/node_modules/qrcode/lib/core/version-check.js +0 -9
  228. package/node_modules/qrcode/lib/core/version.js +0 -163
  229. package/node_modules/qrcode/lib/index.js +0 -12
  230. package/node_modules/qrcode/lib/renderer/canvas.js +0 -63
  231. package/node_modules/qrcode/lib/renderer/png.js +0 -78
  232. package/node_modules/qrcode/lib/renderer/svg-tag.js +0 -81
  233. package/node_modules/qrcode/lib/renderer/svg.js +0 -19
  234. package/node_modules/qrcode/lib/renderer/terminal/terminal-small.js +0 -85
  235. package/node_modules/qrcode/lib/renderer/terminal/terminal.js +0 -49
  236. package/node_modules/qrcode/lib/renderer/terminal.js +0 -9
  237. package/node_modules/qrcode/lib/renderer/utf8.js +0 -71
  238. package/node_modules/qrcode/lib/renderer/utils.js +0 -99
  239. package/node_modules/qrcode/lib/server.js +0 -138
  240. package/node_modules/qrcode/license +0 -10
  241. package/node_modules/qrcode/package.json +0 -77
  242. package/node_modules/require-directory/.jshintrc +0 -67
  243. package/node_modules/require-directory/.npmignore +0 -1
  244. package/node_modules/require-directory/.travis.yml +0 -3
  245. package/node_modules/require-directory/LICENSE +0 -22
  246. package/node_modules/require-directory/README.markdown +0 -184
  247. package/node_modules/require-directory/index.js +0 -86
  248. package/node_modules/require-directory/package.json +0 -40
  249. package/node_modules/require-main-filename/CHANGELOG.md +0 -26
  250. package/node_modules/require-main-filename/LICENSE.txt +0 -14
  251. package/node_modules/require-main-filename/README.md +0 -26
  252. package/node_modules/require-main-filename/index.js +0 -18
  253. package/node_modules/require-main-filename/package.json +0 -35
  254. package/node_modules/set-blocking/CHANGELOG.md +0 -26
  255. package/node_modules/set-blocking/LICENSE.txt +0 -14
  256. package/node_modules/set-blocking/README.md +0 -31
  257. package/node_modules/set-blocking/index.js +0 -7
  258. package/node_modules/set-blocking/package.json +0 -42
  259. package/node_modules/string-width/index.d.ts +0 -29
  260. package/node_modules/string-width/index.js +0 -47
  261. package/node_modules/string-width/license +0 -9
  262. package/node_modules/string-width/package.json +0 -56
  263. package/node_modules/string-width/readme.md +0 -50
  264. package/node_modules/strip-ansi/index.d.ts +0 -17
  265. package/node_modules/strip-ansi/index.js +0 -4
  266. package/node_modules/strip-ansi/license +0 -9
  267. package/node_modules/strip-ansi/package.json +0 -54
  268. package/node_modules/strip-ansi/readme.md +0 -46
  269. package/node_modules/which-module/LICENSE +0 -13
  270. package/node_modules/which-module/README.md +0 -58
  271. package/node_modules/which-module/index.js +0 -9
  272. package/node_modules/which-module/package.json +0 -41
  273. package/node_modules/wrap-ansi/index.js +0 -186
  274. package/node_modules/wrap-ansi/license +0 -9
  275. package/node_modules/wrap-ansi/package.json +0 -61
  276. package/node_modules/wrap-ansi/readme.md +0 -97
  277. package/node_modules/y18n/CHANGELOG.md +0 -35
  278. package/node_modules/y18n/LICENSE +0 -13
  279. package/node_modules/y18n/README.md +0 -109
  280. package/node_modules/y18n/index.js +0 -188
  281. package/node_modules/y18n/package.json +0 -39
  282. package/node_modules/yargs/CHANGELOG.md +0 -420
  283. package/node_modules/yargs/LICENSE +0 -21
  284. package/node_modules/yargs/README.md +0 -140
  285. package/node_modules/yargs/build/lib/apply-extends.d.ts +0 -2
  286. package/node_modules/yargs/build/lib/apply-extends.js +0 -65
  287. package/node_modules/yargs/build/lib/argsert.d.ts +0 -2
  288. package/node_modules/yargs/build/lib/argsert.js +0 -65
  289. package/node_modules/yargs/build/lib/command.d.ts +0 -64
  290. package/node_modules/yargs/build/lib/command.js +0 -416
  291. package/node_modules/yargs/build/lib/common-types.d.ts +0 -36
  292. package/node_modules/yargs/build/lib/common-types.js +0 -25
  293. package/node_modules/yargs/build/lib/completion-templates.d.ts +0 -2
  294. package/node_modules/yargs/build/lib/completion-templates.js +0 -50
  295. package/node_modules/yargs/build/lib/completion.d.ts +0 -21
  296. package/node_modules/yargs/build/lib/completion.js +0 -135
  297. package/node_modules/yargs/build/lib/is-promise.d.ts +0 -1
  298. package/node_modules/yargs/build/lib/is-promise.js +0 -9
  299. package/node_modules/yargs/build/lib/levenshtein.d.ts +0 -1
  300. package/node_modules/yargs/build/lib/levenshtein.js +0 -58
  301. package/node_modules/yargs/build/lib/middleware.d.ts +0 -10
  302. package/node_modules/yargs/build/lib/middleware.js +0 -57
  303. package/node_modules/yargs/build/lib/obj-filter.d.ts +0 -1
  304. package/node_modules/yargs/build/lib/obj-filter.js +0 -14
  305. package/node_modules/yargs/build/lib/parse-command.d.ts +0 -11
  306. package/node_modules/yargs/build/lib/parse-command.js +0 -36
  307. package/node_modules/yargs/build/lib/process-argv.d.ts +0 -2
  308. package/node_modules/yargs/build/lib/process-argv.js +0 -31
  309. package/node_modules/yargs/build/lib/usage.d.ts +0 -49
  310. package/node_modules/yargs/build/lib/usage.js +0 -540
  311. package/node_modules/yargs/build/lib/validation.d.ts +0 -34
  312. package/node_modules/yargs/build/lib/validation.js +0 -330
  313. package/node_modules/yargs/build/lib/yargs.d.ts +0 -274
  314. package/node_modules/yargs/build/lib/yargs.js +0 -1190
  315. package/node_modules/yargs/build/lib/yerror.d.ts +0 -4
  316. package/node_modules/yargs/build/lib/yerror.js +0 -11
  317. package/node_modules/yargs/index.js +0 -40
  318. package/node_modules/yargs/locales/be.json +0 -46
  319. package/node_modules/yargs/locales/de.json +0 -46
  320. package/node_modules/yargs/locales/en.json +0 -51
  321. package/node_modules/yargs/locales/es.json +0 -46
  322. package/node_modules/yargs/locales/fi.json +0 -49
  323. package/node_modules/yargs/locales/fr.json +0 -53
  324. package/node_modules/yargs/locales/hi.json +0 -49
  325. package/node_modules/yargs/locales/hu.json +0 -46
  326. package/node_modules/yargs/locales/id.json +0 -50
  327. package/node_modules/yargs/locales/it.json +0 -46
  328. package/node_modules/yargs/locales/ja.json +0 -51
  329. package/node_modules/yargs/locales/ko.json +0 -49
  330. package/node_modules/yargs/locales/nb.json +0 -44
  331. package/node_modules/yargs/locales/nl.json +0 -49
  332. package/node_modules/yargs/locales/nn.json +0 -44
  333. package/node_modules/yargs/locales/pirate.json +0 -13
  334. package/node_modules/yargs/locales/pl.json +0 -49
  335. package/node_modules/yargs/locales/pt.json +0 -45
  336. package/node_modules/yargs/locales/pt_BR.json +0 -48
  337. package/node_modules/yargs/locales/ru.json +0 -46
  338. package/node_modules/yargs/locales/th.json +0 -46
  339. package/node_modules/yargs/locales/tr.json +0 -48
  340. package/node_modules/yargs/locales/zh_CN.json +0 -48
  341. package/node_modules/yargs/locales/zh_TW.json +0 -47
  342. package/node_modules/yargs/package.json +0 -92
  343. package/node_modules/yargs/yargs.js +0 -14
  344. package/node_modules/yargs-parser/CHANGELOG.md +0 -601
  345. package/node_modules/yargs-parser/LICENSE.txt +0 -14
  346. package/node_modules/yargs-parser/README.md +0 -449
  347. package/node_modules/yargs-parser/index.js +0 -1032
  348. package/node_modules/yargs-parser/lib/tokenize-arg-string.js +0 -40
  349. package/node_modules/yargs-parser/package.json +0 -46
package/docs/03-api.md DELETED
@@ -1,816 +0,0 @@
1
- # 03 API 接口设计文档 — dsh-mobile-remote
2
-
3
- > 版本:v3.2.0 · 状态:已实现 · 配套:00-开发总纲.md、02-architecture.md、04-security.md、09-compatibility.md
4
- > 前缀:`/m`(可配置项 `path`,默认 `/m`)。以下所有路径均以前缀开头。
5
- > 服务端 API 同时服务 Flutter App(dsh-mobile-app)与桌面设置页客户端模块;不含网页版页面(v2.1 起移除)。
6
- ## 1. 通用约定
7
-
8
- - **内容类型**:JSON API 一律 `application/json; charset=utf-8`;错误统一 `{ "error": string, "detail"?: string }`。
9
- - **鉴权**:`authToken` 配置为空 → 无鉴权;非空 → 除 `GET /m/api/qr-config`(仅 loopback)外,所有端点要求凭证。
10
- - **凭证形式**(二选一,任一通过即可):
11
- - 请求头:`X-Mobile-Token: <token>`(App 使用)
12
- - Cookie:`dsh_mobile_token=<token>`(兼容保留,无登录入口)
13
- - **未认证响应**:`401 { "error": "auth-required", "detail": "访问口令未通过验证" }`。
14
- - **路由匹配**:`/m/api/*` 按前缀注册;未知子路径 → `404 { "error": "not-found" }`;`/m` 之外的裸路径不拦截(fallback 仍归桌面 SPA)。
15
- ## 2. 端点总表
16
-
17
- | 方法 | 路径 | 用途 | 鉴权 |
18
- |---|---|---|---|
19
- | GET | `/m/api/bootstrap` | 初始状态:地址、认证要求、agent 摘要、会话列表 | 是 |
20
- | GET | `/m/api/update/manifest` | App 自动更新(主机源):`updateDir` 里的 `manifest.json`(仅含元数据,不含 APK 本体) | 是 |
21
- | GET | `/m/api/update/apk` | 按 manifest 指定文件流式下发 APK(服务端先按 manifest 的 sha256 校验再下发) | 是 |
22
- | POST | `/m/api/send` | 向指定/默认 agent 注入消息(v2.7.2 支持 `mode: "steer"` 插队) | 是 |
23
- | GET | `/m/api/queue` | 排队消息列表(对齐 PC 端 Queue Dock;`placement: "queued"` 可插话、`"steering"` 不可,v2.7.2) | 是 |
24
- | POST | `/m/api/messages` | 排队消息操作:`{ sessionId, itemId, action: { kind: "edit"|"remove"|"steer", content? } }`(转发内核 `session.updateQueue`;错误码如 `queue-item-not-found`/`steer-unavailable` 透传,v2.7.2) | 是 |
25
- | GET | `/m/api/sessions` | 会话列表 | 是 |
26
- | GET | `/m/api/history` | 指定会话的 Conversation timeline 历史(按 seq 分页) | 是 |
27
- | GET | `/m/api/event-detail` | 单个 Visible event 无损详情(按 seq) | 是 |
28
- | GET | `/m/api/turn-outline` | 会话轮次大纲(只读投影;轮次号 / 起始序号 / 提示词与回复预览,轮次导航) | 是 |
29
- | GET | `/m/api/events` | SSE 事件流(session/event 摘要) | 是 |
30
- | GET | `/m/api/catalog` | 模型/推理/权限/预设目录 | 是 |
31
- | GET/POST | `/m/api/session-config` | 会话配置(读写) | 是 |
32
- | POST | `/m/api/sessions` | 新建会话 | 是 |
33
- | POST | `/m/api/sessions/touch` | 标记会话被打开(最近活跃排序) | 是 |
34
- | POST | `/m/api/sessions/archive` | 归档会话 | 是 |
35
- | POST | `/m/api/sessions/unarchive` | 恢复(取消归档) | 是 |
36
- | POST | `/m/api/sessions/stop` | 停止(取消)会话当前运行 | 是 |
37
- | POST | `/m/api/sessions/fork` | 在新对话中分支(映射内核 `session.fork`) | 是 |
38
- | GET/POST | `/m/api/feedback` | 消息反馈 👍/👎(内核 `messageFeedback` 服务,与 PC 端同一份) | 是 |
39
- | GET | `/m/api/notifications` | 通知列表 | 是 |
40
- | POST | `/m/api/notifications/read` | 标记已读 | 是 |
41
- | POST | `/m/api/notifications/delete` | 删除通知记录(单条/批量/全部) | 是 |
42
- | POST | `/m/api/respond` | 回答内核问询/审批(与 PC 端 GUI 同一 respond 通道) | 是 |
43
- | GET | `/m/api/actions` | 插件动作清单 | 是 |
44
- | POST | `/m/api/actions/:id/invoke` | 执行插件动作 | 是 |
45
- | GET | `/m/api/usage` | 会话 token 用量 | 是 |
46
- | GET | `/m/api/workspaces` | 已注册工作区 | 是 |
47
- | GET/POST | `/m/api/directories` | 目录浏览/新建文件夹 | 是 |
48
- | GET | `/m/api/diagnostics` | 环境诊断 | 是 |
49
- | GET | `/m/api/balance` | DeepSeek 官方余额 | 是 |
50
- | GET | `/m/api/account-usage` | 用量与额度(DeepSeek / Codex / OpenCode Go) | 是 |
51
- | GET | `/m/api/qr-config` | 桌面二维码数据(loopback only) | 否(loopback) |
52
- | POST | `/m/api/defaults` | 修改默认 Agent/权限预设 | 是 |
53
- | GET/POST | `/m/api/llm-providers` | 模型提供商列表 / 保存配置(v2.6) | 是 |
54
- | POST | `/m/api/llm-providers/probe` | 探测端点模型列表(v2.6) | 是 |
55
- | GET | `/m/qr.png` | 二维码 PNG | 否 |
56
- | GET | `/m/api/jobs` | 会话后台任务列表(v2.7,内核 jobs 同源) | 是 |
57
- | POST | `/m/api/jobs/kill` | 取消任务(v2.7,映射 `jobs.kill`) | 是 |
58
- | GET | `/m/api/subagents` | 子代理列表(v2.7,按父会话 `subagent.list`) | 是 |
59
- | POST | `/m/api/subagents/interrupt` | 中断子代理(v2.7,`subagent.interrupt`) | 是 |
60
- | GET/POST | `/m/api/goal` | 当前目标 / 创建·暂停·继续·完成(v2.7,goal RPC 同源) | 是 |
61
- | GET/POST | `/m/api/commands` | 斜杠命令目录/执行(v2.8.0;v2.8.2 适配内核 0.1.1-rc.2 四参签名,服务缺失优雅降级) | 是 |
62
-
63
- ### 2.1 App 自动更新(主机源,v3.x)
64
-
65
- **概念**:插件配置 `updateDir`(如 `~/.dsh/mobile-remote/update/`)里放发布脚本产出的 APK 与 `manifest.json`。**manifest 是唯一权威**——App 只读 manifest,不枚举目录。
66
-
67
- **`manifest.json` 契约**(由 `tools/gen-manifest.js` 统一生成,双端发布脚本共用):
68
-
69
- ```json
70
- { "version": "3.0.0+8", "apk": "DSH-Remote-v3.0.0.apk", "sha256": "<小写hex>", "size": 72026882, "notes": "CHANGELOG 最新条目全文(可为空)" }
71
- ```
72
-
73
- - `version`:`X.Y.Z` + 可选 `+build`(App 端精确到 build 判定更新/防降级)。
74
- - `sha256`:APK 字节 SHA-256(小写 hex);`size`:APK 字节数;`notes`:更新说明(可选)。
75
-
76
- **GET `/m/api/update/manifest`**:返回 `{ ok: true, manifest: {...} }`。错误码:
77
- - `503 update-not-configured`:未配置 `updateDir`;
78
- - `404 update-dir-missing` / `404 update-manifest-missing`:目录或 manifest 不存在;
79
- - `404 update-manifest-invalid`:manifest 缺字段/非 JSON。
80
-
81
- **GET `/m/api/update/apk`**:按 manifest `apk` 字段流式下发(`application/vnd.android.package-archive`,含 `content-length`)。**服务端先按 manifest 的 sha256 校验 APK 字节**再下发(防目录内文件被替换/损坏)。错误码:
82
- - 继承 manifest 阶段各错误码;`404 update-apk-missing`:文件不存在(含路径越界防御);
83
- - `500 update-apk-checksum-mismatch`:文件与 manifest sha256 不符(拒发);
84
- - `500 update-apk-read-failed`:读取失败。
85
-
86
- > GitHub 源不经过本插件(App 直连 `api.github.com/repos/201222-L/dsh-mobile-remote/releases/latest`,取首个 `DSH-Remote-*.apk` 资产,`browser_download_url` 直连下载)。
87
-
88
- ## 3. 端点详述(v1 既有端点)
89
- ### 3.1 GET /m/api/bootstrap
90
-
91
- **响应 200**
92
-
93
- ```json
94
- {
95
- "ok": true,
96
- "auth": { "enabled": false },
97
- "server": {
98
- "port": 3080,
99
- "urls": ["http://192.168.1.5:3080", "http://100.101.102.103:3080", "http://127.0.0.1:3080"]
100
- },
101
- "capabilities": {
102
- "eventTimeline": {
103
- "version": 1,
104
- "live": true,
105
- "history": true,
106
- "detail": true,
107
- "unknownEvents": true,
108
- "callCorrelation": true
109
- },
110
- "turnOutline": { "version": 1, "supported": true, "unloaded": true, "truncated": true }
111
- },
112
- "agents": [
113
- { "id": "session-abc", "status": "running", "hasPending": false }
114
- ],
115
- "sessions": [
116
- { "id": "session-abc", "createdAt": 1750000000000, "cwd": "F:\\DSH-Outpost" }
117
- ]
118
- }
119
- ```
120
-
121
- - `urls`:按优先级排列——首个非 internal IPv4(含蒲公英/Tailscale 等虚拟组网段)在前,loopback 最后;`port` 来自 `ctx.webServer.port`。v3.0.0:`lanBridge` 监听成功时首选地址为桥地址(`http://<IP>:<lanBridge.port>`,端口默认 3080),回环 webserver 地址仅作本机自连兜底。
122
- - `agents[].status`:`"running" | "idle"`(映射自 agent 状态与最近事件推断)。
123
- - `capabilities.turnOutline`:轮次大纲能力位(轮次导航)。宿主**真的注册了** `turnOutline` 投影单元、且投影感知观察面(`sessionQuery.observeSession`)可用时,返回 `{ "version": 1, "supported": true, "unloaded": true, "truncated": true }`——`unloaded` 表示未加载轮次也可跳转、`truncated` 表示超过体积上限会显式截断;否则返回 `{ "version": 1, "supported": false }`。**不按宿主版本号推断**:旧宿主/裁剪宿主显式声明不支持,App 据此退回「只用已加载轮次」的降级行为。SSE hello 的同名字段与该处**同判据、同形状**(见 §3.6 与 §6.18)。
124
- - 未认证:`401`(见通用约定)。
125
- ### 3.2 POST /m/api/send
126
- **请求**
127
-
128
- ```json
129
- { "sessionId": "session-abc", "text": "帮我跑一下测试", "mode": "steer" }
130
- ```
131
-
132
- - `requestId` 可选(**v3.0.0 热修 05**):客户端生成的 UUID(`^[A-Za-z0-9-]{8,64}$`,非法 → `400 invalid requestId`)。携带时服务端启用**幂等回执**:投递之前占位 in-progress,处理完成后记录结果快照;同一 `sessionId+requestId` 的重复请求**直接返回第一次结果、不再二次投递**(`Connection reset by peer` 后重试不会产生重复消息)。回执经 `GET /m/api/send-receipt` 查询;单进程内 + TTL 15 分钟幂等,持久化于 `~/.dsh/mobile-remote/send-receipts.json`(重启恢复;处理中状态不跨重启保留)。
133
-
134
- 图片发送(v3.0.0 图像链路,与 PC 端 wire 同形,原始字节不压缩):
135
-
136
- ```json
137
- { "sessionId": "session-abc", "text": "这是什么", "images": [{ "mediaType": "image/png", "data": "<base64>", "name": "photo.png" }] }
138
- ```
139
-
140
- - `sessionId` 可选:指定会话(必须存在且其 agent 存活);缺省 → 第一个 root agent。
141
- - `mode` 可选(v2.7.2):`"followup"`(默认,排队到下一轮)| `"steer"`(插队:消息插到 agent 下一步执行,适合 team 插件子会话向主会话插队)。agent 空闲时 `steer` 自动降级为 `followup`,响应 `note: "agent-idle-followup"`。
142
- - `images` 可选(v3.0.0):`[{ mediaType(仅 png/jpeg/webp/gif), data(canonical base64 原始字节), name? }]`——经内核 `session.prompt` 图片通道(内核限额/降采样/附件落盘,与 PC 端完全同一通路;纯文本仍走 followup)。超限/非规范 → `attachment-error`(`IMAGE_TOO_LARGE`/`INVALID_IMAGE_BASE64` 等)。请求体上限 64MB;**移动端客户端总量上限 40MB(热修 05)**——64MB body 扣掉 base64 膨胀(×4/3)与 JSON 开销后的安全值,超限在客户端明确提示、不落到服务端 413;内核侧 200MB 能力不受影响(PC 端同源)。**mediaType 纠正(v3.0.0 热修 02)**:服务端按字节魔数(PNG/JPEG/GIF/WebP/HEIC)嗅探真实类型,声明与字节不符自动纠正(warn 记录);未识别类型原样交内核裁决。图片路径 200 响应带 `accepted: true`(与文本路径语义一致)。
143
- - **v3.0.0(方案 A)**:`followup` 且 agent **运行中**时,消息**不进内核 next-turn**(内核会在当前轮结束瞬间自动认领执行,PC 端同款语义),而是**插件侧持存**——只出现在 Queue Dock/移动端 dock,**不渲染进对话窗口**(与 PC 端一致);agent 真正空闲(整个任务/目标结束)后按序自动释放为 `followup`。持存期间消息可经 `/messages` 删除/编辑/插队(全部插件侧执行,无认领竞态)。响应 `mode: "queued", note: "held-until-idle"`。持存文件 `~/.dsh/mobile-remote/held-queue.json`,插件重启不丢。图片持存同样支持(base64 随持存落盘,重启恢复;插队=立即 prompt steer)。
144
- **响应**
145
-
146
- - `200 { "ok": true, "agentId": "session-abc", "messageId": "m_<uuid>", "mode": "followup" | "steer" | "queued" }`(`mode: "queued"` 时附 `note`;图片路径 `note: "image-prompt"`)
147
- - `400 { "error": "empty-text" }`:text 为空或非字符串
148
- - `404 { "error": "session-not-found" }`:指定会话不存在
149
- - `503 { "error": "no-live-agent" }`:无匹配的运行中 agent
150
- - `503 { "error": "agents-unavailable" }`:agents 服务不可用(非 web 组合或启动中)
151
- **语义**:服务端构造 `createUserMessage({ content: [{ type: 'text', text }], source: { kind: 'user' } })` 后调用 `agent.followup(message)`(排队/空闲释放)或 `agent.steer(message)`(插队);运行中 `followup` 走持存(见上);含 `images` 时经内核 `session.prompt` 图片通道。`followup` 会持久化消息并唤醒空闲驱动器;不等待执行结果(结果经 SSE 回流)。
152
-
153
- ### 3.2c GET /m/api/send-receipt(v3.0.0 热修 05,发送回执查询)
154
-
155
- **查询参数**:`sessionId`(与发送时一致;缺省按 root agent 解析)、`requestId`(必填,非法 → `400 invalid requestId`)。
156
- **响应**:`200 { "ok": true, "receipt": { "status": "done" | "error" | "in-progress", "result": {...} } }`(`result` 为发送响应快照,含 messageId/accepted/note/mode);未命中 → `404 receipt-not-found`。
157
- **语义**:客户端在传输层错误(reset/超时)后用**同一个 requestId** 查询——`done` 即确认已送达(请勿重发);`in-progress` 稍后轮询;`404` 表示第一次请求未到达服务端(可重试,同 id 幂等)。此端点**不投递任何消息**。
158
-
159
- ### 3.2b GET /m/api/attachment(v3.0.0 图像链路,渲染取图)
160
-
161
- **查询参数**:`sessionId`、`attachmentId`(均必填,来自 SSE/history 摘要的 `images[].attachmentId`)。
162
- **响应**:`200` 原始字节(`Content-Type` 按 `mediaType`,`Cache-Control: private, max-age=3600`,`x-attachment-meta` 带 width/height/bytes/name);`400` 缺参;`404 attachment-not-found`;其他错误透传。鉴权与其他端点一致(`x-mobile-token`/cookie)。
163
- ### 3.3 GET /m/api/sessions
164
-
165
- **响应 200**
166
-
167
- ```json
168
- {
169
- "ok": true,
170
- "sessions": [
171
- {
172
- "id": "session-abc",
173
- "createdAt": 1750000000000,
174
- "cwd": "F:\\DSH-Outpost",
175
- "live": true,
176
- "title": "会话标题(活动会话)",
177
- "archived": false,
178
- "lastActivity": 1750000123456
179
- }
180
- ]
181
- }
182
- ```
183
-
184
- - 排序:`lastActivity` 倒序(无活跃记录时回退 `createdAt`)。数据源:优先 `sessionQuery.listSessions()`(含休眠会话),回退 `ctx.sessions.list()`(仅活动会话)。
185
- - `archived`:是否已归档(见 3.4 归档接口)。
186
- - `lastActivity`:最近活跃时间(ms)。任意会话事件(SSE)与移动端"打开会话"(3.4 touch)都会更新,持久化于 `~/.dsh/mobile-remote/session-activity.json`。
187
-
188
- ### 3.4 归档 / 活跃时间接口
189
-
190
- | 方法 | 路径 | 说明 |
191
- |---|---|---|
192
- | POST | `/m/api/sessions/touch` | 标记会话被打开(更新 lastActivity) |
193
- | POST | `/m/api/sessions/archive` | 归档会话(映射内核 `workspace.archiveSession`,与 PC 端同一份状态) |
194
- | POST | `/m/api/sessions/unarchive` | 恢复(取消归档) |
195
- | POST | `/m/api/sessions/stop` | 停止(取消)会话当前运行(映射核心 RPC `session.cancel`) |
196
-
197
- 请求体统一为 `{ "sessionId": "session-abc" }`。
198
-
199
- - `touch` 响应:`200 { "ok": true, "lastActivity": 1750000123456 }`
200
- - `archive` / `unarchive` 响应:`200 { "ok": true, "archived": true|false }`
201
- - `stop` 响应:`200 { "ok": true, "accepted": true }`;失败时 `400 { "error": "cancel-failed" }`(v2.8.0 起统一 rpcError 映射:内核错误透传 status+code、传输层 502、超时 504;旧版固定 500)
202
- - 归档状态即内核 `workspaceRegistry.archivedSessionIds`(与 PC 端同一份):PC 端归档的会话在移动端同样显示为已归档,反之亦然。归档会话仍出现在 3.3 列表中(`archived: true`),由客户端分栏展示。
203
-
204
- ### 3.5 GET /m/api/history
205
-
206
- **查询参数**
207
-
208
- - `sessionId`(必填):目标会话
209
- - `after`(可选):**增量模式**——只返回 `seq > after` 的事件(SSE 重连补漏用)。
210
- - `before`(可选):**上翻分页**——只返回 `seq < before` 的最近 `limit` 条事件(对话内滚动到顶部加载更早)。
211
- - 三种模式优先级:`after` > `before` > 初始加载(缺省时返回最近 `limit` 条,即尾部)。
212
- - `limit`(可选,默认 500,上限 1000):最多返回条数
213
- **过滤规则**:返回可进入 Conversation timeline 的事件;`assistant/chunk` / `assistant/live-chunk` 只用于实时草稿,`agent/inbox/spliced` 由队列投影承载,`request/header`(含 system prompt/tool schema)、`request/context`、`session/title-llm-request` / `web/deepseek-search-llm-request`(LLM 请求快照:data 含 `system` 系统提示词与 `messages` 对话正文)、`session/end-seed`、`step/start`、`step/end`、`system/message`、`assistant/attempt`(重试/中断的原始 stream 记录)与 `compaction/start` / `compaction/summary` / `compaction/prune` / `compaction/end`(压缩生命周期;summary 正文即替换 shadowed 区间后的上下文快照)属于内部/重建/快照元数据,历史、实时与 3.5b 详情三处都不返回。`compaction/end` 作为不落卡片的实时控制帧通知 App 重新加载 durable snapshot。
214
-
215
- 未知 type(即使带 `ignorable: true`)**保留 `seq`/`type` 进入时间线**(事件保真:不能因客户端尚未认识类型而静默丢弃),但**不下发详情指针**——`detail.available` 只对下方 allow-list 内的可见类型为真,其余类型调 3.5b 返回 `404 event-not-found`。这保证「新类型照常可见」与「未命名记录的原始载荷不外泄」同时成立(fail-closed)。
216
-
217
- **详情可见类型(allow-list)**:`user/message`、`assistant/message`、`tool/call`、`tool/result`、`todo/write`、`turn/start`、`turn/end`。
218
-
219
- 判定依据:这几类正是 `summarizeEventCore` **逐个类型显式审过载荷**并下发给 App 的类型;其余类型走 default 分支只给 `{seq, type}`,App 从不消费其 data。`assistant/chunk` / `assistant/live-chunk` 虽被 App 消费,但属于实时草稿(不落 durable 历史),故不在名单内。
220
-
221
- **有意的 fail-closed 取舍**:`approval/*`、`question/*`、`session/jobs`、`session/title`、`subagent/*`、`command/*`、`tool/ptc-dispatch*` 这些 App 会渲染卡片、但插件**尚未审过载荷**的类型当前**不在** allow-list —— 调试模式下展开这类卡片会显示「详情不可用」。原因是它们可能携带 prompt / 命令正文(如 `subagent/descriptor`、`command/run`)。要放行某个类型,需先逐类型审计载荷,然后**同时**改服务端 `DETAIL_VISIBLE_TYPES` 与 App 侧详情入口白名单。
222
-
223
- > 客户端呈现约定:`session/title`、`model/selection`、`sandbox/mode`、`agent-preset/selected`、`subagent/*`、`team/*`、`feedback/*`、`command/*`、`approval/policy`、`tool/ptc-dispatch*`、`goal/change`、`plan/mode`、`permission/preset`、`schedule/change`、`hook/*`、`llm/retry*`、`deliverables/presented`、`tool-workflow/*`、`session-log-deepseek/delivery-accepted`、`subagent/model-selection-policy` 属协议/运行时元数据——仍下发并保留 `seq`,但普通模式不渲染,调试模式可展开审阅(见 05-test-cases F-34)。`approval/asked` / `approval/decided` 是 durable 审批记录,普通模式以可读标题呈现(历史回放的权威源;问询只有瞬态帧,不保证回放)。
224
- **响应 200**
225
-
226
- ```json
227
- {
228
- "ok": true,
229
- "sessionId": "session-abc",
230
- "after": 42,
231
- "hasMore": false,
232
- "events": [
233
- { "seq": 43, "type": "user/message", "detail": { "available": true, "seq": 43 }, "data": { "text": "帮我跑一下测试" } }
234
- ]
235
- }
236
- ```
237
-
238
- `events[]` 使用与 SSE 帧相同的摘要格式(见 3.6),保证客户端去重逻辑单一。数据源:`ctx.sessions.get(id).events`(追加式冻结快照,天然按 seq 有序)。
239
- - `404 { "error": "session-not-found" }`
240
-
241
- ### 3.5b GET /m/api/event-detail
242
-
243
- 按会话和 durable `seq` 读取单个 Visible event 的无损详情。该端点需要认证;服务端优先使用 `sessionQuery.readEvent`,旧内核回退到活动会话快照或休眠会话读取。
244
-
245
- 查询参数:`sessionId`、`seq`。
246
-
247
- ```json
248
- {
249
- "ok": true,
250
- "sessionId": "session-abc",
251
- "event": {
252
- "seq": 43,
253
- "type": "tool/result",
254
- "time": 1750000000000,
255
- "surfaceOp": "append",
256
- "sourceEventSeqs": [42],
257
- "data": { "callId": "call-1", "isError": false, "text": "完整结果" }
258
- }
259
- }
260
- ```
261
-
262
- 当服务端只能从 seeded session 的当前 surface 读取时,成功响应额外包含 `"degraded": true, "detailMode": "current-surface"`;客户端必须保留该元数据并提示详情可能不完整。详情不存在或属于内部/敏感类型返回 `404 event-not-found`;单事件详情超过 8 MiB 返回 `413 event-detail-too-large`。
263
-
264
- 稳定错误矩阵:`session-not-found`(会话不存在)、`event-not-found`(seq 不存在/不可见/无详情权限)、`session-corrupt`(会话数据损坏)、`event-read-failed`(读取失败)和 `event-detail-too-large`(超过 8 MiB)。错误响应不得泄露主机路径或原始异常;旧服务端未保存详情时客户端显示安全错误并提供重试,不猜测重建。
265
-
266
- > 命名说明:本端点的读取失败码是 **`event-read-failed`**(单事件级),与 `/m/api/history` 的 **`session-read-failed`**(整会话回放失败)语义不同,**不要**为"统一"而合并——App 侧对两者有各自的文案与重试路径。
267
-
268
- **规范化正文(`text`)**:`assistant/message` 的详情响应额外附 `data.text`,由服务端用**与事件摘要同一个 `blocksToText`** 提取(只拼 `type == "text"` 的块,跳过 `reasoning` 与内部块),因此与摘要下发的 `text` 同源同规则。客户端必须直接采用该字段作为正文,**不得自行递归拼接 `message.content`**——那会把 `reasoning` 块并进正文,使思维链在折叠块之外重复出现(issue #1 需求变更记录)。`tool/result` 等其它类型的详情仍以原始事件载荷为准;原始 `message` 块原样保留。
269
-
270
- ### 3.6 GET /m/api/events(SSE)
271
- `Content-Type: text/event-stream`。帧格式(`data:` 单行 JSON):
272
-
273
- ```json
274
- { "type": "session/event", "sessionId": "session-abc", "event": { "seq": 44, "type": "turn/end", "data": { "reason": { "kind": "complete" } }, "detail": { "available": true, "seq": 44 } } }
275
- ```
276
-
277
- **事件摘要 `event` 字段**:实时和历史使用同一摘要 envelope;摘要可包含 `detail: { available: true, seq }`,长参数/结果通过 3.5b 按需读取,不在列表中静默截断。只有 `/event-detail` 真能取回的事件才带该指针(token delta 与内部/快照记录不带,避免客户端显示必然 404 的详情按钮)。
278
-
279
- `assistant/message` 的指针额外带 **`textChars`**:与摘要同 `blocksToText` 口径的**未截断**正文长度。客户端据此**在加载前**判断「详情是否真有正文增量」(`textChars > 当前可见正文长度`),从而普通模式只在确有增量时显示加载入口;调试模式不受此限,始终提供「查看原始事件」。该字段为纯增量,旧客户端忽略即可。
280
-
281
- | event.type | event.data 内容 |
282
- |---|---|
283
- | `user/message` | `{ text: string }`(text blocks 拼接,≤2000 字符);v3.0.0 起附图 `images: [{ attachmentId, mediaType, width?, height?, name? }]`;文件块可带 `files: [{ attachmentId?, path?, name?, mediaType?, size? }]`(**用户自己的附件,保留**;App 只显示文件名/类型/大小,不提供下载——issue #1 需求变更) |
284
- | `assistant/message` | `{ text: string, reasoningChars: number, reasoning?: string }`(`reasoning` 为思维链正文,仅当非空时下发,供移动端折叠块;≤20000 字符);v3.0.0 起附图 `images: [...]`(同上);**v3.0.0 版本二**:`images` 含嵌套收集——`tool-result.content` 内的图片块(read_image 等工具结果)与顶层图一并带出(对齐 PC 端 contentParts 语义);**文件块不再下发 `files`**(issue #1 需求变更:时间线不提供产出文件的下载入口) |
285
- | `assistant/chunk` / `assistant/live-chunk` | `{ text: string }` 文本/reasoning delta;工具参数 delta 的 canonical `chunk.id` 归一为摘要 `callId`(仅实时过程,历史过滤) |
286
- | `tool/call` | `{ turn, step, callId, name, arguments }`;长参数可通过详情端点获取 |
287
- | `tool/result` | `{ callId, name, isError, text, images? }`;长结果可通过详情端点获取;**不再下发 `files`**(issue #1 需求变更:工具产出文件不上时间线;图片仍带 `images`) |
288
- | `turn/start` | `{ turn: number }` |
289
- | `turn/end` | `{ turn: number, reason: object }` |
290
- | 其他 | 保留 `type`、`seq` 和详情指针;调试模式展开无损数据 |
291
-
292
- **控制帧**:连接建立后立即 `data: {"type":"hello","serverTime":...,"capabilities":{"eventTimeline":{...},"turnOutline":{...}}}`;每 25s `: ping` 注释行。bootstrap 同样返回这两项能力;`turnOutline` 与 bootstrap 同判据、同形状(见 §6.18),客户端应按能力协商而不是按版本号猜测。
293
- **错误语义**:鉴权失败在连接建立阶段以 `401` HTTP 状态返回(EventSource 会触发 error 事件,客户端转登录态)。
294
-
295
- ### 3.6b SSE 帧类型汇总(v2.3+ 扩展)
296
-
297
- 除 3.6 的 `session/event` 外,SSE 还会推送:
298
-
299
- | type | 说明 |
300
- |---|---|
301
- | `session/context` | `{ sessionId, contextWindow }`——模型上下文窗口(`request/context` 事件,PC 端圆环同源) |
302
- | `agent/status` | `{ agentId, sessionId, status, child }`——running / waiting / idle;`sessionId` 为去 `session:` 前缀的会话 id,`child` 标记子代理会话(v2.7.2 起携带后两字段) |
303
- | `notifications/changed` | 通知记录增删(如移动端删除后),客户端刷新列表与角标 |
304
- | `mobile/notify` | `{ notification: { id, kind, sessionId, title, detail, time } }`——插件"真结束"判定后推送的通知(completed / failed / needs-answer),悬浮球/App 与通知中心同源渲染(v2.7.2) |
305
- | `mobile/frame` | 内核瞬态帧(问询/审批)。`frame` 字段为 `question/requested`(含 `rpcId`、`questions[]`)、`question/resolved`(`questionRpcId`)、`approval/requested`(`rpcId`、`approvalId`、`toolName`、`reason?`)、`approval/resolved`(`approvalId`)。**App 断线重连时服务端补发挂起的待答帧**(`pendingFrames` 回放) |
306
- | `mobile/queue` | `{ sessionId, rows: [{ id, text, placement }] }`——内核队列快照(`agent/inbox/spliced` 即时镜像,v3.0.2):认领/删除/编辑实时反映,App 端 dock 以此为权威源(`placement`: `queued` / `steering` / `context`,与 GET /queue 同款形状);断线重连时 mux 回放当前队列 |
307
-
308
- 客户端应按 `type` 分派;未知 type 使用通用事件卡保留顺序,并在调试模式通过详情指针展开(前向兼容)。
309
- ### 3.7 GET /m/qr.png
310
-
311
- **查询参数**:`text`(必填,二维码内容,URL 编码)。无 `text` → `400`。 **响应**:`200 image/png`(qrcode 包生成,尺寸 512,纠错级别 M)。
312
- ## 4. 错误码汇总
313
- | HTTP | error 值 | 场景 |
314
- |---|---|---|
315
- | 400 | `bad-request` | JSON 解析失败 / 缺参 / 参数类型错误 |
316
- | 400 | `empty-text` | send 的 text 为空 |
317
- | 401 | `auth-required` | 未提供有效凭证 |
318
- | 401 | `bad-token` | 登录口令错误 |
319
- | 429 | `rate-limited` | 登录失败超限(v2.6,仅 authToken 启用时;响应带 `Retry-After` 头,默认 60s 后恢复) |
320
- | 404 | `not-found` | 未知路径 |
321
- | 404 | `session-not-found` | 会话不存在 |
322
- | 404 | `event-not-found` | 事件不存在、属于内部/敏感类型,或不在移动端 Visible timeline |
323
- | 413 | `event-detail-too-large` | 单个无损详情超过 8 MiB 上限 |
324
- | 405 | `method-not-allowed` | 方法不支持(GET 端点收到 POST 等) |
325
- | 503 | `no-live-agent` | 无运行中 agent |
326
- | 503 | `agents-unavailable` | agents 服务不可用 |
327
-
328
- ## 5. 版本兼容策略
329
-
330
- - 端点仅追加、不破坏性修改;`event` 摘要格式允许增加字段,禁止删除/重命名既有字段。
331
- - 前端与后端同包发布(单文件页面由插件自身服务),无跨版本部署问题。
332
- ## 6. 移动端新增端点(Phase 1,v2.0)
333
- > 全部要求鉴权(与既有端点一致);写操作遵循与 PC 端相同的确认语义(如 Full Access 风险确认由客户端先行,服务端在参数中携带确认标记)。
334
- ### 6.1 端点总表
335
-
336
- | 方法 | 路径 | 用途 |
337
- |---|---|---|
338
- | GET | `/m/api/catalog` | 模型目录 + 推理强度 + 权限预设 + Agent 预设(全部枚举,一次拉取) |
339
- | GET | `/m/api/session-config` | 当前会话配置(模型/推理强度/权限/预设) |
340
- | POST | `/m/api/session-config` | 更新当前会话配置 |
341
- | POST | `/m/api/sessions` | 新建会话(preset 参数) |
342
- | GET | `/m/api/notifications` | 通知列表(未读/已读) |
343
- | POST | `/m/api/notifications/read` | 标记已读(单条/全部) |
344
- | POST | `/m/api/notifications/delete` | 删除通知记录(单条/批量/全部,v2.3) |
345
- | GET | `/m/api/actions` | 插件动作清单(实时) |
346
- | POST | `/m/api/actions/:id/invoke` | 执行插件动作 |
347
- | GET | `/m/api/usage` | 会话 token 用量统计(v2.1) |
348
- | GET | `/m/api/workspaces` | 已注册工作区(新建会话默认目录,v2.1) |
349
- | GET | `/m/api/directories` | 目录浏览(盘符/子目录,v2.1;v3.1.1 根视图响应新增 `sep` 字段) |
350
- | POST | `/m/api/directories` | 新建文件夹(v2.1) |
351
- | GET | `/m/api/diagnostics` | 环境诊断(服务端端点实测,v2.1) |
352
- | GET | `/m/api/balance` | DeepSeek 官方余额(服务端代查,v2.1) |
353
- | GET | `/m/api/account-usage` | 用量与额度(服务端代查,v3.2) |
354
- | GET | `/m/api/turn-outline` | 会话轮次大纲(只读;完整阶梯的轮次摘要,轮次导航) |
355
- | GET | `/m/api/qr-config` | 桌面二维码数据(loopback only,v2.1) |
356
- | POST | `/m/api/defaults` | 修改默认 Agent/权限预设(v2.1) |
357
-
358
- > **路径风格约定(v3.1.1,issue #5)**:`GET /m/api/directories?path=`、`POST /m/api/directories {path}` 与
359
- > `POST /m/api/sessions {cwd}` 的路径允许按客户端平台习惯传分隔符(Windows `\` / WSL·Linux·macOS `/`);
360
- > 服务端会归一化为**当前平台**分隔符后读盘/建夹/建会话(旧版 App 在 WSL 上拼出的 `/\home` 也能命中真实目录)。
361
- > 根视图响应(`path` 为空)携带 `sep`(服务端真实分隔符),新版 App 据此拼接子目录,不再按 Windows 习惯硬编码 `\`;
362
- > 该字段为纯增量,旧版 App 忽略即可。
363
-
364
- ### 6.2 GET /m/api/catalog
365
-
366
- 返回 PC 端真实枚举(单一事实源,客户端不硬编码):
367
- ```json
368
- {
369
- "ok": true,
370
- "models": [
371
- { "provider": "deepseek-official", "id": "deepseek-v4-flash", "name": "DeepSeek-V4-Flash",
372
- "reasoning": { "efforts": [{ "id": "off", "name": "Off" }, { "id": "high", "name": "High" }], "defaultEffort": "high" } },
373
- { "provider": "openai-codex", "id": "gpt-6-luna", "name": "GPT-6-Luna",
374
- "reasoning": { "efforts": [{ "id": "low", "name": "Low" }, { "id": "xhigh", "name": "Xhigh" }] } }
375
- ],
376
- "reasoningEfforts": ["off", "high", "low", "xhigh"],
377
- "permissionPresets": [
378
- { "id": "read-only", "name": "Read Only", "description": "只读 · 拒绝一切写入操作" },
379
- { "id": "workspace-write", "name": "Workspace Write", "description": "仅工作区内读写 · 危险操作前询问" },
380
- { "id": "danger-full-access", "name": "Danger Full Access", "description": "完全访问 · 可执行任何操作" }
381
- ],
382
- "agentPresets": [
383
- { "id": "standard", "name": "标准模式", "description": "功能完整的编码 Agent:文件编辑、Shell、文件与网页检索、Skills、计划、目标、子代理和工作流" },
384
- { "id": "code", "name": "PTC 模式", "description": "—" },
385
- { "id": "minimal", "name": "极简模式", "description": "—" },
386
- { "id": "cordis", "name": "创造模式", "description": "—" }
387
- ],
388
- "defaults": {
389
- "provider": "deepseek-official",
390
- "model": "deepseek-v4-flash",
391
- "reasoningEffort": "max",
392
- "permissionPreset": "workspace-write",
393
- "agentPreset": "standard"
394
- }
395
- }
396
- ```
397
-
398
- `models[].reasoning` 来自内核模型目录,只对该模型有效;没有该字段表示不支持显式强度。`reasoning.defaultEffort` 可以缺省,此时由提供商决定;`off` 是具体等级而不是“跟随默认”。顶层 `reasoningEfforts` 是历史上的跨模型并集,仅为兼容已发布接口保留;客户端必须按当前模型取能力,不能据此绘制或验证当前模型的按钮。`defaults.provider/model` 是部署默认模型,不是第一个活动会话上次使用的模型。目录通常缓存 15 秒;选择失败后的 `GET /m/api/catalog?refresh=1` 绕过缓存,以便重新展示该模型的最新能力。权限、Agent 预设来自对应宿主服务。
399
- ### 6.3 GET /m/api/session-config
400
-
401
- ```json
402
- {
403
- "ok": true,
404
- "sessionId": "session-abc",
405
- "config": {
406
- "provider": "openai-codex",
407
- "model": "gpt-6-luna",
408
- "reasoningEffort": "low",
409
- "permissionPreset": "workspace-write"
410
- }
411
- }
412
- ```
413
-
414
- 模型身份与强度读取自 `session/control` 的 `modelSelection.next`(待生效选择),不是可能滞后的 `lastUsed`。内核可能把省略的 effort 具体化为模型默认值,因此返回 `low` 不代表用户曾显式选择了 `low`。
415
-
416
- ### 6.4 POST /m/api/session-config
417
-
418
- **切换模型**(provider、model 必须成对提供,省略强度即使用新模型默认值):
419
-
420
- ```json
421
- { "sessionId": "session-abc", "provider": "openai-codex", "model": "gpt-6-luna" }
422
- ```
423
-
424
- **只改强度**:`{ "sessionId": "session-abc", "reasoningEffort": "high" }`;**回到模型默认**:`{ "sessionId": "session-abc", "resetReasoningEffort": true }`。两者不可同时提交。服务端使用同一次 `next` 的 provider/model;若 `next` 不可确认,返回 `409 model-selection-unavailable`,不拿 `lastUsed` 或 DeepSeek 兜底。**限定条件(2026-10-04 补充)**:该保证针对**已挂载**会话;对休眠会话,插件在按 `next` 取值后仍会执行 `Object.assign(config, foldFromEvents(events))`,而折叠函数恒返回全部字段(无匹配事件时为 `undefined`),会覆盖投影值——两者同源(都来自 `model/selection` 事件),但休眠会话可能因此报不出 effort。真正的修复方向是核心侧 `readSession` 走 restore 构造路径(见 issue #20)。不支持该模型的显式强度由内核拒绝,目录过期时客户端应刷新目录并显示错误,不静默降级。
425
-
426
- - `permissionPreset` 为 `danger-full-access` 时 `confirmDanger` 必须为 `true`,否则 `400 { "error": "risk-confirmation-required" }`(与 PC 端 Full access 需显式确认风险一致)。
427
- - 权限写入走 PC 端同一路径(`permission/preset` + sandbox/approval 旋钮事件)。
428
- - 模型选择会产生内核 `model/selection` 事件;`404 session-not-found` 通常表示会话不存在——但**请求带 `permissionPreset` 时,休眠(未挂载)会话也会 404**:权限预设需要本地 Session 对象才能 `applyPermissionPreset`,无法像模型/强度那样经内核 `selectModel` 对持久化会话生效(2026-10-04 补充)。
429
- ### 6.5 POST /m/api/sessions(新建会话)
430
-
431
- **请求**:
432
- ```json
433
- {
434
- "preset": "standard",
435
- "provider": "openai-codex",
436
- "model": "gpt-6-luna",
437
- "reasoningEffort": "high",
438
- "permissionPreset": "workspace-write"
439
- }
440
- ```
441
-
442
- - `preset` 未传时使用服务端 Agent 预设默认值;`provider` 和 `model` 必须一起指定,未指定时采用内核部署默认选择(包含部署默认强度),不沿用现有会话配置。主动指定新模型而省略 `reasoningEffort` 时采用该模型默认强度;具体等级由内核校验。
443
- - 新建页模型草稿不修改已有会话。服务端先确定完整模型身份,再创建 agent 并选择模型;选择或附加配置失败时清理新建会话,返回错误而非假报成功。
444
- **响应**:`200 { "ok": true, "sessionId": "session-xyz", "agentId": "session-xyz" }`——客户端随后即可 `POST /api/send` 或订阅 SSE 该会话。
445
- ### 6.6 GET /m/api/notifications
446
-
447
- 通知由服务端从会话事件流**聚合**(不新增存储):
448
-
449
- ```json
450
- {
451
- "ok": true,
452
- "unread": 2,
453
- "items": [
454
- { "id": "n-1", "kind": "needs-answer", "sessionId": "session-abc", "title": "需要你回答", "detail": "发现 214 个重复文件,是否删除以释放空间?", "time": 1750000000000, "unread": true },
455
- { "id": "n-2", "kind": "completed", "sessionId": "session-abc", "title": "任务完成", "detail": "备份配置到 E 盘(耗时 4 分钟)", "time": 1750000000000, "unread": true },
456
- { "id": "n-3", "kind": "failed", "sessionId": "session-def", "title": "任务失败", "detail": "日志分析任务执行失败,已自动重试", "time": 1750000000000, "unread": false }
457
- ]
458
- }
459
- ```
460
-
461
- - `kind`:`completed`(turn/end 正常)、`needs-answer`(等待人类决策——从审批/提问相关事件推导,Phase 1 实现时验证语义)、`failed`(turn/end 异常/aborted)。
462
- - **未读状态持久化**:插件在 settings 域保存已读 id 集合(`ctx.settings`),服务重启不丢。
463
- - 排序:时间倒序;上限 100 条。
464
- ### 6.7 POST /m/api/notifications/read
465
-
466
- ```json
467
- { "ids": ["n-1", "n-2"] }
468
- ```
469
- 或 `{ "all": true }`。响应 `200 { "ok": true }`。
470
- ### 6.7b POST /m/api/notifications/delete(v2.3)
471
-
472
- ```json
473
- { "ids": ["n-1", "n-2"] }
474
- ```
475
- 或 `{ "all": true }`。删除移动端通知镜像中的记录(不影响 PC 端自己的通知中心);不设墓碑——后续新事件仍会正常生成新通知。删除后经 SSE 广播 `notifications/changed` 帧,客户端刷新列表与未读角标。响应 `200 { "ok": true }`。
476
- ### 6.7c POST /m/api/respond(v2.3,问询/审批弹窗)
477
-
478
- 回答内核人类问询(`ask_user_question` 工具)或权限审批。插件经 `apiProxy.respond` 回写,**与 PC 端 GUI 完全同一 pending 通道与校验**(`matchesQuestions`、审批决策等由内核把关):
479
-
480
- **问询**(`kind: "question"`,answers 顺序与提问一致、每问必答):
481
-
482
- ```json
483
- { "kind": "question", "rpcId": "<frame 携带的 rpcId>", "sessionId": "session-abc",
484
- "answers": [ { "id": "q1", "selected": ["方案A"] }, { "id": "q2", "selected": [], "custom": "先跳过" } ] }
485
- ```
486
-
487
- - 单选:`selected` 至多 1 项;给了 `custom` 则 `selected` 必须为空(二选一)。
488
- - 多选:`selected` 多项;`custom` 可与之并存。
489
- - `selected` 必须是提问声明的选项 label。
490
-
491
- **审批**(`kind: "approval"`):
492
-
493
- ```json
494
- { "kind": "approval", "rpcId": "...", "sessionId": "session-abc", "approvalId": "a-1", "outcome": "allowed-once" }
495
- ```
496
-
497
- - `outcome`:`allowed-once` | `rejected`。
498
-
499
- **取消**(`kind: "cancel"`):内核收到 cancelled,agent 按 `ASK_CANCELLED` 继续。
500
-
501
- 响应:`200 { "ok": true, "accepted": true }`;`accepted: false` + `reason`(如 `not-pending`,PC 端已先回答)。
502
-
503
- **SSE 帧**:`question/requested`(含 `questions[]`)、`question/resolved`、`approval/requested`(toolName/reason)、`approval/resolved`,经 `mobile/frame` 帧推送;App 断线重连时服务端补发挂起的待答帧。
504
- ### 6.8 GET /m/api/actions
505
-
506
- **动作契约 v0.1**(可选能力,插件不注册则返回空数组,客户端隐藏动作区):
507
-
508
- ```json
509
- {
510
- "ok": true,
511
- "actions": [
512
- { "id": "fs-cleanup", "title": "清理磁盘", "icon": "trash", "fields": [ { "key": "target", "label": "目标目录", "placeholder": "如 F:\\资料" } ] },
513
- { "id": "test-run", "title": "跑测试", "icon": "zap", "fields": [] }
514
- ]
515
- }
516
- ```
517
-
518
- - 服务端注册表:`ctx.mobileActions.register({ id, title, icon, fields?, handler })`——id 冲突抛错(Cordis 语义);插件卸载随 fiber dispose 自动移除。
519
- - `icon` 限定为移动端内置图标库的 id(不允许插件自带 UI/图标)。
520
- - `fields`:v0.1 仅支持 `text` 类型字段。
521
- ### 6.9 POST /m/api/actions/:id/invoke
522
-
523
- **请求**:`{ "args": { "target": "F:\\资料" } }`
524
-
525
- - 服务端校验注册表存在与参数类型,调用 `handler(args)`(跑在电脑端)。
526
- - 执行结果**不直接返回**(可能长任务):`200 { "ok": true, "accepted": true }`;后续进展经 SSE 会话事件回流(动作应通过既有消息/工具通道呈现)。
527
- ### 6.10 GET /m/api/qr-config(桌面二维码数据,v2.1)
528
- **仅电脑本机可访问**(TCP 层 socket 来源,仅 loopback 可访问;否则 403 `loopback-only`)—— 桌面 dsh 设置页客户端模块用它生成「连接移动端设备」二维码。
529
- > v2.6.0:`/m/qr.png`(二维码图片渲染,供设置页 `<img>` 使用)同样收口——Host 校验 + loopback 来源,非本机 403。
530
- **响应**:`{ "ok": true, "urls": ["http://192.168.1.100:3080/m", ...], "token": "<authToken>", "path": "/m" }`
531
-
532
- > v3.0.0:`lanBridge.enabled` 且监听成功时,`urls` 首选为桥地址(`http://<电脑局域网IP>:<lanBridge.port>/m`,端口默认 3080);绑定失败自动回退 webserver 地址(不指向死端口)。
533
-
534
- 二维码内容格式:`DSHREMOTE|<地址>|<口令>`(地址取 `urls` 中首个非回环项,不含 /m 尾巴)。App 扫码解析后自动配置连接。
535
- ### 6.11 POST /m/api/defaults(修改默认配置,v2.1)
536
- 修改**默认 Agent 预设 / 默认权限预设**(作用于之后新建的会话),与 PC 端设置页同一写入通道(走 `/api` 调 `settings.update`,不在 HTTP 回调直接调 settings 服务)。
537
- **请求**:`{ "agentPreset": "code", "permissionPreset": "workspace-write" }`(两者均可选)
538
-
539
- **响应**:`200 { "ok": true }`;失败 `400 { "error": "update-failed", "detail": "<原因>" }`
540
-
541
- ### 6.11b GET /m/api/diagnostics(宿主能力自检字段)
542
-
543
- 环境诊断在 `checks` 下提供宿主兼容自检(ADR 0017,**纯增量字段,旧 App 忽略即可**):
544
-
545
- - `checks.hostCapabilities`:逐项三态 `ok` / `drift` / `missing`。`drift` = 能力语义漂移(服务在、调用不报错、成员形状或实参语义已变)。包含 `jobs.callerShape`、`jobs.eventsApi`、`settings.read`、`gateway.wireStream`、`gateway.eventsPeer`(issue #34,0.2.1 的进程内 Peer 来源)、`permissionPresets.apply`、`permissionPresets.catalog`、`workspaceRegistry.stateWrite`。
546
- - `checks.hostCapabilityReasons`:仅对 `drift` 项给出的允许清单原因码(如 `settings.read` 的 `settings-get-threw`;`gateway.eventsPeer` 的 `gateway-peer-source-missing`)。
547
- - `checks.hostGeneration`:插件实际走到的代际路径——`jobsCaller`(`sessionId` / `agent`)、`settingsRead`(`describe` / `get`)、`wireStreamArgs`(`controlArg` / `legacyArg`)、`eventsPeer`(`createPeer` / `operatorPeer` / `legacy` / `none` / `unknown`)。
548
- - `checks.remoteEvents` / `checks.approvalMode`:`$events` 双端审批通道是否就绪与实际生效的审批呈现策略。
549
-
550
- 详见 `docs/09-compatibility.md` §2.5/§2.6。
551
-
552
- ### 6.12 错误码补全
553
- | HTTP | error 值 | 场景 |
554
- |---|---|---|
555
- | 400 | `risk-confirmation-required` | 选 danger-full-access 未带 confirmDanger |
556
- | 400 | `invalid-preset` | preset 不在目录内 |
557
- | 404 | `action-not-found` | 动作 id 未注册 |
558
- | 503 | `action-busy` | 同一动作并发执行限制(v0.1 可先不做) |
559
-
560
- ### 6.13 模型提供商(v2.6,与 PC 端「设置 → 模型」同一配置通道)
561
- > 全部端点需鉴权;密钥只写不读(响应不含密钥本身)。
562
-
563
- **GET `/m/api/llm-providers`** — 提供商列表(live + dormant)
564
- ```json
565
- { "ok": true, "providers": [
566
- { "id": "deepseek-official", "name": "DeepSeek", "dormant": false,
567
- "settingsNs": "llm-deepseek", "settingsPath": [],
568
- "baseURL": "https://api.deepseek.com", "apiKeyRef": "DEEPSEEK_API_KEY",
569
- "keyConfigured": true, "keyWritable": true, "catalogModels": [{"id":"deepseek-v4-pro","name":"DeepSeek-V4-Pro"}] },
570
- { "id": "anthropic", "name": "anthropic", "dormant": true, "settingsNs": "…", "baseURL": null, "keyConfigured": false }
571
- ]}
572
- ```
573
- - dormant = 配置目录已声明但未激活(配好 baseURL/密钥即生效);37 个内置 dormant 提供商(anthropic/openai/google/groq 等)
574
- - `apiKeyRef` 为凭据引用名(如 `DEEPSEEK_API_KEY`),**非密钥本身**;`keyConfigured` 指示是否已存
575
-
576
- **POST `/m/api/llm-providers/probe`** — 探测端点模型列表
577
- ```json
578
- 请求: { "settingsNs": "llm-deepseek", "baseURL": "https://…", "apiKey": "可选", "protocol": "可选" }
579
- 响应: { "ok": true, "models": [{"id":"…","name":"…","contextWindow":…}], "fallback": true }
580
- ```
581
- - 优先内核 `discoverModels`;内核未注册模型探测时回退 OpenAI 兼容 `GET {baseURL}/models`
582
- - 凭据一次性使用,不存储;仅允许探测配置目录声明的命名空间
583
-
584
- **POST `/m/api/llm-providers`** — 保存提供商配置
585
- ```json
586
- 请求: { "provider": "deepseek-official", "settingsNs": "llm-deepseek",
587
- "baseURL": "https://…", "apiKey": "可选(留空不修改)", "removeKey": false }
588
- 响应: { "ok": true, "provider": "…", "apiKeyRef": "…", "keyConfigured": true }
589
- ```
590
- - 经 `settings.mutate` 写配置 + `credentials.set` 存密钥(引用派生规则与 PC 端一致:`<PROVIDER>_API_KEY`)
591
- - 仅允许写入配置目录声明的命名空间(`400 unknown-provider`);`removeKey: true` 清除已存密钥
592
-
593
- ### 6.14 会话工具(v2.7:任务 / 子代理 / 目标,PC 端 GUI 同源)
594
- > 全部端点需鉴权;数据与 PC 端「任务 / 子代理 / 目标」同一内核服务,手机只读 + 简单操作。
595
-
596
- **GET `/m/api/jobs?sessionId=可选`** — 会话后台任务列表(不传 sessionId 返回全部)
597
- ```json
598
- { "ok": true, "sessionId": "…", "jobs": [
599
- { "id": "…", "kind": "task", "label": "…", "status": "running",
600
- "startedAt": 1786987923387, "finishedAt": 1786987923387 }
601
- ]}
602
- ```
603
- - 状态:`running / stopping / completed / failed`;任务视图与 SSE `session/jobs` 帧同源
604
- - 会话不存在 `404 session-not-found`;jobs 服务不可用 `503 jobs-unavailable`
605
-
606
- **POST `/m/api/jobs/kill`** — 取消任务
607
- ```json
608
- 请求: { "sessionId": "可选", "jobId": "…" }
609
- 响应: { "ok": true }
610
- ```
611
- - 映射内核 `jobs.kill(jobId, agent, reason)`;失败 `400 job-kill-failed`
612
-
613
- **GET `/m/api/subagents?parentSessionId=…`** — 子代理列表(按父会话)
614
- ```json
615
- { "ok": true, "parentAvailable": true, "catalogDegraded": true, "subagents": [
616
- { "id": "…", "kind": "child", "status": "running", "title": "…", "createdAt": 1730000000000 }
617
- ]}
618
- ```
619
- - 目录来源按优先级取:① 宿主持久目录 `ctx.subagents.listChildren(parentSessionId)` → ② 持久化枚举(父会话休眠/归档时入口不消失)→ ③ 会话注册表派生。
620
- **不调用 `subagents/list` Remote 端点**——该端点在宿主 0.2.0 已被删除
621
- - `kind`:`child`(普通子代理)或 `diagnostic`(目录 `mode: "unknown"` 的不受支持条目,此时 `status` 为原因 `unsupported`)
622
- - `status`:活 agent 的 AgentStatus 映射(`running`/`inactive`)或 diagnostic reason
623
- - `createdAt` 可选,用于排序;`catalogDegraded: true` 表示来源 ②/③ 不完整,客户端据此区分"确实没有子代理"与"目录读不到"
624
- - 缺参数 `400 parentSessionId-required`;三条路径都取不到会话 `404 session-not-found`
625
-
626
- **POST `/m/api/subagents/interrupt`** — 中断子代理
627
- ```json
628
- 请求: { "parentSessionId": "…", "childSessionId": "…" }
629
- 响应: { "ok": true }
630
- ```
631
- - 映射内核 `subagent.interrupt`(payload `{ parentSessionId, childSessionId, mode: "continuable" }`)
632
-
633
- **GET `/m/api/goal?sessionId=…`** — 当前目标(无目标返回 `{ "goal": null }`)
634
- ```json
635
- { "ok": true, "goal": { "id": "…", "revision": 1, "objective": "…",
636
- "phase": "active", "maxGoalRounds": 256, "roundsStarted": 0,
637
- "createdAt": 1786987923387, "updatedAt": 1786987923387, "activation": "armed" } }
638
- ```
639
- - `phase`: active / paused / blocked / complete;blocked 时含 `blockedReason`(如轮次耗尽)
640
- - 无会话 `503 goal-unavailable`
641
-
642
- **POST `/m/api/goal`** — 创建 / 暂停 / 继续 / 完成
643
- ```json
644
- 请求: { "action": "create|pause|resume|complete", "sessionId": "…",
645
- "objective": "create 必填", "maxGoalRounds": "create 可选" }
646
- 响应: { "ok": true }
647
- ```
648
- - 映射内核 goal RPC(契约一致:create 需 `sessionId + objective`;pause/resume/complete 需 `sessionId + ref`,ref 由插件经 `goals.get(agent)` 自动取得)
649
- - 缺参 `400 sessionId-required / objective-required`;无当前目标 `400 no-active-goal`;非法 action `400 bad-action`
650
-
651
- ### 6.15 斜杠命令(v2.8.0 引入 / v2.8.2 适配内核 0.1.1-rc.2)
652
-
653
- **GET `/m/api/commands?sessionId=…`** — 命令目录(对齐内核 `ctx.commands`)
654
- ```json
655
- { "ok": true, "commands": [
656
- { "name": "goal", "description": "set or view the goal for a long-running task",
657
- "input": { "hint": "[<objective>|clear|edit <objective>|pause|resume]", "attachments": true } }
658
- ] }
659
- ```
660
- - 命令条目:`name` / `description` 必填;`input`(`hint` / `attachments`)可选,有则返回——**原样透传**内核 `commands.list()` 的描述符,插件不改写字段
661
- - ⚠️ **字段名是 `attachments`,不是 `images`**(内核 `dsh-commands` 与 PC 端客户端均判定 `input.attachments === true`)。本文档 2026-10-04 修正,此前误写为 `images` —— 按旧文档实现会永远读不到该标志
662
- - `commands` 服务未注册(无 dsh-commands host 服务)→ `200 { ok, commands: [], unavailable: true }`(App 端弹「无可用命令」,不硬 503)
663
- - 会话不存在 → `404 session-not-found`;缺 `sessionId` → `400 bad-request`。**GET 要求会话已挂载**(`agents.get(sessionId)` 命中):休眠/未挂载会话返回 404 —— 这与 `/send`(会先自动 resume 再发)不同,客户端不能假设"能发消息就一定能列命令"
664
-
665
- **POST `/m/api/commands`** — 执行斜杠命令
666
- ```json
667
- 请求: { "sessionId": "…", "line": "/goal" }
668
- 响应: { "ok": true, "result": { "commandId": "cmd-…", "result": { "kind": "success", "text": "…" } } }
669
- ```
670
- - 映射内核 `commands.execute(agent, line, images, signal)`(0.1.1-rc.2 四参签名;本插件 `images` 恒为空数组,`signal` 为 15s 超时中止;2.8.1 的旧三参调用会把 AbortSignal 误传 images 槽,已改正)
671
- - 缺 `sessionId` → `400 missing-sessionId`(注意与 GET 的 `bad-request` **不同码**);`line` 必须以 `/` 开头(否则 `400 bad-request`);未知/畸形命令 `404 command-not-found`;服务未注册 `503 commands-unavailable`(带 detail);服务在而会话不存在 `404 session-not-found`(与 GET 拆分语义一致)
672
- - **会话必须已挂载**:`agents.get(sessionId)` 未命中即 404。休眠会话需先经 `/send` 唤醒,否则命令无法执行(与 `/send` 的自动 resume 语义不同)
673
- - `commands.execute` 外层套 `AbortSignal.timeout(15000)`:15s 是**整个命令**的上限。`/compact` 之类会等一次完整的 LLM 摘要,长会话可能超时 → `504 commands-execute-failed`;客户端此时**不能断言失败**(结果未知)
674
- - `result` 为内核 settle 对象(`commandId` + `result.{kind,text}`),与 PC 端一致
675
-
676
- ### 6.16 GET /m/api/account-usage(用量与额度,v3.2)
677
-
678
- 服务端并发查询三个固定来源,凭据只在电脑端解析和使用;手机端只接收成功来源的脱敏投影。默认使用电脑进程内 60 秒快照;详情页顶部刷新使用 `?refresh=1` 绕过快照并发起一轮新的查询(2 秒内重复刷新受保护)。来源固定按 DeepSeek → Codex → OpenCode Go 排列,未配置/未登录来源不返回,已配置但本次查询失败的来源也不返回。
679
-
680
- ```json
681
- {
682
- "ok": true,
683
- "fetchedAt": "2026-08-30T12:00:00.000Z",
684
- "availableCount": 3,
685
- "failedCount": 0,
686
- "sources": [
687
- { "id": "deepseek", "title": "DeepSeek", "kind": "balance",
688
- "amount": "12.50", "currency": "CNY", "available": true },
689
- { "id": "codex", "title": "Codex", "kind": "quota",
690
- "account": { "displayName": "Personal", "maskedEmail": "p***@example.com" },
691
- "windows": [
692
- { "window": "5h", "remainingPercent": 72, "resetAt": "2026-08-30T13:00:00.000Z" },
693
- { "window": "weekly", "remainingPercent": 64 }
694
- ],
695
- "credits": { "unlimited": false, "balance": "8.00" },
696
- "individualLimit": { "limit": "100", "used": "28", "remaining": "72", "remainingPercent": 72 }
697
- },
698
- { "id": "opencode-go", "title": "OpenCode Go", "kind": "quota",
699
- "windows": [
700
- { "window": "5h", "remainingPercent": 91 },
701
- { "window": "weekly", "remainingPercent": 83 },
702
- { "window": "monthly", "remainingPercent": 76 }
703
- ]
704
- }
705
- ]
706
- }
707
- ```
708
-
709
- - Codex 通过 `dsh-codex-connect` 的 `OpenAICodexCredentialStore`、`openAICodexAuthStatus` 与 `readOpenAICodexRateLimits` 查询同一份当前活动账户快照;若 Codex 配置启用代理,额度请求复用其 `OpenAICodexProxyManager`,代理不可用时不直连;不读取/复制 OAuth 文件,不返回 token。
710
- - OpenCode Go 优先读取 `llm-pi-ai` 的 `opencode-go.apiKeyEnv`,其次读取 `llm-pi-ai/opencode-go` API-key record,再回退 `OPENCODE_GO_API_KEY` / `OPENCODE_API_KEY`;请求固定发送到官方 `GET https://opencode.ai/zen/go/v1/usage`,不接受任意 baseURL。`percent` 转换为 `remainingPercent = 100 - percent`,`rate-limited` 窗口仍保留并标记 `limited: true`。
711
- - `failedCount` 只统计“已配置但本次查询失败”的来源;未配置来源不算失败。`failedCount > 0` 时 App 显示汇总提示,不显示失败来源细节。
712
- - 查询快照在电脑进程内缓存 60 秒并共享并发请求;手动 `refresh=1` 的连点在 2 秒内复用快照;不写手机或电脑持久化文件。配额窗口不相加,有限 Credits 和个人消费上限分别展示。
713
- - HTTP 错误只返回通用 `502 account-usage-failed`(单来源失败不会使整体请求失败)。
714
-
715
- **悬浮球面板消费语义(v3.2)**:原生悬浮球面板把本端点当作「展开时按需读取」的快照——每次展开面板最多触发一次查询(客户端 2 分钟节流,不并入面板打开期间的 5 秒轮询,也不做后台周期轮询);成功快照在进程内保留,失败时沿用旧值并标注相对时间;投影不可用(旧插件 404 / 未配置 / 全部失败)时,面板整体降级为原有的单行 DeepSeek 余额。面板按「一个来源一行」渲染:金额来源保留文字(CNY 优先),配额来源只显示主 bucket 窗口的细条与颜色、不出百分比数字,每个进度条上方居中显示窗口短标签(5h / 每周 / 每月,与详情页窗口语义一致);附加 bucket(服务端命名的 `名称 · 5h`)与账户身份不出现 overlay 面板,详情页仍是完整信息入口(`displayName`/`maskedEmail`、Credits、个人消费上限、重置时间)。
716
-
717
- ### 6.17 文件传输(v3.1.2,B站 csborbbnc 反馈)
718
-
719
- **GET `/m/api/files?path=…`** — 下载电脑文件
720
- - 响应:`200` 文件流(`content-type` 按扩展名推断、`content-disposition: attachment` 带 UTF-8 文件名);路径不存在或非文件 → `404 file-not-found`;缺 `path` → `400 bad-request`
721
- - 与目录选择器(§6.1)同信任模型:口令鉴权 + 现有限流,路径由手机显式指定
722
-
723
- **POST `/m/api/files/upload`** — 上传文件到电脑(写会话工作目录)
724
- ```json
725
- 请求: { "sessionId": "…(可选)", "name": "README.md", "data": "<base64>" }
726
- 响应: { "ok": true, "path": "F:\\DSH-Outpost\\README.md", "bytes": 1234 }
727
- ```
728
- - 目标目录:`sessionId` 对应 agent 的工作目录 → 缺省回退第一个注册工作区根;无法确定 → `503 no-workspace`
729
- - `name` 不合法(含 `\ / : * ? " < > |`、`.`/`..`/超 255)→ `400 invalid-name`;body 上限 64MB → `413 payload-too-large`
730
-
731
- ### 6.18 GET /m/api/turn-outline(轮次大纲,轮次导航)
732
-
733
- > 只读端点:把宿主 `turnOutline` 投影(电脑端刻度轨消费的同一份轮次摘要)**薄透出**为移动端契约。插件只做投影读取与形状稳定化,**不自己折叠会话日志**——轮次边界与预览文字只有宿主一个权威来源,避免桌面端与手机端在边界条件上出现两种答案。不改变会话内容/轮次边界,不写任何持久状态,不新增会话事件类型。
734
-
735
- **查询参数**:`sessionId`(必填;缺 → `400 bad-request`)。
736
-
737
- **响应 200(可用态)**
738
-
739
- ```json
740
- {
741
- "ok": true,
742
- "sessionId": "session-abc",
743
- "state": "available",
744
- "asOfSeq": 12873,
745
- "turns": [
746
- { "turn": 1, "seq": 2048, "prompt": "帮我跑一下测试", "response": "测试结果……" },
747
- { "turn": 2, "seq": 5121, "prompt": "", "response": "纯命令轮的回复" }
748
- ],
749
- "truncated": false,
750
- "dropped": 0
751
- }
752
- ```
753
-
754
- | 字段 | 类型 | 说明 |
755
- |---|---|---|
756
- | `state` | string | `available` / `empty` / `capability-missing` / `read-failed`(四态见下) |
757
- | `asOfSeq` | number \| null | 该投影快照对应的 durable 事件序号;`null` 表示无法确定 |
758
- | `turns` | array | 轮次摘要,按 `turn` 升序;元素字段见下 |
759
- | `turns[].turn` | number | 轮次号(从 1 开始) |
760
- | `turns[].seq` | number | 该轮起始事件(`turn/start`)的 durable 序号,作定位锚点 |
761
- | `turns[].prompt` | string | 该轮提示词预览;可能为空(纯图片/纯命令轮) |
762
- | `turns[].response` | string | 该轮回复摘要;可能为空(该轮尚无回复) |
763
- | `truncated` | boolean | 是否因体积上限截断(仅 `available` 有意义) |
764
- | `dropped` | number | 被截掉的轮次数;未截断为 `0` |
765
-
766
- **四态必须显式区分,空数组不得表示「没有轮次」**(同一份投影在不同宿主/会话上的语义差别很大):
767
-
768
- | `state` | 含义 | 客户端(刻度轨)行为 |
769
- |---|---|---|
770
- | `available` | 能力可用且该会话有大纲 | 与「已加载轮次」取并集画完整刻度;未加载轮次可点击(先跨页加载再定位) |
771
- | `empty` | 能力可用,但该会话暂无大纲(新会话/投影尚未生成) | 退回「只用已加载轮次」的降级行为,不打扰用户 |
772
- | `capability-missing` | 宿主未注册 `turnOutline` 投影单元,或没有投影感知观察面(**全局能力缺失**) | 同 `empty`;**不得**按宿主版本号自行推断 |
773
- | `read-failed` | 读取失败;带稳定 `code` 与 `degraded: true` | 退回降级行为,在刻度轨可见时说明一次原因 |
774
-
775
- `read-failed` 的稳定 `code`:
776
-
777
- | `code` | 场景 |
778
- |---|---|
779
- | `turn-outline-timeout` | 有界等待超时(默认 5000 ms,见下「有界与截断」;从请求到达起算、**包含排队时间**) |
780
- | `turn-outline-busy` | 冷折叠执行槽队列已满(并发 1、队列上限 8),立刻明确降级而不是无限排队 |
781
- | `turn-outline-unavailable` | 宿主观察面无法回放完整日志(seeded 前缀校验缺陷等已知兼容问题) |
782
-
783
- 读取失败响应形状(HTTP 仍为 200;这是**明确的降级结果**,不是错误):
784
-
785
- ```json
786
- { "ok": true, "sessionId": "session-abc", "state": "read-failed", "code": "turn-outline-timeout", "degraded": true, "turns": [] }
787
- ```
788
-
789
- > 旧宿主路径(无投影感知观察面)返回的 `capability-missing` 形状为 `{ "ok": true, "sessionId": …, "state": "capability-missing", "turns": [] }`,可能不含 `asOfSeq`/`truncated`/`dropped`;客户端应按 `state` 分支,不依赖字段恒在。
790
-
791
- **HTTP 错误码**:
792
-
793
- | HTTP | error 值 | 场景 |
794
- |---|---|---|
795
- | 400 | `bad-request` | 缺 `sessionId` |
796
- | 401 | `auth-required` / `bad-token` | 未提供 / 口令错误(见 §1) |
797
- | 403 | `host-not-allowed` | Host 不在白名单(见 docs/04) |
798
- | 404 | `session-not-found` | 会话**明确不存在**(只有这一种情况才 404) |
799
- | 405 | `method-not-allowed` | 非 GET(HEAD 与其它 GET 端点一致:路由放行、无响应体) |
800
- | 500 | `session-corrupt` / `event-read-failed` | 会话数据损坏 / 其它读取异常;错误响应不泄露主机路径或原始异常。**明确的超时/繁忙/已知兼容降级不走 5xx**,而是上表的 200 + `read-failed` |
801
-
802
- **有界与截断**:
803
-
804
- - **有界等待**:配置项 `turnOutlineTimeoutMs`(schema 默认 **5000 ms**;冷折叠单会话最坏实测 1.8 s,约 3× 余量)。截止后立即向仍连接的客户端回 `read-failed`;底层未结束的观察**不强行打断**(同步折叠不可中断),继续占住执行槽直到结束,迟到的投影 lease 随即释放、**不会回写成功**。客户端断开同样取消请求且之后不再写响应。
805
- - **冷折叠串行**:并发 1 + 队列上限 8(3 路并发实测 RSS 峰值 831 MB、耗时近似叠加,故不允许无界并发);队列满立刻回 `turn-outline-busy`。
806
- - **按体积截断**:下发上限 **256 KB**(实测最大 77 KB / 288 轮、p99 29.5 KB)。从**最新**往回保留、至少保留 1 轮;超出部分丢弃并显式标 `truncated: true` / `dropped: N`——绝不静默截断,也不回空数组(回空数组会被读成「没有轮次」)。
807
- - 实现使用宿主的投影感知观察面 `sessionQuery.observeSession(sessionId, { projectionMode: "all", signal })`;读到即释放 lease(不泄漏 prepared 会话 pin)。
808
-
809
- **客户端消费约定(App 刻度轨)**:合并「已加载轮次」与 `turns`——按轮次号升序取并集,两边都有时保留已加载锚点、预览以已加载为准(仅本地为空时用大纲补齐,保证同一轮加载前后文字不跳变);未加载轮次以短淡刻度呈现,点击后按 **200 条/页**有界跨页加载直至覆盖该轮起始序号,再走既有的按序号跳转定位;`empty` / `capability-missing` 不提示用户,`read-failed` / `truncated` 在刻度轨可见时说明一次。
810
-
811
- **能力位**:`GET /m/api/bootstrap` 与 SSE hello 的 `capabilities.turnOutline` 与本节同一判据(见 §3.1、§3.6):宿主真的注册了投影单元且观察面可用才 `supported: true`,否则 `supported: false`。客户端必须先读能力位再决定是否渲染未加载刻度。
812
-
813
-
814
-
815
-
816
-