dsh-zen-remote 1.1.15 → 2.0.0
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.
- package/AGENTS.md +54 -9
- package/README.md +249 -190
- package/README.zh-CN.md +248 -202
- package/assets/home.png +0 -0
- package/assets/info.png +0 -0
- package/assets/pairing.png +0 -0
- package/assets/session.png +0 -0
- package/assets/sheet.png +0 -0
- package/cordis.patch.yml +12 -11
- package/cordis.patch.yml.example +24 -9
- package/docs/interface.md +82 -4
- package/docs/remote-access.en.md +99 -58
- package/docs/remote-access.md +99 -42
- package/dsh-push.mjs +67 -54
- package/lan-gate.mjs +88 -20
- package/lib/activity.js +168 -0
- package/lib/activity.js.map +1 -0
- package/lib/admin-routes.js +526 -0
- package/lib/admin-routes.js.map +1 -0
- package/lib/client-pairing.js +224 -0
- package/lib/client-pairing.js.map +1 -0
- package/lib/client-routes.js +674 -0
- package/lib/client-routes.js.map +1 -0
- package/lib/client.js +11268 -5844
- package/lib/config.js +346 -0
- package/lib/config.js.map +1 -0
- package/lib/fetch-route-intercept.js +418 -0
- package/lib/fetch-route-intercept.js.map +1 -0
- package/lib/fingerprint.js +363 -0
- package/lib/fingerprint.js.map +1 -0
- package/lib/http.js +69 -0
- package/lib/http.js.map +1 -0
- package/lib/index.js +675 -54
- package/lib/index.js.map +1 -1
- package/lib/intercept-shape.js +81 -0
- package/lib/intercept-shape.js.map +1 -0
- package/lib/intercept.js +3040 -0
- package/lib/intercept.js.map +1 -0
- package/lib/lan-gate-server.cjs +532 -121
- package/lib/merge-streams.js +1165 -0
- package/lib/merge-streams.js.map +1 -0
- package/lib/relay-access.js +496 -0
- package/lib/relay-access.js.map +1 -0
- package/lib/relay-client.js +1303 -0
- package/lib/relay-client.js.map +1 -0
- package/lib/relay-filter.js +282 -0
- package/lib/relay-filter.js.map +1 -0
- package/lib/relay-server.js +1792 -0
- package/lib/relay-server.js.map +1 -0
- package/lib/restart-fields.js +34 -0
- package/lib/restart-fields.js.map +1 -0
- package/lib/restart-watcher.js +106 -0
- package/lib/restart-watcher.js.map +1 -0
- package/lib/session-reference.js +157 -0
- package/lib/session-reference.js.map +1 -0
- package/lib/share-export.js +16 -1
- package/lib/share-export.js.map +1 -1
- package/lib/share-ops.js +86 -0
- package/lib/share-ops.js.map +1 -0
- package/lib/share-store.js +294 -0
- package/lib/share-store.js.map +1 -0
- package/lib/types/activity.d.ts +99 -0
- package/lib/types/activity.d.ts.map +1 -0
- package/lib/types/admin-routes.d.ts +129 -0
- package/lib/types/admin-routes.d.ts.map +1 -0
- package/lib/types/client/MobileDrawerFooter.d.ts.map +1 -1
- package/lib/types/client/MobileHome.d.ts +1 -1
- package/lib/types/client/MobileHome.d.ts.map +1 -1
- package/lib/types/client/MobileHomeChips.d.ts +1 -1
- package/lib/types/client/MobileHomeChips.d.ts.map +1 -1
- package/lib/types/client/MobileSessionHeader.d.ts +19 -8
- package/lib/types/client/MobileSessionHeader.d.ts.map +1 -1
- package/lib/types/client/MobileSessionInfo.d.ts +20 -3
- package/lib/types/client/MobileSessionInfo.d.ts.map +1 -1
- package/lib/types/client/RemoteComposerBanner.d.ts +42 -0
- package/lib/types/client/RemoteComposerBanner.d.ts.map +1 -0
- package/lib/types/client/RemoteHeaderIcon.d.ts +37 -0
- package/lib/types/client/RemoteHeaderIcon.d.ts.map +1 -0
- package/lib/types/client/RemoteShareMenu.d.ts +28 -0
- package/lib/types/client/RemoteShareMenu.d.ts.map +1 -0
- package/lib/types/client/RemoteStatusIcon.d.ts +36 -0
- package/lib/types/client/RemoteStatusIcon.d.ts.map +1 -0
- package/lib/types/client/compat/desktop.d.ts +21 -0
- package/lib/types/client/compat/desktop.d.ts.map +1 -0
- package/lib/types/client/compat/types.d.ts +57 -0
- package/lib/types/client/compat/types.d.ts.map +1 -1
- package/lib/types/client/current-session-report.d.ts +37 -0
- package/lib/types/client/current-session-report.d.ts.map +1 -0
- package/lib/types/client/effects/gestures.d.ts.map +1 -1
- package/lib/types/client/effects/header-status.d.ts.map +1 -1
- package/lib/types/client/effects/keyboard-guard.d.ts.map +1 -1
- package/lib/types/client/effects/model-sheet-extras.d.ts.map +1 -1
- package/lib/types/client/effects/permission-sheet.d.ts +39 -0
- package/lib/types/client/effects/permission-sheet.d.ts.map +1 -0
- package/lib/types/client/effects/remote-session.d.ts +25 -0
- package/lib/types/client/effects/remote-session.d.ts.map +1 -0
- package/lib/types/client/effects/turn-fold.d.ts +9 -0
- package/lib/types/client/effects/turn-fold.d.ts.map +1 -1
- package/lib/types/client/effects/workbench-ref-close.d.ts +2 -1
- package/lib/types/client/effects/workbench-ref-close.d.ts.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/locales.d.ts +187 -2
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/remote-fetch.d.ts +51 -0
- package/lib/types/client/remote-fetch.d.ts.map +1 -0
- package/lib/types/client/remote-group-expansion.d.ts +41 -0
- package/lib/types/client/remote-group-expansion.d.ts.map +1 -0
- package/lib/types/client/remote-share-css.d.ts +19 -0
- package/lib/types/client/remote-share-css.d.ts.map +1 -0
- package/lib/types/client/remote-share-register.d.ts +113 -0
- package/lib/types/client/remote-share-register.d.ts.map +1 -0
- package/lib/types/client/remote-status-css.d.ts +15 -0
- package/lib/types/client/remote-status-css.d.ts.map +1 -0
- package/lib/types/client/remote-status-register.d.ts +46 -0
- package/lib/types/client/remote-status-register.d.ts.map +1 -0
- package/lib/types/client/settings/SettingsSection.d.ts +46 -0
- package/lib/types/client/settings/SettingsSection.d.ts.map +1 -0
- package/lib/types/client/settings/register-settings.d.ts +58 -0
- package/lib/types/client/settings/register-settings.d.ts.map +1 -0
- package/lib/types/client/settings/settings-css.d.ts +10 -0
- package/lib/types/client/settings/settings-css.d.ts.map +1 -0
- package/lib/types/client/share/share-preview.d.ts.map +1 -1
- package/lib/types/client/sidebar-panels.d.ts +63 -0
- package/lib/types/client/sidebar-panels.d.ts.map +1 -0
- package/lib/types/client/styles/compat.css.d.ts +1 -1
- package/lib/types/client/styles/compat.css.d.ts.map +1 -1
- package/lib/types/client/styles/composer.css.d.ts +1 -1
- package/lib/types/client/styles/composer.css.d.ts.map +1 -1
- package/lib/types/client/styles/header.css.d.ts +1 -1
- package/lib/types/client/styles/header.css.d.ts.map +1 -1
- package/lib/types/client/styles/info.css.d.ts +1 -1
- package/lib/types/client/styles/info.css.d.ts.map +1 -1
- package/lib/types/client/styles/layout.css.d.ts +1 -1
- package/lib/types/client/styles/layout.css.d.ts.map +1 -1
- package/lib/types/client/styles/remote-session.css.d.ts +2 -0
- package/lib/types/client/styles/remote-session.css.d.ts.map +1 -0
- package/lib/types/client-data/current-session.d.ts +41 -0
- package/lib/types/client-data/current-session.d.ts.map +1 -0
- package/lib/types/client-data/remote-group-expansion.d.ts +76 -0
- package/lib/types/client-data/remote-group-expansion.d.ts.map +1 -0
- package/lib/types/client-data/remote-status.d.ts +148 -0
- package/lib/types/client-data/remote-status.d.ts.map +1 -0
- package/lib/types/client-data/settings-form.d.ts +774 -0
- package/lib/types/client-data/settings-form.d.ts.map +1 -0
- package/lib/types/client-data/shares.d.ts +211 -0
- package/lib/types/client-data/shares.d.ts.map +1 -0
- package/lib/types/client-pairing.d.ts +114 -0
- package/lib/types/client-pairing.d.ts.map +1 -0
- package/lib/types/client-routes.d.ts +112 -0
- package/lib/types/client-routes.d.ts.map +1 -0
- package/lib/types/config.d.ts +200 -0
- package/lib/types/config.d.ts.map +1 -0
- package/lib/types/fetch-route-intercept.d.ts +156 -0
- package/lib/types/fetch-route-intercept.d.ts.map +1 -0
- package/lib/types/fingerprint.d.ts +164 -0
- package/lib/types/fingerprint.d.ts.map +1 -0
- package/lib/types/http.d.ts +41 -0
- package/lib/types/http.d.ts.map +1 -0
- package/lib/types/index.d.ts +56 -24
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/intercept-shape.d.ts +32 -0
- package/lib/types/intercept-shape.d.ts.map +1 -0
- package/lib/types/intercept.d.ts +432 -0
- package/lib/types/intercept.d.ts.map +1 -0
- package/lib/types/merge-streams.d.ts +393 -0
- package/lib/types/merge-streams.d.ts.map +1 -0
- package/lib/types/relay-access.d.ts +310 -0
- package/lib/types/relay-access.d.ts.map +1 -0
- package/lib/types/relay-client.d.ts +274 -0
- package/lib/types/relay-client.d.ts.map +1 -0
- package/lib/types/relay-filter.d.ts +103 -0
- package/lib/types/relay-filter.d.ts.map +1 -0
- package/lib/types/relay-server.d.ts +208 -0
- package/lib/types/relay-server.d.ts.map +1 -0
- package/lib/types/restart-fields.d.ts +22 -0
- package/lib/types/restart-fields.d.ts.map +1 -0
- package/lib/types/restart-watcher.d.ts +83 -0
- package/lib/types/restart-watcher.d.ts.map +1 -0
- package/lib/types/session-reference.d.ts +66 -0
- package/lib/types/session-reference.d.ts.map +1 -0
- package/lib/types/share-export.d.ts +14 -1
- package/lib/types/share-export.d.ts.map +1 -1
- package/lib/types/share-ops.d.ts +79 -0
- package/lib/types/share-ops.d.ts.map +1 -0
- package/lib/types/share-store.d.ts +87 -0
- package/lib/types/share-store.d.ts.map +1 -0
- package/lib/types/virtual-id.d.ts +38 -0
- package/lib/types/virtual-id.d.ts.map +1 -0
- package/lib/virtual-id.js +52 -0
- package/lib/virtual-id.js.map +1 -0
- package/llms.txt +17 -14
- package/package.json +60 -50
- package/scripts/build-client.mjs +5 -3
- package/scripts/check-host-shadow.mjs +19 -2
- package/scripts/check-settings-form.mjs +933 -0
- package/scripts/check-shares.mjs +397 -0
- package/scripts/check-upload-endpoint.mjs +59 -3
- package/scripts/sync-doc-version.mjs +8 -15
- package/scripts/verify-fingerprint-live.mjs +111 -0
- package/src/activity.ts +218 -0
- package/src/admin-routes.ts +627 -0
- package/src/client/MobileAttachButton.tsx +2 -2
- package/src/client/MobileAttachChips.tsx +3 -3
- package/src/client/MobileDrawerFooter.tsx +7 -4
- package/src/client/MobileHome.tsx +20 -16
- package/src/client/MobileHomeChips.tsx +25 -21
- package/src/client/MobileNavOverlay.tsx +2 -2
- package/src/client/MobileNavToggle.tsx +3 -3
- package/src/client/MobileSessionHeader.tsx +100 -107
- package/src/client/MobileSessionInfo.tsx +118 -17
- package/src/client/RemoteComposerBanner.tsx +76 -0
- package/src/client/RemoteHeaderIcon.tsx +103 -0
- package/src/client/RemoteShareMenu.tsx +57 -0
- package/src/client/RemoteStatusIcon.tsx +91 -0
- package/src/client/compat/desktop.ts +22 -0
- package/src/client/compat/types.ts +62 -0
- package/src/client/current-session-report.ts +164 -0
- package/src/client/effects/gestures.ts +8 -17
- package/src/client/effects/header-status.ts +21 -17
- package/src/client/effects/keyboard-avoid.ts +2 -2
- package/src/client/effects/keyboard-guard.ts +75 -7
- package/src/client/effects/model-sheet-extras.ts +8 -2
- package/src/client/effects/permission-sheet.ts +149 -0
- package/src/client/effects/remote-session.ts +68 -0
- package/src/client/effects/turn-fold.ts +52 -0
- package/src/client/effects/workbench-ref-close.ts +16 -23
- package/src/client/index.tsx +151 -36
- package/src/client/locales.ts +396 -2
- package/src/client/remote-fetch.ts +125 -0
- package/src/client/remote-group-expansion.ts +190 -0
- package/src/client/remote-share-css.ts +54 -0
- package/src/client/remote-share-register.ts +244 -0
- package/src/client/remote-status-css.ts +64 -0
- package/src/client/remote-status-register.ts +107 -0
- package/src/client/settings/SettingsSection.tsx +1229 -0
- package/src/client/settings/register-settings.ts +116 -0
- package/src/client/settings/settings-css.ts +236 -0
- package/src/client/share/share-preview.tsx +6 -3
- package/src/client/share/share-sheet.tsx +3 -3
- package/src/client/sidebar-panels.ts +99 -0
- package/src/client/styles/compat.css.ts +10 -0
- package/src/client/styles/composer.css.ts +147 -36
- package/src/client/styles/header.css.ts +15 -11
- package/src/client/styles/info.css.ts +74 -0
- package/src/client/styles/layout.css.ts +20 -0
- package/src/client/styles/remote-session.css.ts +251 -0
- package/src/client-data/current-session.ts +57 -0
- package/src/client-data/remote-group-expansion.ts +145 -0
- package/src/client-data/remote-status.ts +374 -0
- package/src/client-data/settings-form.ts +1371 -0
- package/src/client-data/shares.ts +467 -0
- package/src/client-pairing.ts +244 -0
- package/src/client-routes.ts +732 -0
- package/src/config.ts +447 -0
- package/src/fetch-route-intercept.ts +510 -0
- package/src/fingerprint.ts +408 -0
- package/src/http.ts +68 -0
- package/src/index.ts +719 -62
- package/src/intercept-shape.ts +87 -0
- package/src/intercept.ts +3362 -0
- package/src/merge-streams.ts +1272 -0
- package/src/relay-access.ts +669 -0
- package/src/relay-client.ts +1514 -0
- package/src/relay-filter.ts +291 -0
- package/src/relay-server.ts +2025 -0
- package/src/restart-fields.ts +36 -0
- package/src/restart-watcher.ts +140 -0
- package/src/session-reference.ts +156 -0
- package/src/share-export.ts +26 -1
- package/src/share-ops.ts +114 -0
- package/src/share-store.ts +373 -0
- package/src/virtual-id.ts +59 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared "which sessions have remote enabled" table (2.0.0 remote-session
|
|
3
|
+
* groundwork): pure logic plus a small JSON file under the DSH data dir. The
|
|
4
|
+
* server opens/closes remote per session; a paired desktop client may close
|
|
5
|
+
* but never open; everything else here exists to make one rule survivable
|
|
6
|
+
* across restarts — an idle shared session must put itself away.
|
|
7
|
+
*
|
|
8
|
+
* Three clock rules, all driven by the caller:
|
|
9
|
+
* - `touch()` marks activity (turn start/end, message sent, approval or
|
|
10
|
+
* question answered — opening a session to look at it is NOT activity).
|
|
11
|
+
* - `setBusy()` marks running/waiting; busy sessions never idle out and the
|
|
12
|
+
* idle clock restarts from the moment busy ends.
|
|
13
|
+
* - everything else is derived: `sweep()` closes whatever has been idle past
|
|
14
|
+
* `idleHours`, `remainingMs()` reports the countdown.
|
|
15
|
+
*
|
|
16
|
+
* Accessibility crosses generations: subagent and fork sessions are reachable
|
|
17
|
+
* when any ancestor is shared, without themselves appearing in the table
|
|
18
|
+
* (`isAccessible` walks `parentOf`).
|
|
19
|
+
*
|
|
20
|
+
* Persistence is deliberately dumb: one JSON file, rewritten atomically
|
|
21
|
+
* (tmp + rename) after every operation that changes what the file would say,
|
|
22
|
+
* synchronously, because a crash right after "share" must not silently
|
|
23
|
+
* un-share. `busy` is never persisted — after a restart every session is
|
|
24
|
+
* idle by definition until the caller says otherwise.
|
|
25
|
+
*/
|
|
26
|
+
/** One row of the table, as `list()` reports it. */
|
|
27
|
+
export interface ShareEntry {
|
|
28
|
+
sessionId: string;
|
|
29
|
+
/** When remote was first enabled (repeat `share` does not move it). */
|
|
30
|
+
sharedAt: number;
|
|
31
|
+
/** Last activity the caller announced via `touch()` (or busy-end). */
|
|
32
|
+
lastActivityAt: number;
|
|
33
|
+
/** Memory-only: never persisted, always false after a restart. */
|
|
34
|
+
busy: boolean;
|
|
35
|
+
}
|
|
36
|
+
/** Why a session left the table. `manual` = server-side close, `client` =
|
|
37
|
+
* desktop-client close, `idle` = automatic idle shutdown. */
|
|
38
|
+
export type UnshareReason = 'manual' | 'client' | 'idle';
|
|
39
|
+
export type ShareEvent = {
|
|
40
|
+
type: 'shared';
|
|
41
|
+
sessionId: string;
|
|
42
|
+
} | {
|
|
43
|
+
type: 'unshared';
|
|
44
|
+
sessionId: string;
|
|
45
|
+
reason: UnshareReason;
|
|
46
|
+
};
|
|
47
|
+
export interface ShareStoreOptions {
|
|
48
|
+
/** Absolute path of the persistence file. */
|
|
49
|
+
file: string;
|
|
50
|
+
/** Idle budget in hours; must be 0 < x ≤ 8760, anything else falls back
|
|
51
|
+
* to 48. Changeable later via `setIdleHours`. */
|
|
52
|
+
idleHours: number;
|
|
53
|
+
/** Injectable clock; defaults to Date.now. */
|
|
54
|
+
now?: () => number;
|
|
55
|
+
}
|
|
56
|
+
export interface ShareStore {
|
|
57
|
+
/** Enable remote for a session. True when newly shared; false when it was
|
|
58
|
+
* already shared (sharedAt stays put, lastActivityAt refreshes to now). */
|
|
59
|
+
share(sessionId: string): boolean;
|
|
60
|
+
/** Remove a session from the table. True when it was actually removed. */
|
|
61
|
+
unshare(sessionId: string, reason: UnshareReason): boolean;
|
|
62
|
+
/** Whether the session itself is in the table (inheritance NOT applied). */
|
|
63
|
+
isShared(sessionId: string): boolean;
|
|
64
|
+
/** Whether the session is reachable for remote access: itself shared, or
|
|
65
|
+
* some ancestor within 16 `parentOf` hops shared. */
|
|
66
|
+
isAccessible(sessionId: string, parentOf: (id: string) => string | undefined): boolean;
|
|
67
|
+
/** Announce activity. No-op for sessions not in the table. */
|
|
68
|
+
touch(sessionId: string): void;
|
|
69
|
+
/** Announce running/waiting state. No-op for sessions not in the table.
|
|
70
|
+
* busy true→false restarts the idle clock from now. */
|
|
71
|
+
setBusy(sessionId: string, busy: boolean): void;
|
|
72
|
+
/** Close every expired session now. Returns the closed ids, sorted. */
|
|
73
|
+
sweep(): string[];
|
|
74
|
+
/** undefined when not shared; Infinity while busy; otherwise how many ms
|
|
75
|
+
* are left before this session would idle out (never negative). */
|
|
76
|
+
remainingMs(sessionId: string): number | undefined;
|
|
77
|
+
/** Snapshot of the table, sharedAt ascending (ties broken by id). */
|
|
78
|
+
list(): ShareEntry[];
|
|
79
|
+
/** Replace the idle budget. Invalid values are ignored; takes effect
|
|
80
|
+
* immediately for subsequent sweep/remainingMs calls. */
|
|
81
|
+
setIdleHours(hours: number): void;
|
|
82
|
+
/** Observe share/unshare events. Listener errors are swallowed. Returns
|
|
83
|
+
* an unsubscribe function. */
|
|
84
|
+
subscribe(listener: (event: ShareEvent) => void): () => void;
|
|
85
|
+
}
|
|
86
|
+
export declare function createShareStore(options: ShareStoreOptions): ShareStore;
|
|
87
|
+
//# sourceMappingURL=share-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"share-store.d.ts","sourceRoot":"","sources":["../../src/share-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAKH,oDAAoD;AACpD,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAA;IAChB,sEAAsE;IACtE,cAAc,EAAE,MAAM,CAAA;IACtB,kEAAkE;IAClE,IAAI,EAAE,OAAO,CAAA;CACd;AAED;6DAC6D;AAC7D,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAA;AAExD,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,CAAA;AAElE,MAAM,WAAW,iBAAiB;IAChC,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAA;IACZ;qDACiD;IACjD,SAAS,EAAE,MAAM,CAAA;IACjB,8CAA8C;IAC9C,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,UAAU;IACzB;+EAC2E;IAC3E,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAA;IACjC,0EAA0E;IAC1E,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAA;IAC1D,4EAA4E;IAC5E,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAA;IACpC;yDACqD;IACrD,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAA;IACtF,8DAA8D;IAC9D,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B;2DACuD;IACvD,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAAA;IAC/C,uEAAuE;IACvE,KAAK,IAAI,MAAM,EAAE,CAAA;IACjB;uEACmE;IACnE,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IAClD,qEAAqE;IACrE,IAAI,IAAI,UAAU,EAAE,CAAA;IACpB;6DACyD;IACzD,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC;kCAC8B;IAC9B,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,MAAM,IAAI,CAAA;CAC7D;AAgDD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,UAAU,CA6OvE"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reversible virtual ids for sessions and workspaces the sub-client surfaces
|
|
3
|
+
* locally while their real records live on a relay server (T23b-1). The UI
|
|
4
|
+
* only ever sees `zr~<serverId>~<original id>`; the interceptor maps those
|
|
5
|
+
* back to the original ids before a call travels, and maps server-returned
|
|
6
|
+
* ids back to virtual form on the way out.
|
|
7
|
+
*
|
|
8
|
+
* The mapping is deliberately a PURE function of (serverId, id), not a
|
|
9
|
+
* session-table lookup: every shared session of one server gets the same
|
|
10
|
+
* virtual id whether or not this process ever opened it, so result rewriting
|
|
11
|
+
* needs no state. Parsing validates the fixed skeleton (`zr~` + the 8 hex
|
|
12
|
+
* chars `loadServerId` mints, relay-server.ts) and keeps the remainder
|
|
13
|
+
* VERBATIM — the original id may contain any characters, including `~`, so
|
|
14
|
+
* nothing after the second separator is interpreted.
|
|
15
|
+
*
|
|
16
|
+
* A local DSH id can never collide with the prefix: real session ids are
|
|
17
|
+
* `session-<uuid>` and workspace ids are bare uuids.
|
|
18
|
+
*/
|
|
19
|
+
/** The fixed prefix every virtual id starts with. */
|
|
20
|
+
export declare const VIRTUAL_ID_PREFIX = "zr~";
|
|
21
|
+
/** The two halves {@link fromVirtual} recovers from a virtual id. */
|
|
22
|
+
export interface VirtualIdParts {
|
|
23
|
+
serverId: string;
|
|
24
|
+
id: string;
|
|
25
|
+
}
|
|
26
|
+
/** Build the virtual form of one server-side session or workspace id. */
|
|
27
|
+
export declare function toVirtual(serverId: string, id: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Parse one virtual id back into its halves, or `undefined` for anything
|
|
30
|
+
* that does not match the skeleton. Strict on the parts that carry meaning:
|
|
31
|
+
* the literal prefix, exactly 8 lowercase hex server id chars, and the
|
|
32
|
+
* separating `~` after them. The remainder is not validated — any string,
|
|
33
|
+
* including the empty one, is a legal original id.
|
|
34
|
+
*/
|
|
35
|
+
export declare function fromVirtual(id: unknown): VirtualIdParts | undefined;
|
|
36
|
+
/** True when {@link fromVirtual} would parse this value. */
|
|
37
|
+
export declare function isVirtual(id: unknown): boolean;
|
|
38
|
+
//# sourceMappingURL=virtual-id.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"virtual-id.d.ts","sourceRoot":"","sources":["../../src/virtual-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,qDAAqD;AACrD,eAAO,MAAM,iBAAiB,QAAQ,CAAA;AAMtC,qEAAqE;AACrE,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,EAAE,EAAE,MAAM,CAAA;CACX;AAED,yEAAyE;AACzE,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CASnE;AAED,4DAA4D;AAC5D,wBAAgB,SAAS,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAE9C"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reversible virtual ids for sessions and workspaces the sub-client surfaces
|
|
3
|
+
* locally while their real records live on a relay server (T23b-1). The UI
|
|
4
|
+
* only ever sees `zr~<serverId>~<original id>`; the interceptor maps those
|
|
5
|
+
* back to the original ids before a call travels, and maps server-returned
|
|
6
|
+
* ids back to virtual form on the way out.
|
|
7
|
+
*
|
|
8
|
+
* The mapping is deliberately a PURE function of (serverId, id), not a
|
|
9
|
+
* session-table lookup: every shared session of one server gets the same
|
|
10
|
+
* virtual id whether or not this process ever opened it, so result rewriting
|
|
11
|
+
* needs no state. Parsing validates the fixed skeleton (`zr~` + the 8 hex
|
|
12
|
+
* chars `loadServerId` mints, relay-server.ts) and keeps the remainder
|
|
13
|
+
* VERBATIM — the original id may contain any characters, including `~`, so
|
|
14
|
+
* nothing after the second separator is interpreted.
|
|
15
|
+
*
|
|
16
|
+
* A local DSH id can never collide with the prefix: real session ids are
|
|
17
|
+
* `session-<uuid>` and workspace ids are bare uuids.
|
|
18
|
+
*/
|
|
19
|
+
/** The fixed prefix every virtual id starts with. */
|
|
20
|
+
export const VIRTUAL_ID_PREFIX = 'zr~';
|
|
21
|
+
/** The server id half: exactly the 8 lowercase hex chars relay-server.ts
|
|
22
|
+
* mints (`randomBytes(4).toString('hex')`). */
|
|
23
|
+
const SERVER_ID_PATTERN = /^[0-9a-f]{8}$/;
|
|
24
|
+
/** Build the virtual form of one server-side session or workspace id. */
|
|
25
|
+
export function toVirtual(serverId, id) {
|
|
26
|
+
return `${VIRTUAL_ID_PREFIX}${serverId}~${id}`;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Parse one virtual id back into its halves, or `undefined` for anything
|
|
30
|
+
* that does not match the skeleton. Strict on the parts that carry meaning:
|
|
31
|
+
* the literal prefix, exactly 8 lowercase hex server id chars, and the
|
|
32
|
+
* separating `~` after them. The remainder is not validated — any string,
|
|
33
|
+
* including the empty one, is a legal original id.
|
|
34
|
+
*/
|
|
35
|
+
export function fromVirtual(id) {
|
|
36
|
+
if (typeof id !== 'string' || !id.startsWith(VIRTUAL_ID_PREFIX))
|
|
37
|
+
return undefined;
|
|
38
|
+
const rest = id.slice(VIRTUAL_ID_PREFIX.length);
|
|
39
|
+
// The shortest legal remainder is `<8 hex>~` — 9 chars with the separator
|
|
40
|
+
// at index 8. Anything shorter cannot carry a server id.
|
|
41
|
+
if (rest.length < 9 || rest[8] !== '~')
|
|
42
|
+
return undefined;
|
|
43
|
+
const serverId = rest.slice(0, 8);
|
|
44
|
+
if (!SERVER_ID_PATTERN.test(serverId))
|
|
45
|
+
return undefined;
|
|
46
|
+
return { serverId, id: rest.slice(9) };
|
|
47
|
+
}
|
|
48
|
+
/** True when {@link fromVirtual} would parse this value. */
|
|
49
|
+
export function isVirtual(id) {
|
|
50
|
+
return fromVirtual(id) !== undefined;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=virtual-id.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"virtual-id.js","sourceRoot":"","sources":["../src/virtual-id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,qDAAqD;AACrD,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAA;AAEtC;+CAC+C;AAC/C,MAAM,iBAAiB,GAAG,eAAe,CAAA;AAQzC,yEAAyE;AACzE,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,EAAU;IACpD,OAAO,GAAG,iBAAiB,GAAG,QAAQ,IAAI,EAAE,EAAE,CAAA;AAChD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,EAAW;IACrC,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,iBAAiB,CAAC;QAAE,OAAO,SAAS,CAAA;IACjF,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAA;IAC/C,0EAA0E;IAC1E,yDAAyD;IACzD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;QAAE,OAAO,SAAS,CAAA;IACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IACjC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,SAAS,CAAA;IACvD,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;AACxC,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,SAAS,CAAC,EAAW;IACnC,OAAO,WAAW,CAAC,EAAE,CAAC,KAAK,SAAS,CAAA;AACtC,CAAC"}
|
package/llms.txt
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
# dsh-zen-remote
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> One DeepSeek Harness plugin, two roles, shipped as ONE npm package. The **host** role: app-shell mobile UI (two-level page stack, session-list home, reworked composer, gestures, phone-local attachment upload), pairing-code/device-token gateway for public-internet access behind your own reverse proxy, installable PWA, real Web Push, and per-session sharing to paired desktops. The **client** role: runs inside another DSH desktop app and surfaces the host's remote-enabled sessions in its own sidebar — history, live progress, messages, approvals, terminals; everything executes on the host over zen-remote's relay protocol.
|
|
4
4
|
|
|
5
5
|
## Key facts
|
|
6
|
-
- One npm package `dsh-zen-remote`, MIT. Install: `dsh plugin add dsh-zen-remote
|
|
7
|
-
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
6
|
+
- One npm package `dsh-zen-remote`, MIT. Install: DSH Plugins page → Add plugin → `dsh-zen-remote` → Enable now (desktop app and web UI), or `dsh plugin --profile web add dsh-zen-remote` on a headless web service (the CLI requires `--profile` and refuses `--profile desktop`); by hand: one dependency line in `~/.dsh/profiles/desktop|web/package.json` + `pnpm install` + `dsh-zen-remote` appended to that file's `dsh.profile.bundles`. Then restart DSH. The composition layer is a single row `dsh-zen-remote`; the main entry loads the gateway and push sub-plugins itself according to the `role` config. Old `dsh-zen-remote-gateway` / `dsh-zen-remote-push` rows are warned and skipped — do not re-add them (a second gateway would fight over port 3088).
|
|
7
|
+
- Roles: `role: host` (default) runs mobile UI + gateway child + push + session sharing + the relay server; `role: client` runs the relay client and the sidebar/session-page remote parts only — no gateway, no push, no phone UI. Anything that is not exactly `client` degrades to host.
|
|
8
|
+
- Config lives in the DSH Plugins page settings block; per field the first legal value wins: environment variable > plugin row settings > `~/.dsh/lan-gate.config.json` > default. Restart-required fields (`role`, `port`, `host`, `targetPort`, `rateLimit`, `trustedProxies`, `vapidSubject`, `lang`, push fields) reload the plugin row automatically; everything else is volatile and read live.
|
|
9
|
+
- Host sharing: per-session remote toggle from four places (sidebar session "…" menu, title-row icon with confirm, settings list, the phone session info card's remote access switch); idle sleep after 48h without session activity (running/waiting sessions exempt, viewers don't count); auto-share new sessions (opt-in; relay-created sessions always auto-share); subagent/fork sessions follow the parent.
|
|
10
|
+
- Pairing codes are role-bound: web app devices (phones/browsers, see all sessions) redeem on the pairing page; desktop app devices pair from the client's settings page via `/lan-gate/pair/claim-desktop` and only ever reach the relay prefix (everything else 403 `relay-only`).
|
|
11
|
+
- Relay protocol (prefix `/_dsh/zen-remote/relay`): `GET ping`, `POST v1/handshake` (relay protocol version + DSH interface fingerprints — version mismatch refuses; fingerprint mismatch annotates and degrades; the answer carries `serverName`/`deviceName`), `POST v1/invoke`, `POST v1/stream` (NDJSON, ping heartbeats that carry the current `serverName`/`deviceName`, `unshared` error line on close), `POST v1/event-result`, `POST v1/http` (the plain-HTTP long tail: only `changes.summary` / `changes.diff`, GET only, query normalized to `sessionId`/`seq`/`index`, dispatched in-process through the host's shared `/api` handler), `POST v1/upload` (a remote session's non-image attachments: raw byte stream, share-checked, query normalized to `sessionId`/`name`, 100 MiB cap answers 413, 8 concurrent per device answers 429, dispatched in-process through the same shared handler), `POST v1/unshare`, `POST v1/device/name` (gateway-handled rename of the CALLING device's own record — paired desktop clients only, never forwarded to DSH; 1–40 chars). Access control is a per-method allowlist (`src/relay-access.ts` RELAY_METHODS, mirrored by intercept.ts CLIENT_METHOD_FIELDS, pinned identical by test); global reads (`session/control`, `session/list`, `workspace/follow`) and the @-mention candidates are output-filtered (candidates list only shared sessions); `session/modelCatalog` is a parameter-less global read whose `failures` array the server empties; `dsh-session:` references are scanned wherever DSH would inject them — prompt content, queue-edit content, every string of a `commands/execute` call — and a reference naming an unshared session refuses the call. Approval/question events forward first come, first served; the relay never answers on the client's behalf (an unshared session's approval waits for the server UI or the next subscription), and unshared sessions' notification-type events are never forwarded.
|
|
12
|
+
- Remote extras: a remote session can attach non-image files — the sub-client's own backend wraps the host's `fetchRoutes` upload entry and streams the bytes through `v1/upload` (the 100 MiB cap is the relay's own, an over-limit pick is refused locally on the client, slow links are bounded by a 300 s client-side timeout; export is disabled remotely instead — menu item hidden, backend 403 for virtual ids). The model picker in a remote session lists the SERVER's catalog (groups named "server name · group name"); local sessions list only local models, the last server groups survive relay offline, and virtual groups never appear in local settings such as the vision router (boundary: a server-created session that never sent a message shows the local default model).
|
|
13
|
+
- Gateway auth chain: device token (Bearer) → gateway marker headers `x-zen-remote-via/-role/-device` + `x-zen-remote-secret` (per-load shared secret, `LAN_GATE_RELAY_SECRET`, not externally settable, re-verified with timingSafeEqual) → per-method share-table check. Admin API stays loopback-only; forwarded requests may read status but never mutate.
|
|
14
|
+
- Admin surface: the dsh-zen-remote settings block (pairing with role picker, device list with rename/set-role/revoke, push test, shares list). `/lan-gate/admin` is now just a notice pointing there.
|
|
15
|
+
- Trust model: a paired desktop client is a TRUSTED DEVICE — server-user terminal without sandbox/approval gates, file previews not contained to the session dir, session creation in any workspace. "Shared only" constrains session data (lists, history, events are refused/filtered server-side; @ candidates list only shared sessions; references in messages, queue edits and slash commands naming an unshared session refuse the call), not the machine.
|
|
16
|
+
- Env: `LAN_GATE_PORT` (3088), `LAN_GATE_HOST`, `LAN_GATE_TARGET_PORT` (auto-discovered), `LAN_GATE_RATE_LIMIT` (120), `LAN_GATE_TRUSTED_PROXIES`, `LAN_GATE_VAPID_SUBJECT`, `LAN_GATE_LANG`, `LAN_GATE_RELAY_SECRET` (auto), `DSH_PUSH_*`, `DSH_PUSH_APPROVAL_GRACE_MS`. Same keys (lowerCamelCase) in `lan-gate.config.json`.
|
|
17
|
+
- Security: 8-char one-time role-bound pairing code (10 min TTL), 5-wrong-code lockout, revocable per-device tokens (kills connections + push subscription), 120 req/min per client IP for unpaired requests.
|
|
18
|
+
- Known limits: remote sessions disable export (menu item hidden, backend 403 for virtual ids); changes summary/diff work remotely but the "open on the server machine" entries (open / reveal in Finder / open in app) are hidden there; with dsh-better-sidebar installed, its file surfaces (file browser, editor, git, tasks, bottom workbench, floating windows) are hidden in remote sessions because they act on the CLIENT machine's own files — so remote sessions have no sidebar file browser (it takes over the host's "Files" tab) and file links in chat or deliverables open an empty right column; @ candidates are truncated to 50 by the host BEFORE share filtering (a shared session can be missing from the list on a session-heavy server); third-party plugins' non-standard endpoints are not forwarded — their tools run on the server and are unaffected, what breaks is UI calling the plugin's own backend with a session id (better-sidebar, handled as above); both ends need zen-remote 2.0.x; server switch-back or workspace-id reuse needs a page reload; several DSH windows at once (including the desktop backend opened in a browser) can misjudge which session is currently open — closed-remote hiding can fire at the wrong moment and worst case dismiss an open session to the home page. Dev only (see AGENTS.md hard constraints): same-machine testing must reach the gateway over a LAN IP (loopback = local-direct, skips token checks) — normal two-machine use is unaffected.
|
|
15
19
|
|
|
16
20
|
## Docs
|
|
17
|
-
- README.md (English, install + config), README.zh-CN.md (same in Chinese), AGENTS.md (repo guide), LICENSE.
|
|
18
|
-
- docs/remote-access.md(.en.md) — gateway detail: reverse proxy, pairing, env, admin API,
|
|
19
|
-
- docs/interface.md — interface detail: breakpoints, debug badge, safe-area system, compatible plugins.
|
|
20
|
-
- .ai/ — AI Project OS state, memory, decisions, tasks.
|
|
21
|
+
- README.md (English, install + config + migration + security model), README.zh-CN.md (same in Chinese), AGENTS.md (repo guide), LICENSE.
|
|
22
|
+
- docs/remote-access.md(.en.md) — gateway detail: reverse proxy, roles and pairing, env, admin API, relay protocol, security boundary.
|
|
23
|
+
- docs/interface.md — interface detail: breakpoints, settings block and remote parts, debug badge, safe-area system, compatible plugins.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-zen-remote",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "一个插件、两种角色:主服务端把 DeepSeek Harness 变成手机能安全访问的 PWA(移动端界面、配对码网关、Web Push),并把选定会话共享给已配对的桌面端;子客户端在另一台 DSH 桌面端的侧边栏里远程使用这些会话。One DSH plugin, two roles: the host turns DeepSeek Harness into a phone-reachable PWA (mobile UI, pairing-code gateway behind your own reverse proxy, Web Push) and shares chosen sessions to paired desktops; the client uses those sessions remotely from another DSH desktop app's sidebar.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "lib/index.js",
|
|
@@ -36,7 +36,10 @@
|
|
|
36
36
|
"README.md",
|
|
37
37
|
"README.zh-CN.md",
|
|
38
38
|
"LICENSE",
|
|
39
|
-
"!docs/push-policy.md"
|
|
39
|
+
"!docs/push-policy.md",
|
|
40
|
+
"!docs/SPEC.md",
|
|
41
|
+
"!docs/spike-relay.md",
|
|
42
|
+
"!docs/images"
|
|
40
43
|
],
|
|
41
44
|
"dsh": {
|
|
42
45
|
"bundle": {
|
|
@@ -78,20 +81,21 @@
|
|
|
78
81
|
"web-push": "^3.6.7"
|
|
79
82
|
},
|
|
80
83
|
"peerDependencies": {
|
|
81
|
-
"@deepseek-ai/cordis": "
|
|
82
|
-
"@deepseek-ai/dsh-client-locale": "
|
|
83
|
-
"@deepseek-ai/dsh-client-ui-conversation": "
|
|
84
|
-
"@deepseek-ai/dsh-client-ui-layout": "
|
|
85
|
-
"@deepseek-ai/dsh-client-ui-primitives": "
|
|
86
|
-
"@deepseek-ai/dsh-client-ui-sidebar": "
|
|
87
|
-
"@deepseek-ai/dsh-client-ui-slots": "
|
|
88
|
-
"@deepseek-ai/dsh-host-webserver": "
|
|
89
|
-
"@deepseek-ai/dsh-session": "
|
|
90
|
-
"@deepseek-ai/dsh-session-log-export": "
|
|
91
|
-
"@deepseek-ai/dsh-session-query": "
|
|
92
|
-
"
|
|
93
|
-
"@deepseek-ai/dsh-
|
|
94
|
-
"@deepseek-ai/
|
|
84
|
+
"@deepseek-ai/cordis": "*",
|
|
85
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.7-rc.2 || ^0.2.0-rc.1",
|
|
86
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.7-rc.2 || ^0.2.0-rc.1",
|
|
87
|
+
"@deepseek-ai/dsh-client-ui-layout": "^0.1.7-rc.2 || ^0.2.0-rc.1",
|
|
88
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.7-rc.2 || ^0.2.0-rc.1",
|
|
89
|
+
"@deepseek-ai/dsh-client-ui-sidebar": "^0.1.7-rc.2 || ^0.2.0-rc.1",
|
|
90
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.7-rc.2 || ^0.2.0-rc.1",
|
|
91
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.7-rc.2 || ^0.2.0-rc.1",
|
|
92
|
+
"@deepseek-ai/dsh-session": "^0.1.7-rc.2 || ^0.2.0-rc.1",
|
|
93
|
+
"@deepseek-ai/dsh-session-log-export": "^0.1.7-rc.2 || ^0.2.0-rc.1",
|
|
94
|
+
"@deepseek-ai/dsh-session-query": "^0.1.7-rc.2 || ^0.2.0-rc.1",
|
|
95
|
+
"@deepseek-ai/dsh-tools": "^0.1.7-rc.2 || ^0.2.0-rc.1",
|
|
96
|
+
"@deepseek-ai/dsh-workspace": "^0.1.7-rc.2 || ^0.2.0-rc.1",
|
|
97
|
+
"@deepseek-ai/schemastery": "*",
|
|
98
|
+
"react": "^18.2.0"
|
|
95
99
|
},
|
|
96
100
|
"peerDependenciesMeta": {
|
|
97
101
|
"@deepseek-ai/dsh-host-webserver": {
|
|
@@ -111,38 +115,44 @@
|
|
|
111
115
|
}
|
|
112
116
|
},
|
|
113
117
|
"devDependencies": {
|
|
114
|
-
"@deepseek-ai/cordis": "4.0.
|
|
115
|
-
"@deepseek-ai/cosmokit": "
|
|
116
|
-
"@deepseek-ai/dsh-
|
|
117
|
-
"@deepseek-ai/dsh-api-
|
|
118
|
-
"@deepseek-ai/dsh-api-
|
|
119
|
-
"@deepseek-ai/dsh-
|
|
120
|
-
"@deepseek-ai/dsh-
|
|
121
|
-
"@deepseek-ai/dsh-client-
|
|
122
|
-
"@deepseek-ai/dsh-client-
|
|
123
|
-
"@deepseek-ai/dsh-client-
|
|
124
|
-
"@deepseek-ai/dsh-client-ui-
|
|
125
|
-
"@deepseek-ai/dsh-client-ui-
|
|
126
|
-
"@deepseek-ai/dsh-client-ui-
|
|
127
|
-
"@deepseek-ai/dsh-client-ui-
|
|
128
|
-
"@deepseek-ai/dsh-client-ui-
|
|
129
|
-
"@deepseek-ai/dsh-client-ui-
|
|
130
|
-
"@deepseek-ai/dsh-
|
|
131
|
-
"@deepseek-ai/dsh-
|
|
132
|
-
"@deepseek-ai/dsh-
|
|
133
|
-
"@deepseek-ai/dsh-
|
|
134
|
-
"@deepseek-ai/dsh-
|
|
135
|
-
"@deepseek-ai/dsh-
|
|
136
|
-
"@deepseek-ai/dsh-
|
|
137
|
-
"@deepseek-ai/dsh-
|
|
138
|
-
"@deepseek-ai/dsh-
|
|
139
|
-
"@deepseek-ai/dsh-
|
|
140
|
-
"@deepseek-ai/dsh-
|
|
141
|
-
"@deepseek-ai/dsh-
|
|
142
|
-
"@deepseek-ai/dsh-
|
|
143
|
-
"@deepseek-ai/dsh-
|
|
144
|
-
"@deepseek-ai/dsh-
|
|
145
|
-
"@deepseek-ai/
|
|
118
|
+
"@deepseek-ai/cordis": "4.0.4",
|
|
119
|
+
"@deepseek-ai/cosmokit": "1.8.5",
|
|
120
|
+
"@deepseek-ai/dsh-agent": "0.1.7-rc.2",
|
|
121
|
+
"@deepseek-ai/dsh-api-remotes": "0.1.7-rc.2",
|
|
122
|
+
"@deepseek-ai/dsh-api-session-controller": "0.1.7-rc.2",
|
|
123
|
+
"@deepseek-ai/dsh-api-workspace-controller": "0.1.7-rc.2",
|
|
124
|
+
"@deepseek-ai/dsh-brand": "0.1.7-rc.2",
|
|
125
|
+
"@deepseek-ai/dsh-client-connection": "0.1.7-rc.2",
|
|
126
|
+
"@deepseek-ai/dsh-client-locale": "0.1.7-rc.2",
|
|
127
|
+
"@deepseek-ai/dsh-client-store": "0.1.7-rc.2",
|
|
128
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.1.7-rc.2",
|
|
129
|
+
"@deepseek-ai/dsh-client-ui-layout": "0.1.7-rc.2",
|
|
130
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.2",
|
|
131
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.1.7-rc.2",
|
|
132
|
+
"@deepseek-ai/dsh-client-ui-session": "0.1.7-rc.2",
|
|
133
|
+
"@deepseek-ai/dsh-client-ui-sidebar": "0.1.7-rc.2",
|
|
134
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.2",
|
|
135
|
+
"@deepseek-ai/dsh-host-webserver": "0.1.7-rc.2",
|
|
136
|
+
"@deepseek-ai/dsh-invariants": "0.1.7-rc.2",
|
|
137
|
+
"@deepseek-ai/dsh-llm": "0.1.7-rc.2",
|
|
138
|
+
"@deepseek-ai/dsh-ptc-runtime": "0.1.7-rc.2",
|
|
139
|
+
"@deepseek-ai/dsh-sandbox": "0.1.7-rc.2",
|
|
140
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.1.7-rc.2",
|
|
141
|
+
"@deepseek-ai/dsh-scope": "0.1.7-rc.2",
|
|
142
|
+
"@deepseek-ai/dsh-session": "0.1.7-rc.2",
|
|
143
|
+
"@deepseek-ai/dsh-session-log-export": "0.1.7-rc.2",
|
|
144
|
+
"@deepseek-ai/dsh-session-projection": "0.1.7-rc.2",
|
|
145
|
+
"@deepseek-ai/dsh-session-query": "0.1.7-rc.2",
|
|
146
|
+
"@deepseek-ai/dsh-session-stats": "0.1.7-rc.2",
|
|
147
|
+
"@deepseek-ai/dsh-subagent": "0.1.7-rc.2",
|
|
148
|
+
"@deepseek-ai/dsh-timeout": "0.1.7-rc.2",
|
|
149
|
+
"@deepseek-ai/dsh-token-meter": "0.1.7-rc.2",
|
|
150
|
+
"@deepseek-ai/dsh-tools": "0.1.7-rc.2",
|
|
151
|
+
"@deepseek-ai/dsh-typert-protocol": "0.1.7-rc.2",
|
|
152
|
+
"@deepseek-ai/dsh-util-crypto": "0.1.7-rc.2",
|
|
153
|
+
"@deepseek-ai/dsh-util-values": "0.1.7-rc.2",
|
|
154
|
+
"@deepseek-ai/dsh-workspace": "0.1.7-rc.2",
|
|
155
|
+
"@deepseek-ai/schemastery": "3.18.4",
|
|
146
156
|
"@types/node": "^26.2.0",
|
|
147
157
|
"@types/react": "~18.3.1",
|
|
148
158
|
"@types/react-dom": "~18.3.0",
|
|
@@ -151,7 +161,7 @@
|
|
|
151
161
|
"scripts": {
|
|
152
162
|
"build": "tsc -p tsconfig.json && tsc -p tsconfig.client.json && node scripts/build-client.mjs",
|
|
153
163
|
"verify": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.client.json --noEmit",
|
|
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",
|
|
164
|
+
"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-settings-form.mjs && node scripts/check-shares.mjs && node scripts/check-client-externals.mjs && node scripts/check-info-sheet-stacking.mjs && node scripts/sync-doc-version.mjs --check",
|
|
155
165
|
"version": "node scripts/sync-doc-version.mjs && git add README.md README.zh-CN.md"
|
|
156
166
|
}
|
|
157
167
|
}
|
package/scripts/build-client.mjs
CHANGED
|
@@ -49,7 +49,9 @@ const resolveChild = (parent, rel) => {
|
|
|
49
49
|
return joined === '.' ? '' : joined
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
// Dependency-first topological order from the entry.
|
|
52
|
+
// Dependency-first topological order from the entry. The client tsconfig's
|
|
53
|
+
// rootDir is `src` (so src/client-data can sit beside src/client), which puts
|
|
54
|
+
// the emit under .client-build/client/.
|
|
53
55
|
const visited = new Set()
|
|
54
56
|
const order = []
|
|
55
57
|
const visit = (file) => {
|
|
@@ -62,7 +64,7 @@ const visit = (file) => {
|
|
|
62
64
|
}
|
|
63
65
|
order.push(file)
|
|
64
66
|
}
|
|
65
|
-
visit('index.js')
|
|
67
|
+
visit('client/index.js')
|
|
66
68
|
|
|
67
69
|
const modules = order
|
|
68
70
|
.map((file) => {
|
|
@@ -89,7 +91,7 @@ const wrapped = [
|
|
|
89
91
|
' return module.exports;',
|
|
90
92
|
'}',
|
|
91
93
|
'var module = { exports: {} };',
|
|
92
|
-
'__modules["index.js"](__localRequire, module, module.exports);',
|
|
94
|
+
'__modules["client/index.js"](__localRequire, module, module.exports);',
|
|
93
95
|
'return module.exports; } });',
|
|
94
96
|
'',
|
|
95
97
|
].join('\n')
|
|
@@ -78,6 +78,19 @@ const require = createRequire(import.meta.url)
|
|
|
78
78
|
// devDep then.
|
|
79
79
|
// - npm-published tarballs contain no node_modules, so this shadow only
|
|
80
80
|
// exists on dev machines where the plugin is link-installed.
|
|
81
|
+
// NOTE (T42-fix): src/fingerprint.ts resolves @deepseek-ai packages at
|
|
82
|
+
// runtime — deliberately through the HOST process entry FIRST
|
|
83
|
+
// (process.argv[1]), with the plugin location as a fallback — so a
|
|
84
|
+
// link-installed plugin no longer fingerprints its own devDependency copies.
|
|
85
|
+
// What argv[1] actually is, per launch shape (T23b2-fix3): under the npm
|
|
86
|
+
// CLI it is a SYMLINK (…/bin/dsh into the real install), and fingerprint.ts
|
|
87
|
+
// realpaths it before anchoring — walking node_modules upward from the bin
|
|
88
|
+
// directory would find nothing and silently fall through to this plugin's
|
|
89
|
+
// copies. Under the desktop App it lives inside app.asar (not exercised in
|
|
90
|
+
// practice). Those resolutions are createRequire(...).resolve()
|
|
91
|
+
// calls whose specifier this file's scan regex cannot see, and the runtime
|
|
92
|
+
// priority makes a plugin-copy shadow the fallback, not the read — so no
|
|
93
|
+
// SHADOW_ALLOWED entry is registered for them.
|
|
81
94
|
const SHADOW_ALLOWED = ['@deepseek-ai/dsh-tools', '@deepseek-ai/dsh-session']
|
|
82
95
|
|
|
83
96
|
// Packages that must never exist in this plugin's node_modules at all: DSH
|
|
@@ -86,8 +99,12 @@ const SHADOW_ALLOWED = ['@deepseek-ai/dsh-tools', '@deepseek-ai/dsh-session']
|
|
|
86
99
|
const BANNED_PACKAGES = ['@deepseek-ai/dsh-client-runtime', '@deepseek-ai/dsh-host-apiproxy']
|
|
87
100
|
|
|
88
101
|
// The files the host half actually loads. lib/share-export.js is statically
|
|
89
|
-
// imported by lib/index.js
|
|
90
|
-
|
|
102
|
+
// imported by lib/index.js; lib/fingerprint.js rides the same runtime graph
|
|
103
|
+
// (T42). It is listed here because the scan's whole purpose is watching
|
|
104
|
+
// THIS graph for @deepseek-ai imports: its own package resolutions are
|
|
105
|
+
// createRequire(...).resolve() calls the regex cannot see, but any literal
|
|
106
|
+
// import someone adds there later WILL be caught.
|
|
107
|
+
const HOST_ENTRY_FILES = ['dsh-push.mjs', 'lan-gate.mjs', 'lib/index.js', 'lib/share-export.js', 'lib/fingerprint.js']
|
|
91
108
|
|
|
92
109
|
// Matches the specifier of real module statements only — `... from 'pkg'`,
|
|
93
110
|
// `import 'pkg'`, `import('pkg')`, `require('pkg')` — so @deepseek-ai names
|