@liustack/modlens 3.10.0 → 3.12.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/CHANGELOG.md +16 -0
- package/README.md +1 -1
- package/README.zh-CN.md +2 -2
- package/dist/main.js +306 -170
- package/docs/cli.md +5 -3
- package/docs/harness-setup.md +3 -3
- package/docs/output-schema.md +1 -1
- package/docs/security.md +9 -1
- package/docs/troubleshooting.md +58 -0
- package/dsh/index.js +109 -17
- package/package.json +2 -2
- package/skills/modlens/SKILL.md +5 -5
- package/skills/modlens/references/configure.md +2 -1
- package/skills/modlens/references/find-image.md +1 -1
- package/skills/modlens/references/onboard.md +4 -4
- package/skills/modlens/references/runtime.md +1 -1
- package/skills/modlens/scripts/run.ps1 +1 -1
- package/skills/modlens/scripts/run.sh +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.12.0 - 2026-08-14
|
|
4
|
+
|
|
5
|
+
- **The API providers work behind a proxy ([#20](https://github.com/liustack/modlens/issues/20)).** Node's fetch ignores `HTTP_PROXY`/`HTTPS_PROXY` entirely, so machines that reach the internet through a proxy could not use `gemini-api` at all, and the failure surfaced as a bare `fetch failed`. The three inline API providers now honor the standard environment variables (`NO_PROXY` included, via undici's `EnvHttpProxyAgent`), with an explicit setting as the escape hatch: `modlens config set proxy <url>` for all API providers, `<provider>.proxy` to scope it to one. A connect-level failure now names the unreachable host and points at both knobs instead of saying `fetch failed`. Scope is deliberate and documented: the proxy applies to API requests only, while the remote-image download path keeps its direct, IP-pinned connection, because its SSRF guards validate the exact address being contacted and a proxy would blind them. Thanks to @soloyu for a report that arrived with the diagnosis, the fix direction, and the security boundary already thought through.
|
|
6
|
+
|
|
7
|
+
## 3.11.0 - 2026-08-14
|
|
8
|
+
|
|
9
|
+
- **A full-project audit, all ten findings fixed, then re-reviewed until clean.** An independent deep review of the whole repository (P0: none) surfaced ten conditional-but-real defects. Every fix went back through further independent review rounds, which caught real bugs in the first fixes themselves (case and Unicode boundaries, a cancellation regression); the final round accepted with no blocking findings. Each item below carries a regression test — the suite grew by 32 cases.
|
|
10
|
+
- *Cross-project recovery, two ways in.* The OpenCode directory filter's ancestor branch built a LIKE pattern from the database's own `session.directory`, so `_`/`%` inside another project's path acted as wildcards; the comparison now uses exact SUBSTR prefixes in both directions, measured in Unicode code points (SQLite counts characters, JS counts UTF-16 units, so a path with an emoji shifted the boundary), case-sensitive on POSIX and LOWER-folded on Windows (SQLite's LIKE was ASCII case-insensitive, crossing projects on case-sensitive filesystems), with filesystem roots normalized so `/` and `E:/` match as ancestors. And a Claude/Pi transcript recording no cwd at all was accepted on slug evidence alone, though slugs collide (`/tmp/a.b` and `/tmp/a-b`); ownerless transcripts are now skipped by scanning and session lookup, while an explicit `--transcript` still reads anything you point it at.
|
|
11
|
+
- *Image-type spoofing.* When magic-byte sniffing failed, the extension or content-type used to win, so an `.png` URL serving HTML was encoded and uploaded as an image. Every allowed type must now prove itself from its file header — heic/heif included, via ftyp-box sniffing — so there is no extension or content-type fallback left at all.
|
|
12
|
+
- *Credential redaction, everywhere errors quote foreign text.* Subprocess stderr, the three API providers' error bodies, and discovery-probe failures now pass through a shared redactor (known keys replaced exactly, common token shapes as the second net) before reaching terminals, `meta.attempts`, model contexts, or the discovery cache. Remote-URL errors quote origin and path only, so signed query tokens (S3 presigns) never travel, and the shapes are tuned to leave prose like "token limit_exceeded" alone.
|
|
13
|
+
- *Windows PATH probing.* `findOnPath` now walks `PATHEXT`, so a real `agy.exe`/`claude.cmd` install no longer reads as "not on PATH" (doctor and the failover chain both trusted that verdict).
|
|
14
|
+
- *dsh evidence cache.* Failed reads are no longer memoized forever (a fixed config gets a fresh chance without restarting dsh), concurrent steps join one in-flight read instead of double-spending the engine, the cache is capped LRU-style, and cancellation is per-waiter: aborting one step stops its own wait immediately while the shared read completes into the cache, instead of killing every concurrent joiner. Pasted HEIC/HEIF now maps to its real extension, and an unknown media type refuses instead of dressing up as PNG.
|
|
15
|
+
- *Docs told the truth about less than the code did.* The security page now states per provider who fetches a remote URL (only gemini-api downloads locally behind the SSRF guards, magic-byte check, and size cap; openai/anthropic hand the URL to the vendor). The output contract now lists `visual` as required, matching the schema that always enforced it, with a docs-contract test pinning the two together. Doctor renders CLI providers as `[ok?] installed / sign-in not verified offline` instead of a flat ok, adds a machine-readable `status` field (`ready`/`installed`/`missing`) so JSON consumers stop reading bare-binary as ready, and the install flow treats the first real read as the auth check. The skill's trigger list drops `.bmp` (never supported by the CLI) and gains `.heif`.
|
|
16
|
+
- *Hardening around the edges.* The release script and workflow now run lint, refuse version downgrades, and verify the tag matches package.json; CLI numeric flags reject trailing garbage (`--count 3x`); error exits use `process.exitCode` so piped output survives.
|
|
17
|
+
- **dsh install can silently land on 3.5.0 ([#18](https://github.com/liustack/modlens/issues/18)).** pnpm v11's release-age quarantine falls back to an old version when every recent one is inside the window (10 days on pnpm 11.21, measured), and versions before 3.9.0 carry no `dsh.bundle` declaration, so the plugin installs as a plain dependency and none of the tools appear. There is no CLI or env override for the gate ([pnpm#11224](https://github.com/pnpm/pnpm/issues/11224) is still open), but an explicit version or dist-tag skips it ([pnpm#9989](https://github.com/pnpm/pnpm/issues/9989), verified against pnpm 11.21: a bare add resolved 2.8.0 while `@latest` resolved 3.10.0 under the same default gate). The install command everywhere now carries `@latest`, so new installs land current with zero user-side setup; troubleshooting documents the mechanism plus the durable bare-name `minimumReleaseAgeExclude` fallback for whenever pnpm closes that skip. Thanks to @stonogic086-1 for the precise diagnosis.
|
|
18
|
+
|
|
3
19
|
## 3.10.0 - 2026-08-14
|
|
4
20
|
|
|
5
21
|
- **Pasting into the dsh Web UI now works end to end, thumbnails included.** The plugin registers a wrapper provider whose model list carries two entries, `DeepSeek-V4-Flash (modlens vision)` and `DeepSeek-V4-Pro (modlens vision)`; select one and pastes are admitted, because dsh's image admission asks the selected provider for input modalities and the wrapper declares image input. Conversion happens at request time on the wire messages only: the durable session log and the UI keep the native image blocks (your paste keeps its thumbnail), each pasted attachment is read once through the modlens CLI and cached for every later step, and the wrapped request is answered by the same DeepSeek route it always used. The wrap covers DeepSeek and GLM text models only; their own vision models (deepseek-vl/ocr, janus, glm v-series) are excluded by name pattern and by declared modality, so a model that can already see is never wrapped. The `agent/pre-step` auto-read from 3.9.0 becomes opt-in (`autoRead: true`) since request-time conversion owns the paste path and preserves the native UX. Registration also stopped failing silently: the adapter now supplies the base-class methods dsh's duck-typing expects (`providerInfo`, `providerRetryPolicy`), whose absence was exactly the invisible failure, and a registration error is logged to the harness console instead of swallowed.
|
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ Issues are welcome any time: [open one](https://github.com/liustack/modlens/issu
|
|
|
34
34
|
|
|
35
35
|
## Highlights
|
|
36
36
|
|
|
37
|
-
**🥇 The first vision plugin for DeepSeek Harness (dsh):** one command, `npx -y @deepseek-ai/dsh plugin --profile web add @liustack/modlens`, and the text-only DeepSeek model behind dsh reads images through a native `read_image` tool. For pasted images, switch the model selector to one of the two entries the plugin adds, **`DeepSeek-V4-Flash (modlens vision)`** or **`DeepSeek-V4-Pro (modlens vision)`**: pastes are admitted there, converted to evidence at request time (your message keeps its native thumbnail), and answered by the same DeepSeek route. The wrap covers DeepSeek and GLM text models only; their own vision models are excluded automatically.
|
|
37
|
+
**🥇 The first vision plugin for DeepSeek Harness (dsh):** one command, `npx -y @deepseek-ai/dsh plugin --profile web add @liustack/modlens@latest`, and the text-only DeepSeek model behind dsh reads images through a native `read_image` tool. If dsh warns `declares no dsh.bundle`, pnpm's release-age gate installed an old version: see [troubleshooting](docs/troubleshooting.md#dsh-says-declares-no-dshbundle--installed-as-a-plain-dependency) for the one-line fix. For pasted images, switch the model selector to one of the two entries the plugin adds, **`DeepSeek-V4-Flash (modlens vision)`** or **`DeepSeek-V4-Pro (modlens vision)`**: pastes are admitted there, converted to evidence at request time (your message keeps its native thumbnail), and answered by the same DeepSeek route. The wrap covers DeepSeek and GLM text models only; their own vision models are excluded automatically.
|
|
38
38
|
|
|
39
39
|
**Paste an image and it reads it.** No saving to a file and passing a path first.
|
|
40
40
|
|
package/README.zh-CN.md
CHANGED
|
@@ -34,7 +34,7 @@ DeepSeek 和 GLM 没有视觉能力,无法进行图片识别。ModLens 借助
|
|
|
34
34
|
|
|
35
35
|
## 亮点
|
|
36
36
|
|
|
37
|
-
**🥇 全网第一个支持 DeepSeek Harness(dsh)的外挂视觉识别插件:**一条命令 `npx -y @deepseek-ai/dsh plugin --profile web add @liustack/modlens`,dsh 背后的纯文本 DeepSeek 模型即可通过原生 `read_image`
|
|
37
|
+
**🥇 全网第一个支持 DeepSeek Harness(dsh)的外挂视觉识别插件:**一条命令 `npx -y @deepseek-ai/dsh plugin --profile web add @liustack/modlens@latest`,dsh 背后的纯文本 DeepSeek 模型即可通过原生 `read_image` 工具读图。如果 dsh 提示 `declares no dsh.bundle`,是 pnpm 的发布冷静期把版本压旧了,一行配置可解,见[故障排查](docs/troubleshooting.md#dsh-says-declares-no-dshbundle--installed-as-a-plain-dependency)。要粘贴识图,把模型选择器切到插件新增的两个条目之一:**`DeepSeek-V4-Flash (modlens vision)`** 或 **`DeepSeek-V4-Pro (modlens vision)`**,贴图放行、发请求时转成证据(你的消息保留原生缩略图)、仍由原 DeepSeek 路由回答。包装只覆盖 DeepSeek 与 GLM 的文本模型,两家自己的视觉型号自动排除。
|
|
38
38
|
|
|
39
39
|
**直接粘贴图片识别** 无需先保存成文件再提供路径。
|
|
40
40
|
|
|
@@ -62,7 +62,7 @@ agy # 浏览器完成
|
|
|
62
62
|
**DeepSeek Harness(dsh)用户不走 skill 流程**,本包就是原生 dsh 插件:
|
|
63
63
|
|
|
64
64
|
```sh
|
|
65
|
-
npx -y @deepseek-ai/dsh plugin --profile web add @liustack/modlens
|
|
65
|
+
npx -y @deepseek-ai/dsh plugin --profile web add @liustack/modlens@latest
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
装完即有 `read_image` 工具,选「(modlens vision)」模型变体即可直接粘贴识图。引擎配置同样在 `~/.modlens`,详见[宿主接入](docs/harness-setup.md)。
|