dsh-zen-remote 1.1.13 → 1.1.14

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 (77) hide show
  1. package/README.md +3 -2
  2. package/README.zh-CN.md +3 -2
  3. package/docs/interface.md +46 -3
  4. package/lib/client.js +3421 -103
  5. package/lib/index.js +13 -0
  6. package/lib/index.js.map +1 -1
  7. package/lib/share-export.js +347 -0
  8. package/lib/share-export.js.map +1 -0
  9. package/lib/types/client/MobileAttachButton.d.ts +8 -1
  10. package/lib/types/client/MobileAttachButton.d.ts.map +1 -1
  11. package/lib/types/client/MobileAttachChips.d.ts.map +1 -1
  12. package/lib/types/client/MobileSessionHeader.d.ts +7 -8
  13. package/lib/types/client/MobileSessionHeader.d.ts.map +1 -1
  14. package/lib/types/client/MobileSessionInfo.d.ts.map +1 -1
  15. package/lib/types/client/effects/keyboard-avoid.d.ts.map +1 -1
  16. package/lib/types/client/effects/keyboard-guard.d.ts +16 -11
  17. package/lib/types/client/effects/keyboard-guard.d.ts.map +1 -1
  18. package/lib/types/client/host-attach.d.ts +38 -0
  19. package/lib/types/client/host-attach.d.ts.map +1 -0
  20. package/lib/types/client/index.d.ts.map +1 -1
  21. package/lib/types/client/locales.d.ts +23 -0
  22. package/lib/types/client/locales.d.ts.map +1 -1
  23. package/lib/types/client/share/fetch-share.d.ts +77 -0
  24. package/lib/types/client/share/fetch-share.d.ts.map +1 -0
  25. package/lib/types/client/share/markdown.d.ts +97 -0
  26. package/lib/types/client/share/markdown.d.ts.map +1 -0
  27. package/lib/types/client/share/png-stitch.d.ts +96 -0
  28. package/lib/types/client/share/png-stitch.d.ts.map +1 -0
  29. package/lib/types/client/share/rasterize.d.ts +317 -0
  30. package/lib/types/client/share/rasterize.d.ts.map +1 -0
  31. package/lib/types/client/share/share-card.d.ts +93 -0
  32. package/lib/types/client/share/share-card.d.ts.map +1 -0
  33. package/lib/types/client/share/share-constants.d.ts +27 -0
  34. package/lib/types/client/share/share-constants.d.ts.map +1 -0
  35. package/lib/types/client/share/share-flow.d.ts +76 -0
  36. package/lib/types/client/share/share-flow.d.ts.map +1 -0
  37. package/lib/types/client/share/share-preview.d.ts +11 -0
  38. package/lib/types/client/share/share-preview.d.ts.map +1 -0
  39. package/lib/types/client/share/share-sheet.d.ts +17 -0
  40. package/lib/types/client/share/share-sheet.d.ts.map +1 -0
  41. package/lib/types/client/share/text-layout.d.ts +48 -0
  42. package/lib/types/client/share/text-layout.d.ts.map +1 -0
  43. package/lib/types/client/styles/header.css.d.ts +1 -1
  44. package/lib/types/client/styles/header.css.d.ts.map +1 -1
  45. package/lib/types/client/styles/layout.css.d.ts +1 -1
  46. package/lib/types/client/styles/layout.css.d.ts.map +1 -1
  47. package/lib/types/index.d.ts.map +1 -1
  48. package/lib/types/share-export.d.ts +66 -0
  49. package/lib/types/share-export.d.ts.map +1 -0
  50. package/package.json +7 -2
  51. package/scripts/check-host-shadow.mjs +41 -9
  52. package/scripts/check-keyboard-avoid.mjs +4 -2
  53. package/scripts/check-share-image.mjs +970 -0
  54. package/scripts/check-upload-endpoint.mjs +10 -3
  55. package/src/client/MobileAttachButton.tsx +13 -0
  56. package/src/client/MobileAttachChips.tsx +8 -1
  57. package/src/client/MobileSessionHeader.tsx +90 -30
  58. package/src/client/MobileSessionInfo.tsx +234 -1
  59. package/src/client/effects/keyboard-avoid.ts +14 -7
  60. package/src/client/effects/keyboard-guard.ts +28 -18
  61. package/src/client/host-attach.ts +54 -0
  62. package/src/client/index.tsx +16 -0
  63. package/src/client/locales.ts +46 -0
  64. package/src/client/share/fetch-share.ts +161 -0
  65. package/src/client/share/markdown.ts +609 -0
  66. package/src/client/share/png-stitch.ts +281 -0
  67. package/src/client/share/rasterize.ts +1078 -0
  68. package/src/client/share/share-card.tsx +637 -0
  69. package/src/client/share/share-constants.ts +30 -0
  70. package/src/client/share/share-flow.ts +166 -0
  71. package/src/client/share/share-preview.tsx +337 -0
  72. package/src/client/share/share-sheet.tsx +222 -0
  73. package/src/client/share/text-layout.ts +147 -0
  74. package/src/client/styles/header.css.ts +30 -0
  75. package/src/client/styles/layout.css.ts +5 -2
  76. package/src/index.ts +13 -0
  77. package/src/share-export.ts +431 -0
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Share-image transcript route (issue #7): fold one session's raw event log
3
+ * into the flat user/assistant transcript the phone rasterizes into shareable
4
+ * PNG cards.
5
+ *
6
+ * Why a host route at all: the Chat view is a paged window over a surface the
7
+ * compactor rewrites, so the browser DOM never holds the whole conversation
8
+ * and cloning it cannot work. The whole-log source of truth is the host-side
9
+ * `sessionQuery` service (standard dsh-base composition), which no browser
10
+ * half can reach.
11
+ *
12
+ * Why the RAW log (`readSession`) and not the observed surface: the surface
13
+ * deliberately shadows replaced ranges, so once a replacement lands it shows
14
+ * LESS than the user already saw — wrong source for a human transcript (the
15
+ * official trap note on `isAppendSurfaceEvent`: append-origin events are the
16
+ * transcript's durable source material). The raw log additionally carries the
17
+ * `agent/inbox/spliced` bookkeeping the steering classification below folds —
18
+ * it never exists on the surface. The fold keeps every append-origin message
19
+ * in log order and never applies the shadows, so a steered or compacted
20
+ * session still exports everything the user saw.
21
+ */
22
+ import type { IncomingMessage, ServerResponse } from 'node:http';
23
+ import type { Context } from '@deepseek-ai/cordis';
24
+ /** Exact route the browser GETs a session's share transcript from. */
25
+ export declare const SHARE_EXPORT_ROUTE = "/_dsh/mobile-nav/share-export";
26
+ /** Upper bound on `turns` for `range=last` (PLAN §4). 500 exchanges is
27
+ * already more wall of text than any share card wants, and the cap keeps a
28
+ * careless `turns=99999999` from quietly meaning "the whole log". */
29
+ export declare const MAX_SHARE_TURNS = 500;
30
+ /** One exportable content block of a transcript row. */
31
+ export type ShareBlock = {
32
+ kind: 'text';
33
+ text: string;
34
+ } | {
35
+ kind: 'image';
36
+ };
37
+ /** One transcript row. `seq` is the source event's log position — stable
38
+ * across re-renders and usable as a React key; a range=last body keeps whole
39
+ * turns, so its seqs always arrive turn-contiguous. */
40
+ export interface ShareTurn {
41
+ role: 'user' | 'assistant';
42
+ seq: number;
43
+ blocks: ShareBlock[];
44
+ }
45
+ /** Response body of a successful export (PLAN §4 contract). */
46
+ export interface ShareExportBody {
47
+ ok: true;
48
+ /** Session creation time (Unix epoch ms) from the observed header. */
49
+ createdAt: number;
50
+ turns: ShareTurn[];
51
+ /** Constant false while the server never truncates; the client's slice
52
+ * budget is the only truncation that exists. */
53
+ truncated: false;
54
+ }
55
+ /**
56
+ * Handle one `GET {@link SHARE_EXPORT_ROUTE}?session=<id>&range=all|last&turns=N`
57
+ * request.
58
+ *
59
+ * Exported so an integration test can drive it with a plain node:http server
60
+ * and a fake sessionQuery service instead of booting a harness.
61
+ * @param ctx - host context carrying the sessionQuery service and logger.
62
+ * @param req - inbound request; no body is read.
63
+ * @param res - the response this call owns end to end.
64
+ */
65
+ export declare function handleShareExport(ctx: Context, req: IncomingMessage, res: ServerResponse): Promise<void>;
66
+ //# sourceMappingURL=share-export.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"share-export.d.ts","sourceRoot":"","sources":["../../src/share-export.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAChE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAWlD,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,kCAAkC,CAAA;AAEjE;;qEAEqE;AACrE,eAAO,MAAM,eAAe,MAAM,CAAA;AAElC,wDAAwD;AACxD,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAA;AAErB;;uDAEuD;AACvD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,GAAG,WAAW,CAAA;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,UAAU,EAAE,CAAA;CACrB;AAaD,+DAA+D;AAC/D,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,IAAI,CAAA;IACR,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,SAAS,EAAE,CAAA;IAClB;oDACgD;IAChD,SAAS,EAAE,KAAK,CAAA;CACjB;AAoTD;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAoC9G"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-zen-remote",
3
- "version": "1.1.13",
3
+ "version": "1.1.14",
4
4
  "description": "把 DeepSeek Harness 变成手机 App:移动端界面重排(两级页面栈、会话列表主屏、composer 重排、手势、附件上传)+ 配对码认证网关 + PWA 安装 + 真 Web Push。Complete mobile plugin for DeepSeek Harness (DSH): app-shell mobile UI, pairing-code gateway for public-internet access behind your own reverse proxy, installable PWA, offline service worker, and real Web Push agent-done notifications.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -88,6 +88,7 @@
88
88
  "@deepseek-ai/dsh-host-webserver": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.0 <0.2.0-0 || >=0.1.2-rc.0 <0.2.0-0",
89
89
  "@deepseek-ai/dsh-session": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.0 <0.2.0-0 || >=0.1.2-rc.0 <0.2.0-0",
90
90
  "@deepseek-ai/dsh-session-log-export": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.0 <0.2.0-0 || >=0.1.2-rc.0 <0.2.0-0",
91
+ "@deepseek-ai/dsh-session-query": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.0 <0.2.0-0 || >=0.1.2-rc.0 <0.2.0-0",
91
92
  "react": "^18.2.0",
92
93
  "@deepseek-ai/dsh-tools": ">=0.0.1-rc.1 <0.1.0 || >=0.1.0-rc.1 <0.2.0-0 || >=0.1.2-rc.0 <0.2.0-0",
93
94
  "@deepseek-ai/dsh-workspace": ">=0.1.1-rc.0 <0.2.0-0 || >=0.1.2-rc.0 <0.2.0-0"
@@ -99,6 +100,9 @@
99
100
  "@deepseek-ai/dsh-session": {
100
101
  "optional": true
101
102
  },
103
+ "@deepseek-ai/dsh-session-query": {
104
+ "optional": true
105
+ },
102
106
  "@deepseek-ai/dsh-tools": {
103
107
  "optional": true
104
108
  },
@@ -128,6 +132,7 @@
128
132
  "@deepseek-ai/dsh-scope": "0.1.2-rc.1",
129
133
  "@deepseek-ai/dsh-session": "0.1.2-rc.1",
130
134
  "@deepseek-ai/dsh-session-log-export": "0.1.2-rc.1",
135
+ "@deepseek-ai/dsh-session-query": "0.1.2-rc.1",
131
136
  "@deepseek-ai/dsh-session-stats": "0.1.2-rc.1",
132
137
  "@deepseek-ai/dsh-subagent": "0.1.2-rc.1",
133
138
  "@deepseek-ai/dsh-timeout": "0.1.2-rc.1",
@@ -146,7 +151,7 @@
146
151
  "scripts": {
147
152
  "build": "tsc -p tsconfig.json && tsc -p tsconfig.client.json && node scripts/build-client.mjs",
148
153
  "verify": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.client.json --noEmit",
149
- "test": "node --test \"test/*.test.cjs\" && node scripts/check-sunk-viewport.mjs && node scripts/check-keyboard-avoid.mjs && node scripts/check-attach-upload.mjs && node scripts/check-upload-endpoint.mjs && node scripts/check-host-shadow.mjs && node scripts/check-compat-store.mjs && node scripts/check-client-externals.mjs && node scripts/check-info-sheet-stacking.mjs && node scripts/sync-doc-version.mjs --check",
154
+ "test": "node --test \"test/*.test.cjs\" && node scripts/check-sunk-viewport.mjs && node scripts/check-keyboard-avoid.mjs && node scripts/check-attach-upload.mjs && node scripts/check-share-image.mjs && node scripts/check-upload-endpoint.mjs && node scripts/check-host-shadow.mjs && node scripts/check-compat-store.mjs && node scripts/check-client-externals.mjs && node scripts/check-info-sheet-stacking.mjs && node scripts/sync-doc-version.mjs --check",
150
155
  "version": "node scripts/sync-doc-version.mjs && git add README.md README.zh-CN.md"
151
156
  }
152
157
  }
@@ -8,12 +8,13 @@
8
8
  // means the host half silently loads whatever version this plugin last
9
9
  // installed — "it links and starts" proves nothing.
10
10
  //
11
- // Instead of hand-maintaining a package list, the three files the host half
12
- // actually loads (dsh-push.mjs, lan-gate.mjs, lib/index.js) are scanned for
13
- // @deepseek-ai specifiers — static import, dynamic import() and require() —
14
- // so a new import added there gets watched automatically. src/index.ts is
15
- // type-only imports (erased at runtime) and the gateway subprocess uses only
16
- // stdlib + web-push, so this scan is the whole runtime story.
11
+ // Instead of hand-maintaining a package list, the files the host half
12
+ // actually loads (dsh-push.mjs, lan-gate.mjs, lib/index.js and the modules
13
+ // it statically pulls in) are scanned for @deepseek-ai specifiers — static
14
+ // import, dynamic import() and require() — so a new import added there gets
15
+ // watched automatically. src/index.ts is type-only imports (erased at
16
+ // runtime) and the gateway subprocess uses only stdlib + web-push, so this
17
+ // scan is the whole runtime story.
17
18
  //
18
19
  // Run: node scripts/check-host-shadow.mjs
19
20
  import assert from 'node:assert/strict'
@@ -47,15 +48,46 @@ const require = createRequire(import.meta.url)
47
48
  // first, or temporarily accept the drift).
48
49
  // - npm-published tarballs contain no node_modules, so this shadow only
49
50
  // exists on dev machines where the plugin is link-installed.
50
- const SHADOW_ALLOWED = ['@deepseek-ai/dsh-tools']
51
+ //
52
+ // @deepseek-ai/dsh-session:
53
+ // - Why allowed at all: src/share-export.ts (the share-image transcript
54
+ // route) statically imports the two pure fold helpers
55
+ // (isAppendSurfaceEvent, deriveEventMessage) from the browser-safe
56
+ // `@deepseek-ai/dsh-session/surface` subpath. They are THE canonical
57
+ // per-node projection rule per their own doc; reimplementing them locally
58
+ // would be a second fold that silently drifts, and no host service exposes
59
+ // them, so a module import is the only way to reuse them.
60
+ // - Why 0.1.2-rc.1: same release line as every other devDep; the package is
61
+ // also needed in node_modules for pnpm verify's type check regardless. The
62
+ // shadow only bites the two pure functions — both take plain event records
63
+ // and touch no class identity, and their signatures are identical on the
64
+ // 0.1.2 and 0.1.5 lines — so a version-skewed copy cannot diverge the fold
65
+ // the way a service-class mismatch could.
66
+ // The fact BEHIND that "signatures identical" conclusion, verified against
67
+ // both closures (2026-09-10): the SURFACE_EVENT_TYPES set differs across
68
+ // the lines — 0.1.2-rc.1 admits user/message, assistant/message,
69
+ // tool/result, while 0.1.5-rc.2 also admits system/message (and its
70
+ // deriveEventMessage projects it to a system-role message). The fold in
71
+ // src/share-export.ts neutralizes the difference: its role filter keeps
72
+ // only 'user'/'assistant' rows, so a system/message is dropped on 0.1.5 by
73
+ // the filter and on 0.1.2 by isAppendSurfaceEvent itself — the exported
74
+ // transcript is byte-identical either way. A future DSH that adds another
75
+ // role-bearing surface type must re-run this equivalence, not assume it.
76
+ // - WHEN TO ACT: a future DSH that renames the surface subpath or changes
77
+ // event shapes will break this import loudly at plugin load; re-pin the
78
+ // devDep then.
79
+ // - npm-published tarballs contain no node_modules, so this shadow only
80
+ // exists on dev machines where the plugin is link-installed.
81
+ const SHADOW_ALLOWED = ['@deepseek-ai/dsh-tools', '@deepseek-ai/dsh-session']
51
82
 
52
83
  // Packages that must never exist in this plugin's node_modules at all: DSH
53
84
  // 0.1.2 dropped them, and a stale copy would let the type check quietly pass
54
85
  // against outdated declarations. NOT exemptible via SHADOW_ALLOWED.
55
86
  const BANNED_PACKAGES = ['@deepseek-ai/dsh-client-runtime', '@deepseek-ai/dsh-host-apiproxy']
56
87
 
57
- // The three files the host half actually loads.
58
- const HOST_ENTRY_FILES = ['dsh-push.mjs', 'lan-gate.mjs', 'lib/index.js']
88
+ // The files the host half actually loads. lib/share-export.js is statically
89
+ // imported by lib/index.js, so it is part of the same runtime graph.
90
+ const HOST_ENTRY_FILES = ['dsh-push.mjs', 'lan-gate.mjs', 'lib/index.js', 'lib/share-export.js']
59
91
 
60
92
  // Matches the specifier of real module statements only — `... from 'pkg'`,
61
93
  // `import 'pkg'`, `import('pkg')`, `require('pkg')` — so @deepseek-ai names
@@ -1,12 +1,14 @@
1
1
  // Self-check for the composer keyboard-avoid lift (S10, issue #1 方案 2).
2
2
  // The failing environment (小米 + 微信输入法) cannot be reproduced off-device,
3
3
  // so the boundary cases of the pure geometry are asserted numerically.
4
- // Run: node scripts/check-keyboard-avoid.mjs (needs Node >= 23.6 type stripping)
4
+ // Run: node scripts/check-keyboard-avoid.mjs (needs Node >= 23.6 type stripping;
5
+ // clamped comes from lib/index.js — the host source is multi-file since the
6
+ // share-export route, so strip-only cannot load src/index.ts. Build first.)
5
7
  import assert from 'node:assert/strict'
6
8
  import { readFileSync } from 'node:fs'
7
9
  import { composerLift, estimatedLift, keyboardLift as lift, safetyPad } from '../src/client/effects/keyboard-avoid.ts'
8
10
  import { KEYBOARD_DEFAULTS, parseClientConfig } from '../src/client/client-config.ts'
9
- import { clamped } from '../src/index.ts'
11
+ import { clamped } from '../lib/index.js'
10
12
 
11
13
  // The reported class of bug: keyboard shrinks the visual viewport, browser
12
14
  // does not pan it — the whole shrink is an occluded band at the bottom.