@khorsheed/dsh-file-preview 0.4.0 → 0.4.2
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/CHANGELOG.md +12 -0
- package/README.en.md +24 -2
- package/README.i18n.yaml +2 -2
- package/README.md +24 -2
- package/lib/client.js +17472 -1826
- package/lib/index.js +587 -286
- package/lib/typert.host.js +437 -84
- package/lib/typert.remote-client.d.ts +10 -2
- package/lib/typert.remote-client.js +174 -78
- package/lib/types/bash-writes.d.ts +19 -1
- package/lib/types/bash-writes.js +56 -2
- package/lib/types/client/locales.d.ts +1 -1
- package/lib/types/client/locales.js +6 -2
- package/lib/types/client/open-in-app.js +7 -0
- package/lib/types/index.d.ts +8 -18
- package/lib/types/index.js +50 -233
- package/lib/types/open-external.js +1 -1
- package/lib/types/read-surface.d.ts +269 -0
- package/lib/types/read-surface.js +413 -0
- package/lib/types/reveal.js +5 -5
- package/package.json +17 -19
- package/lib/invariant.js +0 -25
- package/lib/types/invariant.d.ts +0 -16
- package/lib/types/invariant.js +0 -23
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# 变更记录
|
|
2
2
|
|
|
3
|
+
## 0.4.2(2026-10-22)
|
|
4
|
+
|
|
5
|
+
- **修复图片预览路由受插件加载顺序影响而静默缺席**:此前构造时一次性探测 `webServer`/`agents`,Cordis 加载顺序不保证——先应用就永不注册路由,3080 生产实证(2026-10-20)所有会话产物图片读成「二进制文件」。路由改挂 `ctx.inject(['webServer', 'agents'])` 子 fiber:两服务到齐(任意顺序)即注册、任一离开即卸载,宿主 web server 或 agent 注册表重启后自动恢复;headless 组合行为不变(无 webServer 时图片仍读 `binary`)
|
|
6
|
+
- **面板紧凑化(面板规范 P1)**:预览标题行换成 panel-kit 的 PanelHeader,内容/改动记录、预览/源码、HTML 分级开关共用一条下划线 tab 行;紧凑面板(≤560px)把路径/复制/打开手势收进 ⋯、搜索收进图标、结构化文档去掉格式横幅框;markdown 正文改读 `--dsh-panel-*` 阅读字级,移动端触摸字号终于到达预览。「打开所在文件夹 / 在 IDE 打开」等拉起宿主本机程序的手势在非本机浏览器(手机经隧道)上隐藏
|
|
7
|
+
- **新增免 Agent 读取面 `createFilePreviewReadSurface`**(包根导出):list/turnFiles/read 核心抽为按事件源/cwd/fs 参数化的自由函数,Remote 方法改调同一核心(奇偶由构造保证),供已自行授权的可信服务端调用方(如协作插件)消费——不构造、不解析、不唤醒会话运行时对象。Remote 面不加动词、不改 wire,不消费该接缝的部署零行为变化
|
|
8
|
+
- 适配宿主 0.2.1 线:退役 invariant 伴生(上游 0.2.1-rc.1 删除 `@deepseek-ai/dsh-invariants` 与全部 `./invariant` 导出)并声明 `engines.dsh`(`>=0.1.5-rc.1 <0.3.0-0`,0.2.1-alpha.3 起其在场即唯一兼容判据);peer 区间加 `|| ^0.2.1-rc.1`(semver 预发布比较符 `^0.2.0-rc.1` 在严格 npm/pnpm 解析路径不覆盖 0.2.1);verifiedHost 前移 0.2.1-rc.1
|
|
9
|
+
|
|
10
|
+
## 0.4.1(2026-09-30)
|
|
11
|
+
|
|
12
|
+
- **适配宿主 0.2.0 的 `runNativeCommand` 第四参**(`'hidden' | 'visible'`,Windows 启动可见性,其余平台忽略——0.2.0 起必填):GUI 启动器(`open -a`、`open -R`、`explorer /select`、nautilus/dolphin/nemo `--select`)传 `'visible'`,wslpath 翻译工具传 `'hidden'`
|
|
13
|
+
- 加宽 `@deepseek-ai/dsh-*` peer 区间以覆盖宿主 0.2.0
|
|
14
|
+
|
|
3
15
|
## 0.4.0(2026-09-27)
|
|
4
16
|
|
|
5
17
|
- **BREAKING(包结构)**:`@khorsheed/dsh-client-ui-file-preview` 并入本包——宿主 Remote 与浏览器半(「会话产物」右栏 tab、回合变更卡片、详情页)合并为一个包、一条 loader 行(id 仍为 `file-preview`,浏览器半经 `dsh.client` 发现,message-tools / taskpilot 同形态)。迁移:卸载旧界面包并删除组合里的 `ui-file-preview` 行;针对旧行 id 的 `disabled` 覆盖不再匹配,需要的话改写为 `file-preview`。旧包名已在 npm deprecate。
|
package/README.en.md
CHANGED
|
@@ -13,7 +13,7 @@ Since 0.4.0 the host Remote service and the browser UI ship as **this one packag
|
|
|
13
13
|
## Features
|
|
14
14
|
|
|
15
15
|
- **Right-sidebar Produced page (one form on both host lines)** — a page-type right-sidebar tab (entered from the guide page) that is also a claimant: every file the session wrote or edited, latest activity first, searchable; a row click opens the detail view in-tab. The tab type claims `dsh-resource://file/**` addresses with a renderable-suffix filter at the extension band, outranking the official document tab's fallback band (a purely static test with no cold-cache window) — file-tree / mention / turn-card / product-row clicks all land in our detail view; types we cannot render (pdf, archives, binaries) fall through to the official document tab.
|
|
16
|
-
- **Detail view** — breadcrumb path header with actions (copy path always; "show in folder" = the file selected in the host file manager, and "open in IDE" = file-exact open via a split button listing every probed IDE, when the host probe finds a handler); the title row's "Reload" gesture re-reads the current file's content (the old read stays visible in flight, and a failure keeps it, reporting through the existing error slot); a Content / Change history toggle — document-form previews with content search, and per-write diff stepping (the official side still has no such dimension: workspace-changes is memory-resident, git-only, and lost on a host restart — this dimension stays self-drawn, **tracked upstream**).
|
|
16
|
+
- **Detail view** — breadcrumb path header with actions (copy path always; "show in folder" = the file selected in the host file manager, and "open in IDE" = file-exact open via a split button listing every probed IDE, when the host probe finds a handler and the page is open on the host itself); the title row's "Reload" gesture re-reads the current file's content (the old read stays visible in flight, and a failure keeps it, reporting through the existing error slot); a Content / Change history toggle — document-form previews with content search, and per-write diff stepping (the official side still has no such dimension: workspace-changes is memory-resident, git-only, and lost on a host restart — this dimension stays self-drawn, **tracked upstream**).
|
|
17
17
|
- **Turn mutation card (both lines)** — each finished turn ends with a collapsible "N products" card (including bash captures — broader than the official deliverables row) with per-file line deltas; clicks take the official open route. On list-kind hosts (0.1.6-alpha.2+) the official deliverables entry in the same slot (present card + memory-resident changes card) is shadowed by a first-class slot mechanism — the turn area keeps only this durable, complete card.
|
|
18
18
|
- **Session file list (host Remote)** — every file the session's `read`/`write`/`edit` calls touched, with each write/edit change's diff attached.
|
|
19
19
|
- **Content reads** — current text, capped and flagged when truncated; images as browser-loadable URLs on web hosts; binary, missing, and oversized files answer classified notices instead of errors.
|
|
@@ -51,11 +51,33 @@ dsh plugin --profile web remove @khorsheed/dsh-file-preview
|
|
|
51
51
|
|
|
52
52
|
`maxReadBytes` caps a single `read` (larger files answer `too-large`), `maxFiles` caps the `list` fold — both positive integers, defaults 512 KiB and 500 — and `captureBashWrites` (default `true`) toggles the bash-write collector.
|
|
53
53
|
|
|
54
|
+
## Workspace admission seam (0.4.1+, server-side consumption)
|
|
55
|
+
|
|
56
|
+
`createFilePreviewReadSurface` (a package-root export) is the read-only consumption surface for **server-side callers that hold no Agent**. The Remote verbs resolve their `agent` parameter through the host's unified Agent lookup — which can wake an offline session — and a caller already holding an authorized session source should not disturb one for a read. The factory takes an **externally-authorized source** and returns `{ list, turnFiles, read }` in the same shapes as the Remote face:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { createFilePreviewReadSurface } from '@khorsheed/dsh-file-preview'
|
|
60
|
+
|
|
61
|
+
const surface = createFilePreviewReadSurface({
|
|
62
|
+
events: () => readSessionEvents(sessionId), // session events (ascending seq), re-read per call
|
|
63
|
+
cwd: sessionCwd, // the path anchor (the session header's cwd)
|
|
64
|
+
fs: scopedFs, // an fs face of the same shape as ctx.fs
|
|
65
|
+
imageUrl: path => issueAddress(path), // optional: the image-address issuer
|
|
66
|
+
})
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- **Authorization lives entirely with the caller**: what the source reaches (which session's events, which anchor, which fs face) is decided from the caller's own authorization facts — the surface performs no admission check of its own and never constructs, resolves, or wakes a session runtime object (`tests/read-surface.spec.ts` pins this on the source: the module contains no session-runtime lookup call).
|
|
70
|
+
- **One core, one answer**: the Remote verbs and the surface run the same core functions (`src/read-surface.ts`) — the list fold, the per-turn fold, the bash-write merge (rebuilt from the same events through the same collector pipeline), and the existence probe — so for the same session both paths answer byte-identically, pinned verbatim by the contract tests. The caps (`maxReadBytes` / `htmlMaxReadBytes` / `maxFiles` / `captureBashWrites`) share the service config's names and defaults.
|
|
71
|
+
- **Images go through the issuer**: with `imageUrl` injected, an image `read` result carries the issued address in the existing `url` field (the client renders `read.url` unchanged, so the consuming side hands out controlled addresses with zero UI change; an issuer returning `undefined` for a path degrades that one read); `readImageBytes(path)` is a minimal authorized byte read (same anchor, same containment, same `maxReadBytes` cap, returning bytes and MIME) for a caller serving bytes through its own authenticated channel. With no issuer, image reads degrade to `kind: 'binary'` exactly as headless does today and never point at the legacy `/file-preview-image/...` route.
|
|
72
|
+
- **The same path containment as the Remote path**: the same `resolve` with the same anchor — `..` escapes, symlink behavior, and the `missing` / `too-large` / `error` taxonomy are identical verbatim.
|
|
73
|
+
- **Lifecycle**: pure and stateless apart from watermark-keyed caches derived from the injected reader (the per-turn fold and the bash captures, invalidated by the log watermark); the caches are scoped to the returned instance and dropped by `dispose()` (the surface stays usable — the next call re-derives); no timers, no background work.
|
|
74
|
+
- **Local behavior is untouched**: the Remote face and the legacy image route stay byte-identical; a deployment that never consumes this seam is unaffected.
|
|
75
|
+
|
|
54
76
|
## Compatibility
|
|
55
77
|
|
|
56
78
|
| Host line | Verdict |
|
|
57
79
|
| --- | --- |
|
|
58
|
-
| deepseek-harness master (`0.1
|
|
80
|
+
| deepseek-harness master (`0.2.1-rc.1`) | ✅ full — host-021 wave: full build+test green against the 0.2.1-rc.1 tree, no other surface change; the same form as 0.1.5: the self-drawn page claims renderable addresses at the extension band (rc.1's tab registry kept the band mechanism); the turnTail list arm also carries the official deliverables shadow |
|
|
59
81
|
| npm release (`>= 0.1.5-rc.1`) | ✅ full (`verifiedHost: 0.1.5-rc.1`) — same form; on chain-kind turnTail slots the registration probes and falls back to the select + priority -1 preemptive arm; the 0.1.5-rc.1 PTC rename is adapted (only `tool/ptc-dispatch` matches; the official v2→v3 log migration renames persisted rows) |
|
|
60
82
|
| npm release (`<= 0.1.4.x`) | ❌ unsupported — the right-sidebar tab system (`ctx.sidebarRightTabs` / `openResource`) landed in 0.1.5; older hosts stay on the previous release line |
|
|
61
83
|
|
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# last confirmed-consistent state. Both languages carry equal authority; after
|
|
3
3
|
# editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/file-preview/README.en.md
|
|
5
|
-
packages/file-preview/README.en.md:
|
|
6
|
-
packages/file-preview/README.md:
|
|
5
|
+
packages/file-preview/README.en.md: aef5dfbc9fe0d030dd87d51a262a50e6881cc5a2
|
|
6
|
+
packages/file-preview/README.md: 5c9106b55d1456bcc4f0a7e4ce0d18f509aacda3
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ agent 干了半天活,到底动了哪些文件、改成了什么样?装了
|
|
|
13
13
|
## 功能
|
|
14
14
|
|
|
15
15
|
- **右栏「产物」页(双线一致)**——page-type 右栏 tab(向导页进入)兼认领方:会话写入或编辑过的每个文件按最近活动倒序、可搜索;点行进入详情页。0.1.5 与 0.1.7-rc.2 同一份形态。tab 类型以 `dsh-resource://file/**` + 可渲染后缀认领,extension 档压过官方 document tab 的 fallback 档(纯静态判定无冷启动窗口);pdf、压缩包等渲染不了的类型自动回落官方 document tab——文件树/mention/回合卡/产物行点击全部落自绘详情页。
|
|
16
|
-
- **详情页**——面包屑路径头 +
|
|
16
|
+
- **详情页**——面包屑路径头 + 行动作(复制路径恒定可用;宿主探测到对应应用、且页面在宿主本机打开时另有「打开所在文件夹」= 文件管理器选中该文件、「在 IDE 打开」= 文件级精确打开,split button 下拉可选探测到的任一 IDE);标题行「重新加载」手势重读当前文件的内容(重读期间旧内容保持显示,失败保留旧内容并走既有错误槽);「内容 / 改动记录」切换——内容是文档形态预览(含内容搜索),改动记录逐次步进每一次 write/edit 的 diff(官方至今仍无对应维度:workspace-changes 内存态、git-only、宿主重启即失——此维度长期自留,**跟踪上游**)。
|
|
17
17
|
- **回合变更卡片(双线)**——每个已完成回合末尾可收起的「N 个产物」卡片(含 bash 捕获,比官方产物行数据全),逐文件列出行数增减;点击走官方打开路由(可渲染地址由本页认领)。list 槽宿主上(0.1.6-alpha.2 起)同槽官方 deliverables 条目(present 卡 + 内存态 changes 卡)被一等 slot 遮蔽压下——回合区只留这张持久全量卡。
|
|
18
18
|
- **会话文件清单(宿主 Remote)**——会话 `read`/`write`/`edit` 调用触碰过的每个文件,每次 write/edit 改动附 diff。
|
|
19
19
|
- **内容读取**——文件当前文本,超限截断并标记;web 宿主上图片返回浏览器可加载的 URL;二进制、不存在、超限的文件返回分类提示而不是报错。
|
|
@@ -51,11 +51,33 @@ dsh plugin --profile web remove @khorsheed/dsh-file-preview
|
|
|
51
51
|
|
|
52
52
|
`maxReadBytes` 限制单次 `read`(超限返回 `too-large`),`maxFiles` 限制 `list` 折叠结果——均为正整数,默认 512 KiB 与 500——`captureBashWrites`(默认 `true`)开关 bash 写入采集器。
|
|
53
53
|
|
|
54
|
+
## 工作区接入接缝(0.4.1+,服务端消费)
|
|
55
|
+
|
|
56
|
+
`createFilePreviewReadSurface`(包根导出)是给**不持有 Agent 的服务端调用方**的只读消费面。Remote 面动词的 `agent` 查找会经宿主统一 Agent 解析——那会唤醒离线会话——而已持有授权会话来源的调用方不该为一次读取惊动它。工厂吃进一份**外部授权的来源**,返回与 Remote 面同形的 `{ list, turnFiles, read }`:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { createFilePreviewReadSurface } from '@khorsheed/dsh-file-preview'
|
|
60
|
+
|
|
61
|
+
const surface = createFilePreviewReadSurface({
|
|
62
|
+
events: () => readSessionEvents(sessionId), // 会话事件(升序 seq),每次调用重读
|
|
63
|
+
cwd: sessionCwd, // 路径锚点(会话头里的 cwd)
|
|
64
|
+
fs: scopedFs, // 与 ctx.fs 同形的 fs 面
|
|
65
|
+
imageUrl: path => issueAddress(path), // 可选:图片地址签发器
|
|
66
|
+
})
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- **授权完全在调用方**:来源能读到什么(哪个会话的事件、哪个锚点、哪张 fs 面)由调用方按自己的授权事实决定——接入面自身不做任何准入检查,也从不构造、解析或唤醒会话运行时对象(`tests/read-surface.spec.ts` 把这条钉死在源码上:模块里没有任何 session-runtime 查找调用)。
|
|
70
|
+
- **同一份核心,同一份答案**:Remote 动词与接入面跑同一组核心函数(`src/read-surface.ts`)——清单折叠、按回合折叠、bash 写入合入(同一采集管线从同一份事件重建)、存在性探测——所以同一会话两条路的答案逐字节一致,契约测试逐字钉死。各项上限(`maxReadBytes` / `htmlMaxReadBytes` / `maxFiles` / `captureBashWrites`)与服务配置同名同默认。
|
|
71
|
+
- **图片走签发器**:注入 `imageUrl` 时,图片 `read` 结果把签发的地址放进既有的 `url` 字段(客户端照常渲染 `read.url`,消费方零 UI 改动即可发出受控地址;签发器对某个路径返回 `undefined` 时仅该次读取降级);`readImageBytes(path)` 是最小授权字节读(同锚点、同围栏、同 `maxReadBytes` 上限,返回字节与 MIME),供消费方经自己的鉴权通道发字节。不注入签发器时,图片读取与 headless 现状完全一致地降级为 `kind: 'binary'`,绝不指向遗留的 `/file-preview-image/...` 路由。
|
|
72
|
+
- **路径围栏与 Remote 路径同一条**:同样的 `resolve` 加同一个锚点——`..` 逃逸、symlink 行为、`missing` / `too-large` / `error` 分类逐字一致。
|
|
73
|
+
- **生命周期**:除从注入读取器派生的水印缓存(按回合折叠、bash 捕获,按日志水位失效)外纯无状态;缓存随实例作用域,`dispose()` 即弃(面仍可用,下次调用重新派生),无定时器、无后台工作。
|
|
74
|
+
- **本地行为零变化**:Remote 面与遗留图片路由逐字节不动;不消费此接缝的部署不受任何影响。
|
|
75
|
+
|
|
54
76
|
## Compatibility
|
|
55
77
|
|
|
56
78
|
| Host 行 | 结论 |
|
|
57
79
|
| --- | --- |
|
|
58
|
-
| deepseek-harness master(`0.1
|
|
80
|
+
| deepseek-harness master(`0.2.1-rc.1`) | ✅ 完整——host-021 波对 0.2.1-rc.1 树全量 build+test 绿,无其它表面变化;与 0.1.5 同一形态:自建产物页 extension 档认领可渲染地址(rc.1 的 tab-registry 保留 band 机制);turnTail list 槽臂附带官方 deliverables 遮蔽 |
|
|
59
81
|
| npm release(`>= 0.1.5-rc.1`) | ✅ 完整(`verifiedHost: 0.1.5-rc.1`)——同一形态;turnTail 为 chain 槽时按探测回落 select + priority -1 抢占臂;0.1.5-rc.1 的 PTC 更名已适配(只匹配 `tool/ptc-dispatch`;旧日志由官方 v2→v3 迁移改写到新名) |
|
|
60
82
|
| npm release(`<= 0.1.4.x`) | ❌ 不可用——右栏 tab 体系(`ctx.sidebarRightTabs` / `openResource`)随 0.1.5 落地;旧宿主请停留在旧发布线 |
|
|
61
83
|
|