@anionex/dsh-vision-toolkit 0.1.45 → 0.1.47
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 +40 -1
- package/README.i18n.yaml +2 -2
- package/README.md +33 -4
- package/README.zh.md +34 -4
- package/docs/requirements-traceability/README.i18n.yaml +2 -2
- package/docs/requirements-traceability/README.md +6 -5
- package/docs/requirements-traceability/README.zh.md +6 -5
- package/lib/client.js +20 -1
- package/lib/client.js.map +1 -1
- package/lib/config.js +147 -5
- package/lib/config.js.map +1 -1
- package/lib/credentials-compat.js +12 -0
- package/lib/credentials-compat.js.map +1 -0
- package/lib/evidence-cache.js +3 -0
- package/lib/evidence-cache.js.map +1 -1
- package/lib/image-input-variants.js +12 -3
- package/lib/image-input-variants.js.map +1 -1
- package/lib/index.js +5 -8
- package/lib/index.js.map +1 -1
- package/lib/paths.js +15 -2
- package/lib/paths.js.map +1 -1
- package/lib/runtime.js +56 -13
- package/lib/runtime.js.map +1 -1
- package/lib/settings-compat.js +38 -0
- package/lib/settings-compat.js.map +1 -0
- package/lib/tools.js +2 -2
- package/lib/tools.js.map +1 -1
- package/lib/types/client/index.d.ts +7 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/config.d.ts +20 -5
- package/lib/types/config.d.ts.map +1 -1
- package/lib/types/credentials-compat.d.ts +6 -0
- package/lib/types/credentials-compat.d.ts.map +1 -0
- package/lib/types/evidence-cache.d.ts.map +1 -1
- package/lib/types/image-input-variants.d.ts.map +1 -1
- package/lib/types/index.d.ts +2 -2
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/paths.d.ts +2 -0
- package/lib/types/paths.d.ts.map +1 -1
- package/lib/types/runtime.d.ts +7 -2
- package/lib/types/runtime.d.ts.map +1 -1
- package/lib/types/settings-compat.d.ts +12 -0
- package/lib/types/settings-compat.d.ts.map +1 -0
- package/lib/types/upstream.d.ts +4 -1
- package/lib/types/upstream.d.ts.map +1 -1
- package/lib/types/web.d.ts.map +1 -1
- package/lib/upstream.js +88 -29
- package/lib/upstream.js.map +1 -1
- package/lib/web.js +11 -2
- package/lib/web.js.map +1 -1
- package/package.json +24 -22
- package/src/client/index.tsx +29 -3
- package/src/config.ts +172 -10
- package/src/credentials-compat.ts +16 -0
- package/src/evidence-cache.ts +3 -0
- package/src/image-input-variants.ts +12 -3
- package/src/index.ts +5 -9
- package/src/paths.ts +20 -2
- package/src/runtime.ts +60 -13
- package/src/settings-compat.ts +56 -0
- package/src/tools.ts +2 -2
- package/src/upstream.ts +88 -25
- package/src/web.ts +9 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,43 @@ All notable user-facing changes to DSH Vision Toolkit are documented in this fil
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.1.47] - 2026-10-18
|
|
8
|
+
|
|
9
|
+
### Compatibility
|
|
10
|
+
|
|
11
|
+
- Support DSH `0.2.1-rc.2`. Headless profiles resolve vision credentials through the host launch environment when the interactive credential service is disabled; interactive credentials remain authoritative.
|
|
12
|
+
- 支持 DSH `0.2.1-rc.2`。Headless 禁用交互式凭据服务时从宿主启动环境读取视觉凭据;交互模式仍以凭据服务为准。
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Added actionable writable-session-workspace guidance for permission-denied or read-only workspace-local storage creation. Documented the Windows writable-workspace requirement; storage isolation, the lack of automatic fallback, and the Windows shared-storage ACL restriction remain unchanged (partial follow-up to #169).
|
|
17
|
+
- 工作区内存储因权限或只读文件系统无法创建时,错误会提示改用可写会话工作区;补充 Windows 的可写工作区要求。存储隔离、不自动回退以及 Windows 共享存储的 ACL 限制均保持不变(#169 的部分改进)。
|
|
18
|
+
- Preserve elapsed upstream time when subprocess startup/collection rejects, including abort paths. Clarify that `timeoutMs` gives queue waiting and execution separate budgets, and that execution covers all upstream attempts and retries. The default timeout and runtime deadline behavior are unchanged (partial diagnostics/documentation follow-up to #166).
|
|
19
|
+
- 上游子进程启动或结果收集被拒绝时(包括中止路径)保留已用耗时。明确 `timeoutMs` 分别约束排队与执行,执行预算涵盖所有上游尝试和重试;默认超时和运行时截止行为保持不变(#166 的部分诊断与文档改进)。
|
|
20
|
+
|
|
21
|
+
## [0.1.46] - 2026-10-01
|
|
22
|
+
|
|
23
|
+
### Compatibility
|
|
24
|
+
|
|
25
|
+
- Verified DSH `0.2.0-rc.2` using an isolated, hoisted tarball Profile, actual visual-tool calls, and Web Settings. Add an exact peer/compatibility declaration and a required CI Profile lane for this release. The fixture explicitly selects its OpenAI-compatible protocol after the host's default provider protocol changed.
|
|
26
|
+
- 已通过 DSH `0.2.0-rc.2` 隔离压缩包 Profile、真实视觉工具调用及 Web 设置验收;增加确切兼容声明及 CI 验收矩阵。官方默认协议变更后,测试 fixture 显式选择原有 OpenAI 协议。核实时官方尚无正式稳定版。
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- Added an explicit OpenAI Responses protocol for custom vision providers. An optional, constrained `reasoningEffort` is forwarded only for Responses requests, exposed conditionally in bilingual Settings, and included in both live-Session and durable evidence cache identities. Existing OpenAI configurations remain on Chat Completions and omit effort by default.
|
|
31
|
+
|
|
32
|
+
- Added hardened `provider.headers` for non-secret deployment metadata and `provider.sessionHeaders` for gateways such as OpenCode Zen that require per-session routing. Session values are process-keyed HMAC identifiers that never expose raw Session ids or workspace paths; configured headers are bounded, conflict-checked, error-redacted, cache-isolated, restricted to the provider origin/base path, and shared by both requests in a connection-and-model health operation. The manifest-verified vendored snapshot remains unchanged ([#144](https://github.com/Anionex/dsh-vision-toolkit/issues/144)).
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
- Restored startup and live Settings on DSH 0.1.7, whose Config projection replaced the old `settings.register/get/watch` API. The plugin now uses the host's matching Schema form, unwraps live configuration values for runtime use, and follows Settings document updates. Earlier DSH versions continue using their registration API.
|
|
37
|
+
- Vision API subprocesses now try IPv4 addresses before IPv6 when both are returned by DNS. This avoids exhausting the connection timeout on unusable fake IPv6 addresses in TUN proxies while preserving IPv6-only endpoints.
|
|
38
|
+
- Stopped the image-input variant route from crashing on request messages that carry no `source`. The variant read `message.source.kind` while assembling the evidence pass, but the official `createUserMessage()` helper writes no `source` at runtime (its published type still declares one as required), so a plugin that built a programmatic `ctx.llm.stream()` call on a `vision-toolkit-*` route — the usual summarize, title, or classify pattern (`@modusensus/dsh-mneme` ≤ 0.8.1 did exactly this) — failed with `TypeError: Cannot read properties of undefined (reading 'kind')`, which the host surfaced only as a generic `UNKNOWN` failure chunk. A user message without provenance is now treated as a user turn, and assistant history without provenance is skipped instead of throwing; messages that do carry a source behave exactly as before.
|
|
39
|
+
|
|
40
|
+
### Documentation
|
|
41
|
+
|
|
42
|
+
- Documented `/responses` endpoint composition, common and provider-specific effort values, possible token/latency/cost effects, and the need to review provider data-retention policy even though requests send `store: false`.
|
|
43
|
+
|
|
7
44
|
## [0.1.45] - 2026-09-14
|
|
8
45
|
|
|
9
46
|
### Fixed
|
|
@@ -455,7 +492,9 @@ All notable user-facing changes to DSH Vision Toolkit are documented in this fil
|
|
|
455
492
|
- Runtime teardown cancels in-flight operations before removing Agent-scoped tools, the activation bootstrap, and the Skill.
|
|
456
493
|
- The Web client is published through the current nested `dsh.client` manifest and loader-compatible built artifact required by DSH snapshot0810.
|
|
457
494
|
|
|
458
|
-
[Unreleased]: https://github.com/Anionex/dsh-vision-toolkit/compare/v0.1.
|
|
495
|
+
[Unreleased]: https://github.com/Anionex/dsh-vision-toolkit/compare/v0.1.47...HEAD
|
|
496
|
+
[0.1.47]: https://github.com/Anionex/dsh-vision-toolkit/compare/v0.1.46...v0.1.47
|
|
497
|
+
[0.1.46]: https://github.com/Anionex/dsh-vision-toolkit/compare/v0.1.45...v0.1.46
|
|
459
498
|
[0.1.45]: https://github.com/Anionex/dsh-vision-toolkit/compare/v0.1.44...v0.1.45
|
|
460
499
|
[0.1.44]: https://github.com/Anionex/dsh-vision-toolkit/compare/v0.1.43...v0.1.44
|
|
461
500
|
[0.1.43]: https://github.com/Anionex/dsh-vision-toolkit/compare/v0.1.42...v0.1.43
|
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write dsh-vision-toolkit/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 4fbd8f3ed25c4e810601067365c07aeaacac14da
|
|
6
|
+
README.zh.md: 6c1b977fa3f142f70f97dd03aa3d6f80bebf3ffc
|
package/README.md
CHANGED
|
@@ -274,14 +274,40 @@ You can also configure a Profile patch:
|
|
|
274
274
|
protocol: openai
|
|
275
275
|
```
|
|
276
276
|
|
|
277
|
-
OpenAI Chat Completions-compatible endpoints and Anthropic Messages are supported.
|
|
277
|
+
OpenAI Chat Completions-compatible endpoints, OpenAI Responses, and Anthropic Messages are supported. Existing configurations continue to use Chat Completions. To use a Responses-compatible endpoint, set `protocol: responses`; the plugin appends `/responses` to the configured base URL. An optional `reasoningEffort` is sent only for Responses requests:
|
|
278
|
+
|
|
279
|
+
```yaml
|
|
280
|
+
protocol: responses
|
|
281
|
+
reasoningEffort: medium
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Leaving `reasoningEffort` blank uses the model or proxy default. Common values are `none`, `minimal`, `low`, `medium`, `high`, and `xhigh`, but the field intentionally accepts provider-specific values made from letters, digits, `.`, `_`, and `-` (up to 64 characters). Support and billing vary by model or proxy; higher effort may increase reasoning tokens, latency, and cost. The Responses request sets `store: false`, but that flag does not replace reviewing the provider's own data-retention policy. The Web Settings panel exposes the full provider, runtime, timeout, image-limit, and image-input-variant configuration.
|
|
285
|
+
Profile patches can also supply non-secret deployment metadata in `provider.headers`. A gateway that routes by conversation can name one or more generated headers in `provider.sessionHeaders`; OpenCode Zen, for example, requires `x-opencode-session`:
|
|
286
|
+
|
|
287
|
+
```yaml
|
|
288
|
+
- id: vision-toolkit
|
|
289
|
+
config:
|
|
290
|
+
provider:
|
|
291
|
+
headers:
|
|
292
|
+
x-tenant: acme
|
|
293
|
+
sessionHeaders:
|
|
294
|
+
- x-opencode-session
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Static header values are ordinary plaintext Settings, not Credentials. Do not put API keys, bearer tokens, cookies, or other secrets in `provider.headers`; continue to use `provider.credential` for the vision API key. The plugin rejects client-owned authentication headers and HTTP routing/framing headers, case-insensitive duplicates, conflicts between the two fields, invalid names or values, more than 32 combined entries, names over 128 bytes, values over 4096 bytes, or more than 16384 bytes in total.
|
|
298
|
+
|
|
299
|
+
Every session header receives the first 32 hexadecimal characters of an HMAC-SHA256 value keyed with fresh process randomness. The value is stable for the same operation identity while the DSH process is running and rotates after restart. Calls with a Session use its id as the private HMAC input; only calls without one fall back to `workspace:<absolute path>`, and neither the raw Session id nor workspace path is sent. One Settings health operation uses the same derived value for `GET /models` and its real model request. Headers are injected only into URLs on the configured provider origin and beneath its base path. With both fields empty, no extra-header environment value is created and provider wire-header behavior remains unchanged.
|
|
278
300
|
|
|
279
301
|
The advanced **Default save directory** setting can place artifacts, pasted images, and caches below an absolute POSIX shared root such as `/tmp/dsh-vision-toolkit`; the plugin creates a private mode-0700 child for the current user and workspace. Leaving it blank keeps the existing workspace-local `.dsh-vision-toolkit` directory. Configured shared roots are currently rejected on Windows because their ownership and access-control lists cannot yet be verified safely.
|
|
280
302
|
|
|
303
|
+
On Windows, the session workspace must allow the current user to create and write `.dsh-vision-toolkit` and its contents. A read-only workspace (for example, `C:\Program Files (x86)`) prevents vision execution tools from running. Choose a writable session workspace, such as a project directory under your user profile, and leave **Default save directory** (`storageDir`) blank. Storage creation failures do not automatically fall back to the user home or temporary directory; a successful Settings health check does not establish that the session workspace is writable.
|
|
304
|
+
|
|
281
305
|
When the configured save directory changes, the plugin retains earlier validated roots as read-only input locations. Web Profiles persist that history in the plugin-owned `vision_toolkit_storage` storage-domain sidecar, including when the active Settings provider is read-only, so existing pasted-image paths remain usable after a Profile restart. Custom Profiles should compose `@deepseek-ai/dsh-storage-domain` when they use configured shared storage.
|
|
282
306
|
|
|
283
307
|
For a trusted internal endpoint that uses a self-signed certificate or MITM proxy, start the DSH process with `VISION_SSL_VERIFY=0`. The plugin forwards that value to the isolated Python runtime; certificate verification remains enabled when the variable is unset or has any other value. The false values `false`, `off`, `no`, `none`, and `disabled` are also accepted, case-insensitively.
|
|
284
308
|
|
|
309
|
+
`timeoutMs` defaults to `30000` and accepts `1000`–`600000`. Queue waiting and execution each receive that budget, so a queued call can take longer than `timeoutMs` overall. Execution includes image preparation and all provider attempts/retry waits; this is not a per-HTTP-attempt timeout. For a slow provider, raise the per-call `timeoutMs` based on measured latency, or adjust the profile default when appropriate. The parent cancels unfinished work at its execution deadline even if the provider is still processing it. Logs distinguish `queueMs` from `upstreamMs`; upstream time measures the CLI invocation, including startup, retries and failure collection, rather than pure provider latency.
|
|
310
|
+
|
|
285
311
|
### Configure the Python runtime
|
|
286
312
|
|
|
287
313
|
Most users never need to configure the Python runtime: the plugin prefers a system Python 3.11+ and otherwise downloads a pinned standalone Python automatically from the domestic mirror, falling back to GitHub when the mirror is unreachable.
|
|
@@ -292,11 +318,14 @@ For advanced setups — overriding `runtime.python`, using `runtime.mode: extern
|
|
|
292
318
|
|
|
293
319
|
| Problem | What to do |
|
|
294
320
|
| --- | --- |
|
|
295
|
-
|
|
|
321
|
+
| `plugin storage directory is not writable` | Choose a session workspace writable by the current user and retry. On Windows, leave **Default save directory** (`storageDir`) blank; configured shared storage remains unavailable pending safe ACL validation |
|
|
322
|
+
| The vision-model test fails with `Vision API returned an incompatible response structure` | The base URL usually needs a path prefix. Local OpenAI-compatible services such as LM Studio and Ollama should be entered as `http://127.0.0.1:1234/v1` (include `/v1`); the plugin appends `/chat/completions` for OpenAI Chat Completions or `/responses` for OpenAI Responses, and a port-only address may hit an unknown endpoint |
|
|
296
323
|
| Pasting an image still says the model does not support image input | Restart the Web Profile, refresh the page, and confirm the selected route has the `(Vision Toolkit)` suffix. You can also place the image in the session workspace and invoke `/vision-skills` |
|
|
324
|
+
| A tool reports `timed out` | Check `queueMs` and `upstreamMs` in the diagnostic log; for slow successful provider calls, use a larger per-call `timeoutMs`. Queue time and execution have separate budgets |
|
|
297
325
|
| The vision service returns 429 | Wait for the `Retry-After` interval, or switch to your own endpoint when you need stable higher volume |
|
|
298
326
|
| The image exceeds a size or pixel limit | Crop or resize it first; the error identifies whether bytes or decoded pixels caused the rejection |
|
|
299
327
|
| A custom Credential is missing | Enter the API key in **Settings → Vision Toolkit** and confirm the Credential name matches the provider configuration |
|
|
328
|
+
| OpenCode Zen returns `400 MissingSessionID` | Add `x-opencode-session` to `provider.sessionHeaders` in the Profile patch, then restart the Profile; do not put a Session id or API key in `provider.headers` |
|
|
300
329
|
| First-time runtime setup fails | The standalone-Python download needs network and disk access (domestic mirror first, GitHub fallback). Check connectivity or package-cache access, or install Python 3.11+ / configure `runtime.python` in Settings, then retry the model test |
|
|
301
330
|
| Chrome is not found | Install Chrome, Chromium, or Edge. Only HTML screenshot rendering is unavailable; the other tools still work |
|
|
302
331
|
| DSH Desktop says `dsh` is not recognized, or its built-in marketplace install fails | Open **DSH Terminal** from the tray, run `dsh plugin --profile desktop add @anionex/dsh-vision-toolkit`, then restart DSH Desktop. The Desktop 2.0.1 marketplace has known install issues, so the terminal command is the reliable path for now |
|
|
@@ -304,9 +333,9 @@ For advanced setups — overriding `runtime.python`, using `runtime.mode: extern
|
|
|
304
333
|
|
|
305
334
|
## FAQ
|
|
306
335
|
|
|
307
|
-
**
|
|
336
|
+
**How does a vision model affect costs?**
|
|
308
337
|
|
|
309
|
-
|
|
338
|
+
Each inspection is a separate multimodal request containing the necessary intent and image; context does not accumulate across calls. Actual cost depends on the provider, image, model, and — for Responses — `reasoningEffort`. Higher effort may consume more reasoning tokens and take longer. Leave effort blank to use the provider default, or use a locally deployed small multimodal side model (for example the Gemma 4 or Qwen 3.5/3.6 series) when predictable local cost matters.
|
|
310
339
|
|
|
311
340
|
## Donation
|
|
312
341
|
|
package/README.zh.md
CHANGED
|
@@ -272,14 +272,40 @@ flowchart LR
|
|
|
272
272
|
protocol: openai
|
|
273
273
|
```
|
|
274
274
|
|
|
275
|
-
支持 OpenAI Chat Completions
|
|
275
|
+
支持 OpenAI Chat Completions 兼容端点、OpenAI Responses 和 Anthropic Messages。已有配置仍默认使用 Chat Completions。若要使用兼容 Responses 的端点,请设置 `protocol: responses`;插件会在 API 地址后拼接 `/responses`。可选的 `reasoningEffort` 仅随 Responses 请求发送:
|
|
276
|
+
|
|
277
|
+
```yaml
|
|
278
|
+
protocol: responses
|
|
279
|
+
reasoningEffort: medium
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
`reasoningEffort` 留空时使用模型或代理的默认值。常见值包括 `none`、`minimal`、`low`、`medium`、`high` 和 `xhigh`;为兼容第三方代理和未来扩展,该字段也接受由字母、数字、`.`、`_`、`-` 组成且不超过 64 位的提供方自定义值。实际支持范围与计费由模型或代理决定;较高强度可能增加推理 token、延迟和费用。Responses 请求会设置 `store: false`,但该标记不能替代核查服务商自己的数据保留政策。Web Settings 页面还可以调整超时、图片限制、并发、运行时和图片输入变体。
|
|
283
|
+
Profile patch 还可以通过 `provider.headers` 配置非秘密的部署元数据。需要按会话路由的网关可在 `provider.sessionHeaders` 中列出一个或多个运行时生成的请求头;例如 OpenCode Zen 要求 `x-opencode-session`:
|
|
284
|
+
|
|
285
|
+
```yaml
|
|
286
|
+
- id: vision-toolkit
|
|
287
|
+
config:
|
|
288
|
+
provider:
|
|
289
|
+
headers:
|
|
290
|
+
x-tenant: acme
|
|
291
|
+
sessionHeaders:
|
|
292
|
+
- x-opencode-session
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
静态请求头是保存在 Settings 中的普通明文,不是 Credential。不要在 `provider.headers` 中填写 API Key、Bearer Token、Cookie 或其他秘密;视觉 API Key 仍应使用 `provider.credential`。插件会拒绝客户端自有的鉴权头和 HTTP 路由/请求帧控制头、大小写不敏感的重复项、两个字段间的冲突、非法名称或值、合计超过 32 项、名称超过 128 字节、值超过 4096 字节,或总量超过 16384 字节的配置。
|
|
296
|
+
|
|
297
|
+
每个会话请求头都会获得 HMAC-SHA256(带密钥的不可逆摘要)的前 32 位十六进制值,密钥在 DSH 进程启动时随机生成。同一进程内,相同 operation identity(一次调用采用的会话身份)得到稳定值;重启后会轮换。有 Session 的调用只把 Session id 作为内部 HMAC 输入;确实没有 Session 时才回退到 `workspace:<绝对路径>`,原始 Session id 和 workspace 路径都不会发送。同一次 Settings 健康检查的 `GET /models` 与真实模型请求共用派生值。请求头只注入配置 provider 的同源 URL 且路径必须位于其 base path 下。两个字段均为空时,不创建额外请求头环境变量,provider 的网络请求头语义保持原样。
|
|
276
298
|
|
|
277
299
|
高级设置中的 **默认保存目录** 可以把产物、粘贴图片和缓存放到 `/tmp/dsh-vision-toolkit` 等 POSIX 绝对共享根目录下;插件会为当前用户和工作区创建权限为 0700 的私有子目录。留空时继续使用工作区内原有的 `.dsh-vision-toolkit` 目录。Windows 目前会拒绝配置共享根目录,因为插件尚不能安全校验其所有权和访问控制列表。
|
|
278
300
|
|
|
301
|
+
Windows 上,会话工作区必须允许当前用户创建、写入 `.dsh-vision-toolkit` 及其内容。只读工作区(例如 `C:\Program Files (x86)`)会阻止视觉执行工具运行。请改用用户目录下具有写权限的项目目录作为会话工作区,并将 **默认保存目录**(`storageDir`)留空。创建存储失败时不会自动回退到用户目录或临时目录;Settings 健康检查成功不代表会话工作区可写。
|
|
302
|
+
|
|
279
303
|
配置的保存目录变更后,插件会把之前验证过的根目录保留为只读输入位置。Web Profile 会把这段历史保存在插件自有的 `vision_toolkit_storage` storage-domain sidecar 中;即使当前 Settings 提供方只读,Profile 重启后原有粘贴图片路径仍可继续使用。使用配置共享存储的自定义 Profile 应组合 `@deepseek-ai/dsh-storage-domain`。
|
|
280
304
|
|
|
281
305
|
如果受信任的内部端点使用自签证书或 MITM 代理,可在启动 DSH 进程时设置 `VISION_SSL_VERIFY=0`。插件会把该值传入隔离的 Python 运行环境;未设置或使用其他值时仍默认校验证书。还支持大小写不敏感的假值 `false`、`off`、`no`、`none` 和 `disabled`。
|
|
282
306
|
|
|
307
|
+
`timeoutMs` 默认是 `30000`,允许范围为 `1000`–`600000`。排队等待和执行各自获得这份预算,因此排队调用的总耗时可能超过 `timeoutMs`。执行预算包括图片准备、所有服务请求尝试和重试等待;它并不是每次 HTTP 请求的超时。对于较慢的服务商,可根据实测延迟提高单次调用的 `timeoutMs`,必要时再调整 Profile 默认值。即使服务商仍在处理,父进程也会在执行截止时间取消未完成的任务。日志用 `queueMs` 和 `upstreamMs` 区分排队与上游耗时;后者包含 CLI 启动、重试和失败收集,并不等同于纯服务端延迟。
|
|
308
|
+
|
|
283
309
|
### 配置 Python 运行时
|
|
284
310
|
|
|
285
311
|
大多数用户无需配置 Python 运行时:插件会优先使用系统 Python 3.11+,找不到时自动从国内镜像下载固定版本的托管 Python;国内镜像不可用时回退到 GitHub 官方发布源。
|
|
@@ -290,11 +316,14 @@ flowchart LR
|
|
|
290
316
|
|
|
291
317
|
| 问题 | 处理方式 |
|
|
292
318
|
| --- | --- |
|
|
293
|
-
|
|
|
319
|
+
| `plugin storage directory is not writable` | 将会话工作区改为当前用户可写的目录后重试。Windows 上将 **默认保存目录**(`storageDir`)留空;安全 ACL 校验就绪前,共享存储仍不可用 |
|
|
320
|
+
| 视觉模型测试失败:`Vision API returned an incompatible response structure` | 通常是 API 地址少了路径前缀。LM Studio、Ollama 等本地 OpenAI 兼容服务需填写 `http://127.0.0.1:1234/v1`(带 `/v1`);OpenAI Chat Completions 会拼接 `/chat/completions`,OpenAI Responses 会拼接 `/responses`,只填端口号可能命中未知端点 |
|
|
294
321
|
| 粘贴图片后仍提示模型不支持图片 | 重启 Web Profile 并刷新页面,确认当前模型已切换到带 `(Vision Toolkit)` 的变体;也可以把图片先放进会话工作区,再调用 `/vision-skills` |
|
|
322
|
+
| 工具提示 `timed out` | 查看诊断日志中的 `queueMs` 和 `upstreamMs`;对于可成功但较慢的服务调用,提高单次 `timeoutMs`。排队和执行使用独立预算 |
|
|
295
323
|
| 视觉服务提示 429 | 按错误中的 `Retry-After` 等待后重试;如果需要稳定高额度,切换到自己的视觉端点 |
|
|
296
324
|
| 图片过大或像素超限 | 先裁剪或缩放图片;错误会明确显示是字节还是像素限制 |
|
|
297
325
|
| 自定义 Credential 缺失 | 在 **设置 → 视觉工具** 填写 API Key,并确认 Credential 名称与配置一致 |
|
|
326
|
+
| OpenCode Zen 返回 `400 MissingSessionID` | 在 Profile patch 的 `provider.sessionHeaders` 中加入 `x-opencode-session`,然后重启 Profile;不要把 Session id 或 API Key 写进 `provider.headers` |
|
|
298
327
|
| 首次运行时准备失败 | 自动下载托管 Python 需要网络和磁盘权限(默认先走国内镜像,失败时回退 GitHub);失败时检查网络或包缓存,也可以安装 Python 3.11+ 或在 Settings 中配置 `runtime.python`,然后重新测试 |
|
|
299
328
|
| 找不到 Chrome | 安装 Chrome、Chromium 或 Edge;只有 HTML 截图不可用,其他工具不受影响 |
|
|
300
329
|
| DSH Desktop 提示找不到 `dsh` 命令,或内置插件市场安装失败 | 从托盘打开 **DSH 终端**,运行 `dsh plugin --profile desktop add @anionex/dsh-vision-toolkit`,再重启 DSH Desktop。桌面版 2.0.1 的内置市场存在已知安装问题,当前请优先使用终端安装 |
|
|
@@ -302,9 +331,9 @@ flowchart LR
|
|
|
302
331
|
|
|
303
332
|
## FAQ
|
|
304
333
|
|
|
305
|
-
|
|
334
|
+
**视觉模型会怎样影响成本?**
|
|
306
335
|
|
|
307
|
-
|
|
336
|
+
每次检查都是一条独立的多模态请求,只携带必要意图和图片,调用之间不会累积上下文。实际费用取决于服务商、图片、模型,以及 Responses 的 `reasoningEffort`;较高强度可能消耗更多推理 token 并增加延迟。留空可使用提供方默认值;若更看重可预测的本地成本,也可以使用本地部署的小型多模态侧模型(例如 Gemma 4 或 Qwen 3.5/3.6 系列)。
|
|
308
337
|
|
|
309
338
|
## 赞赏
|
|
310
339
|
|
|
@@ -319,6 +348,7 @@ flowchart LR
|
|
|
319
348
|
- 安全漏洞请按 [SECURITY.md](SECURITY.md) 私下报告。
|
|
320
349
|
- 版本变化见 [CHANGELOG.md](CHANGELOG.md),赞助说明见 [FUNDING.md](FUNDING.md)。
|
|
321
350
|
- 通用视觉工具、跨 Agent 接入和视觉任务方法论请访问上游 [agent-vision-toolkit](https://github.com/Anionex/agent-vision-toolkit)。
|
|
351
|
+
- 国内访问:项目已同步至 [AtomGit](https://atomgit.com/Anionex/dsh-vision-toolkit),方便国内开发者访问、下载和使用。
|
|
322
352
|
|
|
323
353
|
<p align="center">
|
|
324
354
|
<img src="assets/community-group-qr.png" alt="agent-vision-toolkit 项目交流群二维码" width="240" />
|
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write dsh-vision-toolkit/docs/requirements-traceability/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: d54a924435446194cd92e4449177d1fdb95f0ade
|
|
6
|
+
README.zh.md: 03541d3d2c7ca6fd43cdff852c9fda5f1e7ede53
|
|
@@ -11,7 +11,7 @@ This reference maps the DSH Vision Toolkit product brief's committed P0/P1 requi
|
|
|
11
11
|
| P0-1 Standard Profile Bundle | **Delivered** | [`package.json`](../../package.json), [`cordis.patch.yml`](../../cordis.patch.yml), [`src/index.ts`](../../src/index.ts), committed `lib/` | [`tests/package-layout.spec.ts`](../../tests/package-layout.spec.ts), [`tests/profile-install.e2e.spec.ts`](../../tests/profile-install.e2e.spec.ts), clean `--dump-config`, Web/Headless boot, disable, and removal checks |
|
|
12
12
|
| P0-2 Upstream reuse | **Delivered** | [`src/version.ts`](../../src/version.ts), [`src/runtime-install.ts`](../../src/runtime-install.ts), [`scripts/upstream-manifest.mjs`](../../scripts/upstream-manifest.mjs), [`scripts/sync-upstream.mjs`](../../scripts/sync-upstream.mjs), [`vendor/agent-vision-toolkit/UPSTREAM_MANIFEST.json`](../../vendor/agent-vision-toolkit/UPSTREAM_MANIFEST.json) | [`tests/upstream.spec.ts`](../../tests/upstream.spec.ts), [`tests/runtime-install.spec.ts`](../../tests/runtime-install.spec.ts), build-time manifest verification |
|
|
13
13
|
| P0-3 Native tools | **Delivered** | Independent definitions in [`src/tools.ts`](../../src/tools.ts), Agent-scoped publication in [`src/exposure.ts`](../../src/exposure.ts), shared execution in [`src/runtime.ts`](../../src/runtime.ts), upstream adapter in [`src/upstream.ts`](../../src/upstream.ts) | [`tests/tools.spec.ts`](../../tests/tools.spec.ts), [`tests/runtime.spec.ts`](../../tests/runtime.spec.ts), [`tests/upstream.spec.ts`](../../tests/upstream.spec.ts), [`tests/vision-prompt-guard.spec.ts`](../../tests/vision-prompt-guard.spec.ts), real-profile progressive calls for all five P0 tools |
|
|
14
|
-
| P0-4 Configuration and Credentials | **Delivered** | [`src/config.ts`](../../src/config.ts), durable configured-root history in [`src/storage-history.ts`](../../src/storage-history.ts), per-operation resolution in [`src/runtime.ts`](../../src/runtime.ts), isolated environment construction in [`src/upstream.ts`](../../src/upstream.ts) | [`tests/config.spec.ts`](../../tests/config.spec.ts), [`tests/storage-history.spec.ts`](../../tests/storage-history.spec.ts), Credential/error/redaction cases in [`tests/runtime.spec.ts`](../../tests/runtime.spec.ts) and [`tests/errors.spec.ts`](../../tests/errors.spec.ts) |
|
|
14
|
+
| P0-4 Configuration and Credentials | **Delivered** | [`src/config.ts`](../../src/config.ts), durable configured-root history in [`src/storage-history.ts`](../../src/storage-history.ts), per-operation protocol/credential resolution in [`src/runtime.ts`](../../src/runtime.ts), isolated environment construction in [`src/upstream.ts`](../../src/upstream.ts) | [`tests/config.spec.ts`](../../tests/config.spec.ts), [`tests/storage-history.spec.ts`](../../tests/storage-history.spec.ts), OpenAI Responses/optional reasoning-effort transport plus Credential/error/redaction cases in [`tests/runtime.spec.ts`](../../tests/runtime.spec.ts) and [`tests/errors.spec.ts`](../../tests/errors.spec.ts) |
|
|
15
15
|
| P0-5 Skill lifecycle | **Delivered** | Readiness ordering, lifecycle abort, and global disposer in [`src/index.ts`](../../src/index.ts); per-Agent activation, restoration, and teardown in [`src/exposure.ts`](../../src/exposure.ts); bundled content in [`src/skill.ts`](../../src/skill.ts) | Agent isolation, native/direct/Code Mode activation, Session restoration, in-flight cancellation, and disposal cases in [`tests/tools.spec.ts`](../../tests/tools.spec.ts); progressive exposure plus disable/uninstall paths in [`tests/profile-install.e2e.spec.ts`](../../tests/profile-install.e2e.spec.ts) |
|
|
16
16
|
| P0-6 Text-only model results | **Delivered** | JSON output schemas and pure renderers in [`src/tools.ts`](../../src/tools.ts); canonical Artifact descriptors in [`src/artifacts.ts`](../../src/artifacts.ts); schema visibility derived from durable Skill-load evidence in [`src/exposure.ts`](../../src/exposure.ts) | Tool schema/presentation assertions in [`tests/tools.spec.ts`](../../tests/tools.spec.ts), request-by-request schema and model-facing transcript assertions in [`tests/profile-install.e2e.spec.ts`](../../tests/profile-install.e2e.spec.ts) |
|
|
17
17
|
| P0-7 Stable errors | **Delivered** | [`src/errors.ts`](../../src/errors.ts), boundary validation in [`src/paths.ts`](../../src/paths.ts), [`src/runtime.ts`](../../src/runtime.ts), and [`src/upstream.ts`](../../src/upstream.ts) | [`tests/errors.spec.ts`](../../tests/errors.spec.ts), [`tests/paths.spec.ts`](../../tests/paths.spec.ts), parser, timeout, cancellation, credential, and capacity cases in runtime/upstream tests |
|
|
@@ -25,7 +25,7 @@ This reference maps the DSH Vision Toolkit product brief's committed P0/P1 requi
|
|
|
25
25
|
| P1-2 Extended tools | **Delivered** | `vision_pixel_diff`, `vision_long_screenshot_ocr`, `vision_extract_foreground`, `vision_dominant_colors`, and `vision_html_screenshot` in [`src/tools.ts`](../../src/tools.ts) and [`src/runtime.ts`](../../src/runtime.ts) | P1 parser and runtime cases in [`tests/upstream.spec.ts`](../../tests/upstream.spec.ts) and [`tests/runtime.spec.ts`](../../tests/runtime.spec.ts); pixel-diff and long-OCR real-profile calls |
|
|
26
26
|
| P1-3 Dedicated Web presentation | **Delivered** | Browser plugin and dedicated cards in [`src/client/index.tsx`](../../src/client/index.tsx); presentation-only capability metadata in [`src/artifact-access.ts`](../../src/artifact-access.ts) | [`tests/client.spec.ts`](../../tests/client.spec.ts), safe preview tests in [`tests/artifact-access.spec.ts`](../../tests/artifact-access.spec.ts), Web visual/console QA |
|
|
27
27
|
| P1-4 Health checks | **Delivered** | Health/version runtime contracts in [`src/runtime.ts`](../../src/runtime.ts), same-origin Web actions in [`src/web.ts`](../../src/web.ts), and the Settings surface in [`src/client/index.tsx`](../../src/client/index.tsx); Settings distinguishes the lightweight authenticated `GET /models` probe from an explicit real multimodal request using the bundled diagnostic image, and these administrative diagnostics are deliberately absent from the model tool registry | Health and real-model success/failure cases in [`tests/runtime.spec.ts`](../../tests/runtime.spec.ts), explicit API/model-test behavior in [`tests/web.spec.ts`](../../tests/web.spec.ts) and [`tests/client.spec.ts`](../../tests/client.spec.ts), plus model-tool absence in [`tests/tools.spec.ts`](../../tests/tools.spec.ts) |
|
|
28
|
-
| P1-5 Settings | **Delivered** | Namespace/config in [`src/config.ts`](../../src/config.ts), prepare-before-swap manager in [`src/runtime-manager.ts`](../../src/runtime-manager.ts), private same-origin route in [`src/web.ts`](../../src/web.ts), section in [`src/client/index.tsx`](../../src/client/index.tsx) | [`tests/runtime-manager.spec.ts`](../../tests/runtime-manager.spec.ts), [`tests/web.spec.ts`](../../tests/web.spec.ts), [`tests/client.spec.ts`](../../tests/client.spec.ts), clean Web-profile save/restart QA |
|
|
28
|
+
| P1-5 Settings | **Delivered** | Namespace/config in [`src/config.ts`](../../src/config.ts), prepare-before-swap manager in [`src/runtime-manager.ts`](../../src/runtime-manager.ts), private same-origin route in [`src/web.ts`](../../src/web.ts), section in [`src/client/index.tsx`](../../src/client/index.tsx), including conditional OpenAI Responses reasoning effort | [`tests/runtime-manager.spec.ts`](../../tests/runtime-manager.spec.ts), [`tests/web.spec.ts`](../../tests/web.spec.ts), conditional display/validation/save/reload cases in [`tests/client.spec.ts`](../../tests/client.spec.ts), clean Web-profile save/restart QA |
|
|
29
29
|
| P1-6 Install and upgrade experience | **Delivered** | Bundle lifecycle in [`src/index.ts`](../../src/index.ts), content-addressed managed runtime in [`src/runtime-install.ts`](../../src/runtime-install.ts), generation manager in [`src/runtime-manager.ts`](../../src/runtime-manager.ts), and the registry/Profile-constrained Settings updater with DSH Web restart handoff in [`src/plugin-update.ts`](../../src/plugin-update.ts) | Runtime interruption/concurrency tests, frozen-lockfile rollback and runtime-readiness checks in [`tests/plugin-update.spec.ts`](../../tests/plugin-update.spec.ts), package-layout test, clean-profile lifecycle, persisted Settings and failed-candidate retention checks |
|
|
30
30
|
|
|
31
31
|
## Cross-cutting requirements
|
|
@@ -34,8 +34,8 @@ This reference maps the DSH Vision Toolkit product brief's committed P0/P1 requi
|
|
|
34
34
|
|---|---|
|
|
35
35
|
| Security | [`src/paths.ts`](../../src/paths.ts) confines reads and writes by realpath; [`src/runtime.ts`](../../src/runtime.ts) decodes and limits images before upload and structurally parses trace SVG before commit; [`src/upstream.ts`](../../src/upstream.ts) marks image text/instructions as untrusted in every direct and long-OCR visual-model prompt; [`src/tools.ts`](../../src/tools.ts) and [`src/skill.ts`](../../src/skill.ts) tell the text agent to treat derived output as evidence, not commands; [`src/artifact-access.ts`](../../src/artifact-access.ts) revalidates signed files on every read and sandboxes SVG; [`src/errors.ts`](../../src/errors.ts) redacts secrets. Path, symlink, format, size, XML/doctype, prompt guard, token-forgery, file-replacement, and CSP cases are automated. |
|
|
36
36
|
| Portability | Runtime and browser calls use argv vectors and Node filesystem/process APIs rather than POSIX shell composition. Managed preparation has `uv` and `venv`/pip paths; Python and Chrome discovery include platform-specific candidates. Package tests avoid machine-local dependency specifiers, and the checked-in fixture/profile flow is keyless. |
|
|
37
|
-
| Performance and cancellation | [`src/runtime.ts`](../../src/runtime.ts) applies
|
|
38
|
-
| Observability | [`src/runtime.ts`](../../src/runtime.ts) logs bounded tool name, outcome, total/upstream duration, image count/bytes/pixels, cache hits, model, and error category while excluding base64, credentials, headers, and unbounded upstream output. |
|
|
37
|
+
| Performance and cancellation | [`src/runtime.ts`](../../src/runtime.ts) applies independent queue-wait and execution deadlines (each `timeoutMs`), propagates `AbortSignal`, bounds per-session concurrency, rejects oversized decoded images before remote I/O, deduplicates repeated inputs within one glance operation, and keeps a one-entry content/configuration-keyed cache for the last successful identical glance in each live Session. Both this cache and the durable evidence cache include the effective Responses reasoning effort. [`src/index.ts`](../../src/index.ts) aborts plugin-owned calls before unregistering tools. Timeout, caller/plugin cancellation, effort isolation, cache-hit/miss, semaphore, and independent-session behavior are automated. |
|
|
38
|
+
| Observability | [`src/runtime.ts`](../../src/runtime.ts) logs bounded tool name, outcome, total/queue/upstream duration (including rejected upstream calls), image count/bytes/pixels, cache hits, model, and error category while excluding base64, credentials, headers, and unbounded upstream output. |
|
|
39
39
|
| Model-context economy | The profile-global runtime publishes one small bootstrap, while [`src/exposure.ts`](../../src/exposure.ts) mounts the ten execution schemas only in an Agent that loads `vision-skills` and hides the bootstrap afterward. Other Agents remain unchanged. Health, connection, and version diagnostics stay exclusively on the Web Settings seam and never become model tools. Unit tests cover Agent isolation and restoration; every real-profile tool flow asserts the initial and activated schema sets. |
|
|
40
40
|
| Maintainability | Every tool calls one [`VisionToolkitRuntime`](../../src/runtime.ts); DSH-specific adaptation remains in [`src/tools.ts`](../../src/tools.ts), [`src/exposure.ts`](../../src/exposure.ts), [`src/index.ts`](../../src/index.ts), [`src/web.ts`](../../src/web.ts), and [`src/client/index.tsx`](../../src/client/index.tsx), while the pinned upstream remains the sole algorithm source. Runtime readiness, Skill/bootstrap publication, and Agent-scoped schemas belong to one lifecycle generation. |
|
|
41
41
|
| HTML screenshot isolation | The pinned screenshot guard uses a disposable Chrome profile, `--use-mock-keychain`, and `--incognito`, removes the profile after each call, and keeps network disabled. [`tests/html-screenshot-guard.spec.ts`](../../tests/html-screenshot-guard.spec.ts) captures the actual argv and cleanup behavior. |
|
|
@@ -45,6 +45,7 @@ This reference maps the DSH Vision Toolkit product brief's committed P0/P1 requi
|
|
|
45
45
|
|
|
46
46
|
| Scenario | Expected behavior | Evidence |
|
|
47
47
|
|---|---|---|
|
|
48
|
+
| Workspace-local storage creation denied (`EACCES`, `EPERM`, `EROFS`) | Reject with category `path` and writable-session-workspace guidance; do not silently select another storage root. Windows shared storage remains disabled pending ACL verification | [`tests/workspace-storage.spec.ts`](../../tests/workspace-storage.spec.ts), existing fence coverage in [`tests/paths.spec.ts`](../../tests/paths.spec.ts) |
|
|
48
49
|
| Missing/invalid image, format, region, or path | Reject before upstream execution with an input, capacity, or path-safe error | [`tests/paths.spec.ts`](../../tests/paths.spec.ts), [`tests/runtime.spec.ts`](../../tests/runtime.spec.ts) |
|
|
49
50
|
| Missing Credential | Local tools remain usable; remote tools plus explicit API-connection and real-model tests report a redacted configuration/service action | [`tests/runtime.spec.ts`](../../tests/runtime.spec.ts), [`tests/web.spec.ts`](../../tests/web.spec.ts) |
|
|
50
51
|
| 401/403, 429, timeout, malformed output, or cancellation | Return a stable actionable category, preserve bounded diagnostics, and stop the request/subprocess | [`tests/errors.spec.ts`](../../tests/errors.spec.ts), [`tests/runtime.spec.ts`](../../tests/runtime.spec.ts), [`tests/upstream.spec.ts`](../../tests/upstream.spec.ts) |
|
|
@@ -58,7 +59,7 @@ This reference maps the DSH Vision Toolkit product brief's committed P0/P1 requi
|
|
|
58
59
|
| Scope | Status | Decision |
|
|
59
60
|
|---|---|---|
|
|
60
61
|
| P2 stable `ctx.visionToolkit` service and capability discovery | **Deferred by design** | The product brief requires at least one independent plugin consumer before stabilizing this API. `VisionToolkitRuntime` remains package-internal, so P0/P1 can evolve without creating a false compatibility promise. |
|
|
61
|
-
| P2 provider ecosystem | **Deferred by design** | The package supports its pinned upstream and one configured endpoint through OpenAI Chat Completions or Anthropic Messages; it does not prebuild an unused provider registry. |
|
|
62
|
+
| P2 provider ecosystem | **Deferred by design** | The package supports its pinned upstream and one configured endpoint through OpenAI Chat Completions, OpenAI Responses, or Anthropic Messages; it does not prebuild an unused provider registry. |
|
|
62
63
|
| P3 exploratory inputs and automation | **Out of scope** | Upload/drag-and-drop, camera/video/audio/document ingestion, interactive annotations, automatic clicking, remote clusters, model routing/voting, and cross-session caches are not part of this release contract. |
|
|
63
64
|
|
|
64
65
|
## Reproducible verification
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
| P0-1 标准 Profile Bundle | **已交付** | [`package.json`](../../package.json)、[`cordis.patch.yml`](../../cordis.patch.yml)、[`src/index.ts`](../../src/index.ts)、已提交的 `lib/` | [`tests/package-layout.spec.ts`](../../tests/package-layout.spec.ts)、[`tests/profile-install.e2e.spec.ts`](../../tests/profile-install.e2e.spec.ts)、干净 `--dump-config`、Web/Headless 启动、禁用和移除检查 |
|
|
12
12
|
| P0-2 上游复用 | **已交付** | [`src/version.ts`](../../src/version.ts)、[`src/runtime-install.ts`](../../src/runtime-install.ts)、[`scripts/upstream-manifest.mjs`](../../scripts/upstream-manifest.mjs)、[`scripts/sync-upstream.mjs`](../../scripts/sync-upstream.mjs)、[`vendor/agent-vision-toolkit/UPSTREAM_MANIFEST.json`](../../vendor/agent-vision-toolkit/UPSTREAM_MANIFEST.json) | [`tests/upstream.spec.ts`](../../tests/upstream.spec.ts)、[`tests/runtime-install.spec.ts`](../../tests/runtime-install.spec.ts)、构建时 manifest 验证 |
|
|
13
13
|
| P0-3 原生工具 | **已交付** | [`src/tools.ts`](../../src/tools.ts) 中的独立定义、[`src/exposure.ts`](../../src/exposure.ts) 中的 Agent 级发布、[`src/runtime.ts`](../../src/runtime.ts) 中的共享执行、[`src/upstream.ts`](../../src/upstream.ts) 中的上游适配 | [`tests/tools.spec.ts`](../../tests/tools.spec.ts)、[`tests/runtime.spec.ts`](../../tests/runtime.spec.ts)、[`tests/upstream.spec.ts`](../../tests/upstream.spec.ts)、[`tests/vision-prompt-guard.spec.ts`](../../tests/vision-prompt-guard.spec.ts)、全部五个 P0 工具的真实 Profile 渐进调用 |
|
|
14
|
-
| P0-4 配置与 Credential | **已交付** | [`src/config.ts`](../../src/config.ts)、[`src/storage-history.ts`](../../src/storage-history.ts) 中配置根目录历史的持久化、[`src/runtime.ts`](../../src/runtime.ts)
|
|
14
|
+
| P0-4 配置与 Credential | **已交付** | [`src/config.ts`](../../src/config.ts)、[`src/storage-history.ts`](../../src/storage-history.ts) 中配置根目录历史的持久化、[`src/runtime.ts`](../../src/runtime.ts) 中的逐操作协议/Credential 解析、[`src/upstream.ts`](../../src/upstream.ts) 中的隔离环境构造 | [`tests/config.spec.ts`](../../tests/config.spec.ts)、[`tests/storage-history.spec.ts`](../../tests/storage-history.spec.ts)、[`tests/runtime.spec.ts`](../../tests/runtime.spec.ts) 中的 OpenAI Responses/可选推理强度传输,以及该文件与 [`tests/errors.spec.ts`](../../tests/errors.spec.ts) 中的 Credential/错误/脱敏用例 |
|
|
15
15
|
| P0-5 skill 生命周期 | **已交付** | [`src/index.ts`](../../src/index.ts) 中的就绪顺序、生命周期中止和全局 disposer;[`src/exposure.ts`](../../src/exposure.ts) 中的逐 Agent 激活、恢复和释放;[`src/skill.ts`](../../src/skill.ts) 中的打包内容 | [`tests/tools.spec.ts`](../../tests/tools.spec.ts) 中的 Agent 隔离、原生/直接/Code Mode 激活、Session 恢复、在途取消和释放用例;[`tests/profile-install.e2e.spec.ts`](../../tests/profile-install.e2e.spec.ts) 中的渐进暴露、禁用和卸载路径 |
|
|
16
16
|
| P0-6 纯文本模型结果 | **已交付** | [`src/tools.ts`](../../src/tools.ts) 中的 JSON 输出 schema 与纯渲染函数;[`src/artifacts.ts`](../../src/artifacts.ts) 中的规范产物描述;[`src/exposure.ts`](../../src/exposure.ts) 中由持久 Skill 加载证据派生的 schema 可见性 | [`tests/tools.spec.ts`](../../tests/tools.spec.ts) 中的工具 schema/展示断言、[`tests/profile-install.e2e.spec.ts`](../../tests/profile-install.e2e.spec.ts) 中逐请求的 schema 与模型可见 transcript(文本记录)断言 |
|
|
17
17
|
| P0-7 稳定错误 | **已交付** | [`src/errors.ts`](../../src/errors.ts),以及 [`src/paths.ts`](../../src/paths.ts)、[`src/runtime.ts`](../../src/runtime.ts)、[`src/upstream.ts`](../../src/upstream.ts) 中的边界验证 | [`tests/errors.spec.ts`](../../tests/errors.spec.ts)、[`tests/paths.spec.ts`](../../tests/paths.spec.ts),以及运行时/上游测试中的解析、超时、取消、Credential 和容量用例 |
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
| P1-2 扩展工具 | **已交付** | [`src/tools.ts`](../../src/tools.ts) 和 [`src/runtime.ts`](../../src/runtime.ts) 中的 `vision_pixel_diff`、`vision_long_screenshot_ocr`、`vision_extract_foreground`、`vision_dominant_colors`、`vision_html_screenshot` | [`tests/upstream.spec.ts`](../../tests/upstream.spec.ts) 和 [`tests/runtime.spec.ts`](../../tests/runtime.spec.ts) 中的 P1 解析器/运行时用例;真实 Profile pixel-diff 和长截图 OCR 调用 |
|
|
26
26
|
| P1-3 专用 Web 展示 | **已交付** | [`src/client/index.tsx`](../../src/client/index.tsx) 中的浏览器插件与专用卡片;[`src/artifact-access.ts`](../../src/artifact-access.ts) 中仅供展示的能力元数据 | [`tests/client.spec.ts`](../../tests/client.spec.ts)、[`tests/artifact-access.spec.ts`](../../tests/artifact-access.spec.ts) 中的安全预览测试、Web 视觉/Console QA |
|
|
27
27
|
| P1-4 健康检查 | **已交付** | [`src/runtime.ts`](../../src/runtime.ts) 中的健康检查/版本运行时契约、[`src/web.ts`](../../src/web.ts) 中的同源 Web 操作,以及 [`src/client/index.tsx`](../../src/client/index.tsx) 中的 Settings 界面;Settings 明确区分携带凭据的轻量 `GET /models` 探测与使用自带诊断图片的显式真实多模态请求,这些管理诊断能力有意不进入模型工具注册表 | [`tests/runtime.spec.ts`](../../tests/runtime.spec.ts) 中的健康检查及真实模型成功/失败用例、[`tests/web.spec.ts`](../../tests/web.spec.ts) 与 [`tests/client.spec.ts`](../../tests/client.spec.ts) 中的显式 API/模型测试行为,以及 [`tests/tools.spec.ts`](../../tests/tools.spec.ts) 中的模型工具缺席断言 |
|
|
28
|
-
| P1-5 Settings | **已交付** | [`src/config.ts`](../../src/config.ts) 中的 namespace/配置、[`src/runtime-manager.ts`](../../src/runtime-manager.ts) 中的 prepare-before-swap manager、[`src/web.ts`](../../src/web.ts) 中的同源私有路由、[`src/client/index.tsx`](../../src/client/index.tsx)
|
|
28
|
+
| P1-5 Settings | **已交付** | [`src/config.ts`](../../src/config.ts) 中的 namespace/配置、[`src/runtime-manager.ts`](../../src/runtime-manager.ts) 中的 prepare-before-swap manager、[`src/web.ts`](../../src/web.ts) 中的同源私有路由、[`src/client/index.tsx`](../../src/client/index.tsx) 中包含条件显示 OpenAI Responses 推理强度的 Settings 分区 | [`tests/runtime-manager.spec.ts`](../../tests/runtime-manager.spec.ts)、[`tests/web.spec.ts`](../../tests/web.spec.ts)、[`tests/client.spec.ts`](../../tests/client.spec.ts) 中的条件显示/校验/保存/重载用例、干净 Web Profile 保存/重启 QA |
|
|
29
29
|
| P1-6 安装与升级体验 | **已交付** | [`src/index.ts`](../../src/index.ts) 中的 Bundle 生命周期、[`src/runtime-install.ts`](../../src/runtime-install.ts) 中的内容寻址 managed 运行时、[`src/runtime-manager.ts`](../../src/runtime-manager.ts) 中的 generation manager,以及 [`src/plugin-update.ts`](../../src/plugin-update.ts) 中按 registry/Profile 安全约束执行的 Settings 更新器和 DSH Web 重启交接 | 运行时中断/并发测试、[`tests/plugin-update.spec.ts`](../../tests/plugin-update.spec.ts) 中的 frozen-lockfile 回滚与 Runtime 就绪检查、软件包布局测试、干净 Profile 生命周期、Settings 持久化和失败候选保留检查 |
|
|
30
30
|
|
|
31
31
|
## 横切需求
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
|---|---|
|
|
35
35
|
| 安全 | [`src/paths.ts`](../../src/paths.ts) 通过 realpath 限制读写范围;[`src/runtime.ts`](../../src/runtime.ts) 在上传前解码并限制图片,并在提交前结构化解析 trace SVG;[`src/upstream.ts`](../../src/upstream.ts) 在每个直接和长截图 OCR 视觉模型提示词中把图片文字/指令标记为不可信;[`src/tools.ts`](../../src/tools.ts) 和 [`src/skill.ts`](../../src/skill.ts) 要求文本 agent 只把衍生输出当作证据而非命令;[`src/artifact-access.ts`](../../src/artifact-access.ts) 每次读取都重新验证签名文件并 sandbox SVG;[`src/errors.ts`](../../src/errors.ts) 对密钥脱敏。路径、符号链接、格式、大小、XML/doctype、提示词 guard、伪造 token、文件替换和 CSP 用例均有自动化覆盖。 |
|
|
36
36
|
| 可移植性 | 运行时和浏览器调用使用 argv 向量与 Node 文件系统/进程 API,不拼接 POSIX Shell。Managed 准备同时提供 `uv` 和 `venv`/pip 路径;Python 与 Chrome 探测包含平台候选。软件包测试拒绝机器本地依赖声明,已提交 fixture/Profile 流程无需真实 Key。 |
|
|
37
|
-
| 性能与取消 | [`src/runtime.ts`](../../src/runtime.ts)
|
|
38
|
-
| 可观察性 | [`src/runtime.ts`](../../src/runtime.ts)
|
|
37
|
+
| 性能与取消 | [`src/runtime.ts`](../../src/runtime.ts) 分别为排队和执行应用独立的硬截止时间(各为 `timeoutMs`)、传递 `AbortSignal`、限制每个会话的并发数、在远程 I/O 前拒绝解码后过大的图片、在一次 glance 操作中对重复输入去重,并为每个活动会话保留一条按内容/配置键控的最近成功相同 glance 缓存。该缓存和持久 evidence 缓存都会纳入实际生效的 Responses 推理强度。[`src/index.ts`](../../src/index.ts) 会在注销工具前中止插件拥有的调用。超时、调用方/插件取消、effort 隔离、缓存命中/未命中、信号量和独立会话行为均有自动化覆盖。 |
|
|
38
|
+
| 可观察性 | [`src/runtime.ts`](../../src/runtime.ts) 记录有界的工具名、结果、总耗时/排队耗时/上游耗时(包括上游调用被拒绝的路径)、图片数量/字节/像素、缓存命中、模型和错误类别,同时排除 base64、Credential、鉴权头和无界上游输出。 |
|
|
39
39
|
| 模型上下文经济性 | Profile 级运行时只发布一个很小的引导工具,[`src/exposure.ts`](../../src/exposure.ts) 仅为加载 `vision-skills` 的 Agent 挂载 10 个执行 schema,并在成功后隐藏引导工具。其他 Agent 不受影响。健康检查、连接测试和版本诊断只存在于 Web Settings 边界,永远不会成为模型工具。单元测试覆盖 Agent 隔离与恢复;每条真实 Profile 工具流程都会断言初始和激活后的 schema 集合。 |
|
|
40
40
|
| 可维护性 | 每个工具都调用同一个 [`VisionToolkitRuntime`](../../src/runtime.ts);DSH 专用适配保留在 [`src/tools.ts`](../../src/tools.ts)、[`src/exposure.ts`](../../src/exposure.ts)、[`src/index.ts`](../../src/index.ts)、[`src/web.ts`](../../src/web.ts) 和 [`src/client/index.tsx`](../../src/client/index.tsx),固定上游继续作为算法唯一来源。运行时就绪、skill/引导工具发布和 Agent 级 schema 属于同一个生命周期 generation。 |
|
|
41
41
|
| HTML 截图隔离 | 固定截图 guard 使用一次性 Chrome Profile、`--use-mock-keychain` 和 `--incognito`,每次调用后删除 Profile,并保持网络禁用。[`tests/html-screenshot-guard.spec.ts`](../../tests/html-screenshot-guard.spec.ts) 捕获实际 argv 与清理行为。 |
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
|
|
46
46
|
| 场景 | 预期行为 | 证据 |
|
|
47
47
|
|---|---|---|
|
|
48
|
+
| 工作区内存储创建被拒绝(`EACCES`、`EPERM`、`EROFS`) | 返回 `path` 错误并提示使用可写会话工作区,不自动切换存储根;Windows 共享存储在 ACL 校验就绪前保持禁用 | [`tests/workspace-storage.spec.ts`](../../tests/workspace-storage.spec.ts)、[`tests/paths.spec.ts`](../../tests/paths.spec.ts) 中已有的围栏测试 |
|
|
48
49
|
| 图片缺失/无效、格式、区域或路径错误 | 在执行上游前以输入、容量或路径安全错误拒绝 | [`tests/paths.spec.ts`](../../tests/paths.spec.ts)、[`tests/runtime.spec.ts`](../../tests/runtime.spec.ts) |
|
|
49
50
|
| Credential 缺失 | 本地工具保持可用;远程工具以及显式 API 连接/真实模型测试返回脱敏且可执行下一步的配置/服务结果 | [`tests/runtime.spec.ts`](../../tests/runtime.spec.ts)、[`tests/web.spec.ts`](../../tests/web.spec.ts) |
|
|
50
51
|
| 401/403、429、超时、畸形输出或取消 | 返回稳定且可执行下一步的类别,保留有界诊断信息,并停止请求/子进程 | [`tests/errors.spec.ts`](../../tests/errors.spec.ts)、[`tests/runtime.spec.ts`](../../tests/runtime.spec.ts)、[`tests/upstream.spec.ts`](../../tests/upstream.spec.ts) |
|
|
@@ -58,7 +59,7 @@
|
|
|
58
59
|
| 范围 | 状态 | 决策 |
|
|
59
60
|
|---|---|---|
|
|
60
61
|
| P2 稳定 `ctx.visionToolkit` 服务与能力发现 | **按设计推迟** | 产品需求要求至少一个独立插件消费方出现后再稳定该 API。`VisionToolkitRuntime` 保持包内部使用,使 P0/P1 可以继续演进,而不会制造虚假的兼容性承诺。 |
|
|
61
|
-
| P2 提供方生态 | **按设计推迟** | 本包支持固定上游,并通过 OpenAI Chat Completions 或 Anthropic Messages 使用一个已配置端点;不会预先构建无人使用的提供方注册表。 |
|
|
62
|
+
| P2 提供方生态 | **按设计推迟** | 本包支持固定上游,并通过 OpenAI Chat Completions、OpenAI Responses 或 Anthropic Messages 使用一个已配置端点;不会预先构建无人使用的提供方注册表。 |
|
|
62
63
|
| P3 探索性输入与自动化 | **范围外** | 上传/拖拽、摄像头/视频/音频/文档输入、交互式标注、自动点击、远程集群、模型路由/投票和跨 Session 缓存不属于本版本契约。 |
|
|
63
64
|
|
|
64
65
|
## 可复现验证
|
package/lib/client.js
CHANGED
|
@@ -87,6 +87,8 @@ const DEFAULT_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKi
|
|
|
87
87
|
const BUILT_IN_FREE_VISION_BASE_URL = 'https://vision.anionex.me/v1';
|
|
88
88
|
const BUILT_IN_FREE_VISION_CREDENTIAL = 'ANIONEX_FREE_VISION';
|
|
89
89
|
const BUILT_IN_FREE_VISION_MODEL = 'gemini-3.7-flash';
|
|
90
|
+
const MAX_REASONING_EFFORT_LENGTH = 64;
|
|
91
|
+
const REASONING_EFFORT_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/u;
|
|
90
92
|
const AIHUBMIX_TUTORIAL_URL_EN = 'https://github.com/Anionex/dsh-vision-toolkit/blob/main/docs/aihubmix-gemini-vision.md';
|
|
91
93
|
const AIHUBMIX_TUTORIAL_URL_ZH = 'https://github.com/Anionex/dsh-vision-toolkit/blob/main/docs/aihubmix-gemini-vision.zh.md';
|
|
92
94
|
const en = {
|
|
@@ -109,6 +111,9 @@ const en = {
|
|
|
109
111
|
credentialHint: 'The built-in free provider needs no user key. For a custom provider, this is the DSH credential reference used to store its key.',
|
|
110
112
|
model: 'Model',
|
|
111
113
|
protocol: 'API protocol',
|
|
114
|
+
reasoningEffort: 'Vision service reasoning effort',
|
|
115
|
+
reasoningEffortHint: 'Optional. Common values: none, minimal, low, medium, high, xhigh. Supported values and billing depend on the model or proxy; higher effort may increase tokens, latency, and cost. Requests send store:false, but you should still verify the provider\'s data-retention policy.',
|
|
116
|
+
reasoningEffortInvalid: 'Vision service reasoning effort must be at most 64 ASCII letters, digits, dots, underscores, or hyphens.',
|
|
112
117
|
anthropicThinking: 'Anthropic thinking',
|
|
113
118
|
anthropicThinkingHint: 'omit has the broadest compatibility. Use disabled or adaptive only when the selected model documents that mode; restore omit first after HTTP 400.',
|
|
114
119
|
userAgent: 'User-Agent',
|
|
@@ -297,6 +302,9 @@ const zh = {
|
|
|
297
302
|
credentialHint: '内置免费视觉服务无需用户密钥;切换到自定义服务时,此处是保存其密钥的 DSH 凭据名称。',
|
|
298
303
|
model: '模型名称',
|
|
299
304
|
protocol: 'API 协议',
|
|
305
|
+
reasoningEffort: '视觉服务推理强度',
|
|
306
|
+
reasoningEffortHint: '可选。常见值:none、minimal、low、medium、high、xhigh。实际支持值和计费由模型或代理决定;较高强度可能增加 token、延迟和费用。请求会发送 store:false,但仍应核查服务商的数据保留政策。',
|
|
307
|
+
reasoningEffortInvalid: '视觉服务推理强度最多 64 位,只能包含 ASCII 字母、数字、点、下划线或连字符。',
|
|
300
308
|
anthropicThinking: 'Anthropic thinking',
|
|
301
309
|
anthropicThinkingHint: 'omit 兼容性最好。仅当所选模型明确支持时使用 disabled 或 adaptive;遇到 HTTP 400 时先恢复 omit。',
|
|
302
310
|
userAgent: 'User-Agent',
|
|
@@ -854,8 +862,11 @@ function draftOf(value) {
|
|
|
854
862
|
credential: value.provider?.credential ?? BUILT_IN_FREE_VISION_CREDENTIAL,
|
|
855
863
|
model: value.provider?.model ?? BUILT_IN_FREE_VISION_MODEL,
|
|
856
864
|
protocol: value.provider?.protocol ?? 'openai',
|
|
865
|
+
reasoningEffort: value.provider?.protocol === 'responses' ? value.provider.reasoningEffort ?? '' : '',
|
|
857
866
|
anthropicThinking: value.provider?.anthropicThinking ?? 'omit',
|
|
858
867
|
userAgent: value.provider?.userAgent ?? DEFAULT_USER_AGENT,
|
|
868
|
+
providerHeaders: { ...(value.provider?.headers ?? {}) },
|
|
869
|
+
providerSessionHeaders: [...(value.provider?.sessionHeaders ?? [])],
|
|
859
870
|
language: value.language ?? 'zh',
|
|
860
871
|
timeoutMs: String(value.timeoutMs ?? 30000),
|
|
861
872
|
maxImageBytes: String(value.maxImageBytes ?? 4194304),
|
|
@@ -891,14 +902,22 @@ function apiKeyFailure(value, t) {
|
|
|
891
902
|
return undefined;
|
|
892
903
|
}
|
|
893
904
|
function valueOf(draft, t) {
|
|
905
|
+
const reasoningEffort = draft.reasoningEffort.trim();
|
|
906
|
+
if (draft.protocol === 'responses' && reasoningEffort.length > 0
|
|
907
|
+
&& (reasoningEffort.length > MAX_REASONING_EFFORT_LENGTH || !REASONING_EFFORT_PATTERN.test(reasoningEffort))) {
|
|
908
|
+
throw new Error(t('reasoningEffortInvalid'));
|
|
909
|
+
}
|
|
894
910
|
return {
|
|
895
911
|
provider: {
|
|
896
912
|
baseUrl: draft.baseUrl.trim(),
|
|
897
913
|
credential: draft.credential.trim(),
|
|
898
914
|
model: draft.model.trim(),
|
|
899
915
|
protocol: draft.protocol,
|
|
916
|
+
...(draft.protocol === 'responses' && reasoningEffort.length > 0 ? { reasoningEffort } : {}),
|
|
900
917
|
anthropicThinking: draft.anthropicThinking,
|
|
901
918
|
userAgent: draft.userAgent.trim(),
|
|
919
|
+
...(Object.keys(draft.providerHeaders).length === 0 ? {} : { headers: { ...draft.providerHeaders } }),
|
|
920
|
+
...(draft.providerSessionHeaders.length === 0 ? {} : { sessionHeaders: [...draft.providerSessionHeaders] }),
|
|
902
921
|
},
|
|
903
922
|
language: draft.language,
|
|
904
923
|
timeoutMs: positiveInteger(draft.timeoutMs, t('timeout'), t),
|
|
@@ -1157,7 +1176,7 @@ function LoadedSettings({ controller, t }) {
|
|
|
1157
1176
|
return;
|
|
1158
1177
|
void controller.applyUpdate(latestVersion);
|
|
1159
1178
|
};
|
|
1160
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: "dvt-settings", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-alert notice", children: t('externalNotice') }), !snapshot.writable ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert warning", children: t('readOnly') }) : null, draftError === undefined ? null : (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert error", children: draftError }), state.error === undefined ? null : (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert error", children: state.error }), state.message === 'saved' ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert success", children: t('saved') }) : null, state.message === 'restarting' && state.restart !== undefined ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert success", children: t('restarting', { version: state.restart.toVersion }) }) : null, state.message === 'manual-restart-required' && state.restart !== undefined ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert success", children: t('manualRestartRequired', { version: state.restart.toVersion }) }) : null, snapshot.runtime.lastError === undefined ? null : (0, jsx_runtime_1.jsxs)("div", { className: "dvt-alert error", children: [(0, jsx_runtime_1.jsx)("strong", { children: runtimeErrorTitle }), (0, jsx_runtime_1.jsx)("span", { children: snapshot.runtime.lastError })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel dvt-essential", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-panel-title", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { children: t('provider') }), (0, jsx_runtime_1.jsx)("p", { children: t('providerHint') })] }), (0, jsx_runtime_1.jsx)("span", { className: `dvt-badge ${snapshot.credential.configured ? 'ok' : 'error'}`, children: snapshot.credential.configured ? t('configured') : t('missing') })] }), (0, jsx_runtime_1.jsx)("p", { className: "dvt-tutorial-link", children: (0, jsx_runtime_1.jsx)("a", { href: aihubmixTutorialUrl, target: "_blank", rel: "noreferrer", children: t('aihubmixTutorial') }) }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('protocol'), children: (0, jsx_runtime_1.jsxs)("select", { disabled: !snapshot.writable || busy, value: draft.protocol, onChange: (event) => { update('protocol', event.target.value); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "openai", children: "OpenAI Chat Completions" }), (0, jsx_runtime_1.jsx)("option", { value: "anthropic", children: "Anthropic Messages" })] }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('baseUrl'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { disabled: !snapshot.writable || busy, value: draft.baseUrl, onChange: (event) => { update('baseUrl', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('model'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { disabled: !snapshot.writable || busy, value: draft.model, onChange: (event) => { update('model', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('apiKey'), hint: keyLocked ? t('apiKeyLocked') : snapshot.credential.source === undefined ? t('apiKeyHint') : `${t('apiKeyHint')} ${t('sourceHint', { source: t('source'), value: credentialSource(snapshot.credential.source, t) })}`, children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('apiKey'), type: "password", autoComplete: "new-password", disabled: busy || keyLocked, placeholder: snapshot.credential.configured ? t('apiKeyPlaceholderConfigured') : t('apiKeyPlaceholderMissing'), value: apiKey, onChange: (event) => { setApiKey(event.target.value); setDraftError(undefined); } }) })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-save-row", children: [(0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "primary", disabled: !canSave || busy, onClick: save, children: state.action === 'save' ? t('saving') : t('save') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "outline", disabled: busy, onClick: () => { void controller.load(); }, children: t('reload') })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-panel-title", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { children: t('health') }), (0, jsx_runtime_1.jsx)("p", { children: t('connectionHint') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-actions", children: [(0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", disabled: busy || !snapshot.runtime.ready, onClick: () => { void controller.runHealth('health'); }, children: state.action === 'health' ? t('testing') : t('runHealth') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", disabled: busy || !snapshot.runtime.ready, onClick: () => { void controller.runHealth('connection'); }, children: state.action === 'connection' ? t('testing') : t('testConnection') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "primary", disabled: busy || !snapshot.runtime.ready, onClick: () => { void controller.runHealth('model'); }, children: state.action === 'model' ? t('testingModel') : t('testModel') })] })] }), (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('saveBeforeTesting') }), state.health === undefined ? (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('notTested') }) : (0, jsx_runtime_1.jsx)("div", { className: "dvt-health-grid", children: Object.entries(state.health.checks).map(([name, check]) => {
|
|
1179
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "dvt-settings", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-alert notice", children: t('externalNotice') }), !snapshot.writable ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert warning", children: t('readOnly') }) : null, draftError === undefined ? null : (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert error", children: draftError }), state.error === undefined ? null : (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert error", children: state.error }), state.message === 'saved' ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert success", children: t('saved') }) : null, state.message === 'restarting' && state.restart !== undefined ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert success", children: t('restarting', { version: state.restart.toVersion }) }) : null, state.message === 'manual-restart-required' && state.restart !== undefined ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert success", children: t('manualRestartRequired', { version: state.restart.toVersion }) }) : null, snapshot.runtime.lastError === undefined ? null : (0, jsx_runtime_1.jsxs)("div", { className: "dvt-alert error", children: [(0, jsx_runtime_1.jsx)("strong", { children: runtimeErrorTitle }), (0, jsx_runtime_1.jsx)("span", { children: snapshot.runtime.lastError })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel dvt-essential", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-panel-title", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { children: t('provider') }), (0, jsx_runtime_1.jsx)("p", { children: t('providerHint') })] }), (0, jsx_runtime_1.jsx)("span", { className: `dvt-badge ${snapshot.credential.configured ? 'ok' : 'error'}`, children: snapshot.credential.configured ? t('configured') : t('missing') })] }), (0, jsx_runtime_1.jsx)("p", { className: "dvt-tutorial-link", children: (0, jsx_runtime_1.jsx)("a", { href: aihubmixTutorialUrl, target: "_blank", rel: "noreferrer", children: t('aihubmixTutorial') }) }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('protocol'), children: (0, jsx_runtime_1.jsxs)("select", { disabled: !snapshot.writable || busy, value: draft.protocol, onChange: (event) => { update('protocol', event.target.value); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "openai", children: "OpenAI Chat Completions" }), (0, jsx_runtime_1.jsx)("option", { value: "responses", children: "OpenAI Responses" }), (0, jsx_runtime_1.jsx)("option", { value: "anthropic", children: "Anthropic Messages" })] }) }), draft.protocol === 'responses' ? (0, jsx_runtime_1.jsx)(Field, { label: t('reasoningEffort'), hint: t('reasoningEffortHint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('reasoningEffort'), disabled: !snapshot.writable || busy, placeholder: "none / minimal / low / medium / high / xhigh", value: draft.reasoningEffort, onChange: (event) => { update('reasoningEffort', event.target.value); setDraftError(undefined); } }) }) : null, (0, jsx_runtime_1.jsx)(Field, { label: t('baseUrl'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { disabled: !snapshot.writable || busy, value: draft.baseUrl, onChange: (event) => { update('baseUrl', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('model'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { disabled: !snapshot.writable || busy, value: draft.model, onChange: (event) => { update('model', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('apiKey'), hint: keyLocked ? t('apiKeyLocked') : snapshot.credential.source === undefined ? t('apiKeyHint') : `${t('apiKeyHint')} ${t('sourceHint', { source: t('source'), value: credentialSource(snapshot.credential.source, t) })}`, children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('apiKey'), type: "password", autoComplete: "new-password", disabled: busy || keyLocked, placeholder: snapshot.credential.configured ? t('apiKeyPlaceholderConfigured') : t('apiKeyPlaceholderMissing'), value: apiKey, onChange: (event) => { setApiKey(event.target.value); setDraftError(undefined); } }) })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-save-row", children: [(0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "primary", disabled: !canSave || busy, onClick: save, children: state.action === 'save' ? t('saving') : t('save') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "outline", disabled: busy, onClick: () => { void controller.load(); }, children: t('reload') })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-panel-title", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { children: t('health') }), (0, jsx_runtime_1.jsx)("p", { children: t('connectionHint') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-actions", children: [(0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", disabled: busy || !snapshot.runtime.ready, onClick: () => { void controller.runHealth('health'); }, children: state.action === 'health' ? t('testing') : t('runHealth') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", disabled: busy || !snapshot.runtime.ready, onClick: () => { void controller.runHealth('connection'); }, children: state.action === 'connection' ? t('testing') : t('testConnection') }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "primary", disabled: busy || !snapshot.runtime.ready, onClick: () => { void controller.runHealth('model'); }, children: state.action === 'model' ? t('testingModel') : t('testModel') })] })] }), (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('saveBeforeTesting') }), state.health === undefined ? (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('notTested') }) : (0, jsx_runtime_1.jsx)("div", { className: "dvt-health-grid", children: Object.entries(state.health.checks).map(([name, check]) => {
|
|
1161
1180
|
const testTag = name === 'model' ? modelTestTag(state.health, check) : undefined;
|
|
1162
1181
|
return (0, jsx_runtime_1.jsxs)("div", { "data-status": check.status, children: [(0, jsx_runtime_1.jsx)("span", { children: t(HEALTH_NAME_KEYS[name] ?? 'health') }), testTag === undefined ? null : (0, jsx_runtime_1.jsx)("em", { className: "dvt-health-test-tag", "data-status": testTag.status, children: t(testTag.label) }), (0, jsx_runtime_1.jsx)("strong", { children: t(HEALTH_STATUS_KEYS[check.status]) }), (0, jsx_runtime_1.jsx)("p", { children: healthDetail(name, check.detail, t) })] }, name);
|
|
1163
1182
|
}) })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel dvt-update-panel", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-panel-title", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { children: t('updates') }), (0, jsx_runtime_1.jsx)("p", { children: t('updatesHint') })] }), (0, jsx_runtime_1.jsx)("span", { className: `dvt-badge ${pluginUpdate?.updateAvailable ? 'warning' : pluginUpdate !== undefined && pluginUpdate.supported ? 'ok' : ''}`, children: pluginUpdate?.updateAvailable ? t('updateAvailable') : pluginUpdate !== undefined && pluginUpdate.supported ? t('upToDate') : t('pluginVersion') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-update-grid", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { children: t('updateInstalled') }), (0, jsx_runtime_1.jsx)("strong", { children: snapshot.release.pluginVersion })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { children: t('updateLatest') }), (0, jsx_runtime_1.jsx)("strong", { children: latestVersion ?? '—' })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { children: t('updateProfile') }), (0, jsx_runtime_1.jsx)("strong", { children: updateCapability.profile ?? '—' })] })] }), !updateCapability.supported ? (0, jsx_runtime_1.jsxs)("div", { className: "dvt-alert warning", children: [(0, jsx_runtime_1.jsx)("strong", { children: t('updateUnsupported') }), (0, jsx_runtime_1.jsx)("span", { children: updateReason })] }) : null, updateCapability.supported && updateHasUnsavedChanges ? (0, jsx_runtime_1.jsx)("div", { className: "dvt-alert warning", children: t('updateSaveFirst') }) : null, pluginUpdate?.supported && pluginUpdate.updateAvailable && latestVersion !== undefined ? (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('updateAvailableDetail', { version: latestVersion }) }) : null, pluginUpdate?.supported && !pluginUpdate.updateAvailable && latestVersion !== undefined ? (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('upToDateDetail', { version: latestVersion }) }) : null, (0, jsx_runtime_1.jsx)("p", { className: "dvt-muted", children: t('manualUpdateHint') }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-manual-update", children: [(0, jsx_runtime_1.jsx)("code", { children: manualUpdateCommand }), (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { size: "sm", variant: "outline", onClick: copyManualUpdate, children: copiedCommand ? t('copied') : t('copy') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-actions", children: [(0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "outline", disabled: busy || !updateCheckSupported || state.restart !== undefined, onClick: () => { void controller.checkUpdate(); }, children: state.action === 'check-update' ? t('checkingUpdate') : t('checkUpdate') }), pluginUpdate?.supported && pluginUpdate.updateAvailable && latestVersion !== undefined ? (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Button, { variant: "primary", disabled: busy || state.restart !== undefined || updateHasUnsavedChanges, onClick: applyUpdate, children: state.action === 'apply-update' ? t('updatingPlugin') : t('updateNow') }) : null] })] }), (0, jsx_runtime_1.jsxs)("details", { className: "dvt-advanced", children: [(0, jsx_runtime_1.jsxs)("summary", { children: [(0, jsx_runtime_1.jsxs)("span", { children: [(0, jsx_runtime_1.jsx)("strong", { children: t('advanced') }), (0, jsx_runtime_1.jsx)("small", { children: t('advancedHint') })] }), (0, jsx_runtime_1.jsx)("span", { className: "dvt-details-chevron", "aria-hidden": "true", children: "\u2304" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-advanced-body", children: [(0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-panel-title", children: (0, jsx_runtime_1.jsx)("h3", { children: t('provider') }) }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('credential'), hint: t('credentialHint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('credential'), disabled: !snapshot.writable || busy, value: draft.credential, onChange: (event) => { update('credential', event.target.value); } }) }), draft.protocol === 'anthropic' ? (0, jsx_runtime_1.jsx)(Field, { label: t('anthropicThinking'), hint: t('anthropicThinkingHint'), children: (0, jsx_runtime_1.jsxs)("select", { "aria-label": t('anthropicThinking'), value: draft.anthropicThinking, onChange: (event) => { update('anthropicThinking', event.target.value); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "omit", children: "omit (widest compatibility)" }), (0, jsx_runtime_1.jsx)("option", { value: "disabled", children: "disabled (model support required)" }), (0, jsx_runtime_1.jsx)("option", { value: "adaptive", children: "adaptive (model support required)" })] }) }) : null, (0, jsx_runtime_1.jsx)(Field, { label: t('userAgent'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { value: draft.userAgent, onChange: (event) => { update('userAgent', event.target.value); } }) })] })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-panel-title", children: (0, jsx_runtime_1.jsx)("h3", { children: t('limits') }) }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('language'), children: (0, jsx_runtime_1.jsxs)("select", { value: draft.language, onChange: (event) => { update('language', event.target.value); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "zh", children: "\u4E2D\u6587" }), (0, jsx_runtime_1.jsx)("option", { value: "en", children: "English" })] }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('timeout'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { inputMode: "numeric", value: draft.timeoutMs, onChange: (event) => { update('timeoutMs', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('maxBytes'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { inputMode: "numeric", value: draft.maxImageBytes, onChange: (event) => { update('maxImageBytes', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('maxPixels'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { inputMode: "numeric", value: draft.maxImagePixels, onChange: (event) => { update('maxImagePixels', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('concurrency'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { inputMode: "numeric", value: draft.concurrency, onChange: (event) => { update('concurrency', event.target.value); } }) })] })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-panel-title", children: (0, jsx_runtime_1.jsx)("h3", { children: t('imageInput') }) }), (0, jsx_runtime_1.jsxs)("label", { className: "dvt-check", children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: draft.hiddenVariants, disabled: !snapshot.writable || busy, onChange: (event) => { update('hiddenVariants', event.target.checked); } }), (0, jsx_runtime_1.jsx)("span", { children: t('hiddenVariantsLabel') }), (0, jsx_runtime_1.jsx)("small", { children: t('hiddenVariantsHint') })] })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsx)("div", { className: "dvt-panel-title", children: (0, jsx_runtime_1.jsx)("h3", { children: t('storage') }) }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('storageDir'), hint: t('storageDirHint'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { "aria-label": t('storageDir'), placeholder: "/tmp/dsh-vision-toolkit", value: draft.storageDir, onChange: (event) => { update('storageDir', event.target.value); } }) }), (0, jsx_runtime_1.jsx)(Field, { label: t('allowedDirs'), hint: t('allowedDirsHint'), children: (0, jsx_runtime_1.jsx)("textarea", { rows: 3, value: draft.allowedDirs, onChange: (event) => { update('allowedDirs', event.target.value); } }) })] })] }), (0, jsx_runtime_1.jsxs)("section", { className: "dvt-panel", children: [(0, jsx_runtime_1.jsxs)("div", { className: "dvt-panel-title", children: [(0, jsx_runtime_1.jsx)("h3", { children: t('runtime') }), (0, jsx_runtime_1.jsx)("span", { className: `dvt-badge ${snapshot.runtime.ready ? 'ok' : 'error'}`, children: snapshot.runtime.ready ? snapshot.runtime.upstream?.source === 'managed' ? t('runtimeManaged') : snapshot.runtime.upstream?.source === 'external' ? t('runtimeExternal') : t('runtimeReady') : t('runtimeUnavailable') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-form-grid", children: [(0, jsx_runtime_1.jsx)(Field, { label: t('runtimeMode'), children: (0, jsx_runtime_1.jsxs)("select", { value: draft.runtimeMode, onChange: (event) => { update('runtimeMode', event.target.value); }, children: [(0, jsx_runtime_1.jsx)("option", { value: "managed", children: t('runtimeManaged') }), (0, jsx_runtime_1.jsx)("option", { value: "external", children: t('runtimeExternal') })] }) }), draft.runtimeMode === 'external' ? (0, jsx_runtime_1.jsx)(Field, { label: t('toolkitPath'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { value: draft.toolkitPath, onChange: (event) => { update('toolkitPath', event.target.value); } }) }) : null, (0, jsx_runtime_1.jsx)(Field, { label: t('python'), children: (0, jsx_runtime_1.jsx)(dsh_client_ui_primitives_1.Input, { placeholder: "python3", value: draft.python, onChange: (event) => { update('python', event.target.value); } }) })] }), snapshot.runtime.upstream === undefined ? null : (0, jsx_runtime_1.jsxs)("div", { className: "dvt-runtime-facts", children: [(0, jsx_runtime_1.jsx)("code", { children: snapshot.runtime.upstream.path }), (0, jsx_runtime_1.jsxs)("code", { children: [snapshot.runtime.upstream.python, " \u00B7 ", snapshot.runtime.upstream.pythonVersion] }), (0, jsx_runtime_1.jsx)("code", { children: snapshot.runtime.upstream.runtimeHome })] })] })] })] }), (0, jsx_runtime_1.jsxs)("footer", { className: "dvt-settings-footer", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { className: "dvt-kicker", children: t('pluginKind') }), (0, jsx_runtime_1.jsx)("h2", { children: t('settingsTitle') }), (0, jsx_runtime_1.jsx)("p", { children: t('settingsIntro') })] }), (0, jsx_runtime_1.jsxs)("div", { className: "dvt-release", children: [(0, jsx_runtime_1.jsxs)("span", { children: [t('pluginVersion'), " ", (0, jsx_runtime_1.jsx)("strong", { children: snapshot.release.pluginVersion })] }), (0, jsx_runtime_1.jsxs)("span", { children: [t('upstreamVersion'), " ", (0, jsx_runtime_1.jsx)("strong", { children: snapshot.release.upstreamVersion })] }), (0, jsx_runtime_1.jsxs)("span", { children: [t('activeGeneration'), " ", (0, jsx_runtime_1.jsx)("strong", { children: t('activeGenerationValue', { generation: snapshot.runtime.generation }) })] })] })] })] }));
|