@mokuyoaxis/dsh-iris 0.2.6 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +23 -1
  2. package/README.en.md +28 -19
  3. package/README.md +27 -18
  4. package/bin/dsh-iris.js +140 -26
  5. package/docs/AGENT_QUICKSTART.md +5 -5
  6. package/docs/ARCHITECTURE.md +9 -3
  7. package/docs/CLI_MANAGEMENT.md +1 -1
  8. package/docs/CORE_RUNTIME_CONTRACT.md +36 -5
  9. package/docs/CORE_SKILLS.md +62 -0
  10. package/docs/DSH_COMPATIBILITY.md +4 -4
  11. package/docs/DSH_CORE_MIGRATION.md +4 -2
  12. package/docs/DSH_PROVIDER_CONNECTIONS.md +1 -1
  13. package/docs/DSH_RUNTIME.md +36 -0
  14. package/docs/FUNCTION_MODEL_POOLS.md +1 -1
  15. package/docs/HEADLESS_CLI.md +11 -3
  16. package/docs/HOST_DOCTOR.md +13 -1
  17. package/docs/IMAGE_FLOW.md +71 -0
  18. package/docs/LOCAL_API.md +60 -0
  19. package/docs/LOCAL_API_CONTRACT.md +115 -0
  20. package/docs/LOCAL_CREATION.md +50 -0
  21. package/docs/PROVIDER_HEALTH.md +1 -1
  22. package/docs/ROADMAP.md +14 -2
  23. package/docs/SECURITY.md +9 -1
  24. package/docs/SYNC_DELIVERY_RECOVERY.md +2 -0
  25. package/docs/VISION_CLI.md +1 -1
  26. package/docs/WEB_PROTOTYPE.md +47 -0
  27. package/docs/releases/0.2.7.md +62 -0
  28. package/lib/actions.js +79 -68
  29. package/lib/api.js +38 -38
  30. package/lib/application-runtime.js +186 -0
  31. package/lib/client.js +89 -3
  32. package/lib/core-artifact-store.js +5 -0
  33. package/lib/core-maintenance.js +25 -2
  34. package/lib/core-runtime.js +19 -0
  35. package/lib/core-tasks.js +62 -8
  36. package/lib/creation-request-store.js +159 -0
  37. package/lib/doctor.js +12 -1
  38. package/lib/dsh-core-adapter.js +666 -572
  39. package/lib/dsh-host-adapter.js +4 -1
  40. package/lib/flow-store.js +195 -0
  41. package/lib/image-flow.js +188 -0
  42. package/lib/index.js +59 -53
  43. package/lib/local-api-projection.js +27 -0
  44. package/lib/local-api.js +381 -0
  45. package/lib/local-creation.js +85 -0
  46. package/lib/private-storage.js +12 -0
  47. package/lib/provider-task-runner.js +22 -2
  48. package/lib/render-address.js +35 -0
  49. package/lib/render.js +52 -13
  50. package/lib/skill-installation.js +199 -0
  51. package/lib/workbench-artifacts.js +1 -2
  52. package/lib/workbench-management.js +8 -9
  53. package/package.json +3 -2
  54. package/skills/iris-core/SKILL.md +40 -0
  55. package/skills/iris-core/agents/openai.yaml +4 -0
  56. package/skills/iris-core/references/cli.md +95 -0
  57. package/skills/iris-core/references/tasks.md +58 -0
  58. package/user_guide.md +14 -8
package/CHANGELOG.md CHANGED
@@ -4,6 +4,27 @@
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.2.7] - 2026-10-22
8
+
9
+ 0.2.7 修复宿主截图集成,并提供宿主无关的图片流程、本地 API 与显式 Core Skill 安装器。DSH 沿用 12 个准确版本;独立 Web 仍是面向 0.3.0 的源码演示,尚未连接真实执行入口。英文发布说明附中文摘要,见 [0.2.7](docs/releases/0.2.7.md)。
10
+
11
+ - 增加显式 `skills install/status/uninstall`:从当前 npm 包复制 `iris-core` 到 Kimi 或自定义扫描目录,记录版本/摘要并保护本地修改;升级与卸载保留根外备份,状态只读,安装/卸载支持 `--dry-run`。不添加 npm 自动安装钩子,不改 DSH Skill 注册或模型/MCP 配置。见 [Skill 安装](docs/CORE_SKILLS.md#显式安装检查与卸载)。
12
+
13
+ - 增加宿主无关的 `iris-core` Skill:英文正文附中文标题,指导图片导入、生成、续作、任务查询与作品导出,通过现有 CLI 执行,配套参考随候选包分发。DSH 原有三项 Skill 与注册保持;不代表正式 MCP 或独立 UI 已接入。见 [Core Skill](docs/CORE_SKILLS.md)。
14
+
15
+ - DSH 的 Core 装配接入实例应用队列:工具、动作和工作台显式使用插件实例,连接解析引用、任务观察与启动接管归实例持有。卸载取消并等待在途操作和租约释放,失败保留现场;原根、数据格式、协议和计费规则保持,legacy 存储另行迁移。见 [DSH 实例运行时](docs/DSH_RUNTIME.md)。
16
+ - 增加显式本地创建 API:新空 Flow 根执行 `api init` 后,`api serve --allow-local-writes` 可上传静态图片和创建 Run;同 Key 使用持久意图/预留身份接回原结果,不重复创建。请求/业务提交共用一次 writer,补齐未完成引用与 writer 版本检查,成功作品显式删除后不自动复活。默认只读,未开放生成/取消接口,不迁移原根。见 [本地创建](docs/LOCAL_CREATION.md)。
17
+ - 本地 API 拒绝未完成上传时先发送完整错误,再丢弃剩余正文并关闭,避免 Windows 客户端先收到连接重置。大小、鉴权、并发和超时限制保持;拒绝不创建作品或消费 Key,停止服务可关闭尚未结束的正文。
18
+ - 增加显式 `api serve` 默认只读本地入口:固定回环监听、独立 Bearer 与 Host/Origin 边界,提供模型/流程白名单投影、受控图片及已选择作品下载。会话 Token 写入新建的根外私有文件,不进入 stdout 或 URL;GET/HEAD 不写 Core、不调用 Provider,执行/取消尚未开放。Demo 保留原样。见 [只读 API](docs/LOCAL_API.md)。
19
+ - 源码树保留面向 0.3.0 的独立 Web Demo,展示选图、状态、并排/叠图比较、选择与下载,运行详情默认折叠;界面与交互细节留到 0.3.0 阶段统一润色。当前仅使用内存演示和内置样图,不连接模型、不计费、不写 Core,也未接入只读 API;写操作契约仍待实现。见 [Demo 说明](docs/WEB_PROTOTYPE.md)和 [API 契约](docs/LOCAL_API_CONTRACT.md)。
20
+ - 增加显式 CLI 固定改图流程:专用数据根、私有输入、稳定步骤/Task 绑定、继续执行、比较、选择和导出;同步保护 Flow 引用,缓存原任务的完成结果并复用预留作品 ID。未知受理不重新生成,旧根不自动迁移,尚未接入真实 Web 执行入口。见 [图片流程](docs/IMAGE_FLOW.md)。
21
+ - 0.3 前置:增加实例级写队列与操作级取消/释放,CLI 图片生成、导入、比较、作品查询/导出和单项任务查询开始共用内部应用运行时;命令参数与既有 Task/Artifact 格式保持。
22
+ - 宿主 HTML 截图优先使用显式 `DSH_WEB_BASE`,否则从公开的监听 host/port/protocol 取得实际地址;兼容动态端口和通配监听地址。旧宿主没有可靠地址时仍回落到 3080 并在诊断中提示,公开媒体链接保持原基址约定。
23
+ - 宿主诊断区分浏览器接口存在与截图实测,新增主动“截图自检”、取消、错误说明及折叠预览;自检复用 HTML action 生成一张本地作品,不调用模型,默认诊断和刷新仍不打开浏览器。
24
+ - 修复 DSH HTML 截图与浏览器保存目录限制的冲突:显式请求 PNG 并接收返回的图片数据,经现有 Core 保存作品,不再要求浏览器写入 Iris 数据根。
25
+ - 修正浏览器调用的取消参数,补齐打开会话期间取消后的关闭与临时目录清理;导航、截图失败时保留原始错误。
26
+ - 宿主截图返回数据须为有效 PNG/base64,最多 64 MiB;实际尺寸沿用独立 Chromium 后端的单边 16384、4000 万像素上限。HTML 沙箱与资源限制保持。
27
+
7
28
  ## [0.2.6] - 2026-10-19
8
29
 
9
30
  0.2.6 的重点是减少重复配置,并让模型候选与当前连接保持一致:复用 DSH 连接、可选功能自动池、按需目录同步和能力检测。旧名单、空配置及任务恢复边界保持。升级后需重载 DSH 服务端并刷新页面,见 [发布说明](docs/releases/0.2.6.md)。
@@ -360,7 +381,8 @@ Iris 0.2.0 要求 Node.js ≥ 22.0.0,DSH 插件仅支持 `0.2.0-rc.2`;旧宿
360
381
  - 上传采用流式限额、临时 `.part` 文件和原子落盘,失败时会清理未完成文件。
361
382
  - 修改状态的路由拒绝明确的跨站请求,媒体文件继续使用随机能力令牌访问。
362
383
 
363
- [Unreleased]: https://github.com/mokuyoaxis/dsh-iris/compare/v0.2.6...HEAD
384
+ [Unreleased]: https://github.com/mokuyoaxis/dsh-iris/compare/v0.2.7...HEAD
385
+ [0.2.7]: https://github.com/mokuyoaxis/dsh-iris/compare/v0.2.6...v0.2.7
364
386
  [0.2.6]: https://github.com/mokuyoaxis/dsh-iris/compare/v0.2.5...v0.2.6
365
387
  [0.2.5]: https://github.com/mokuyoaxis/dsh-iris/compare/v0.2.4...v0.2.5
366
388
  [0.2.4]: https://github.com/mokuyoaxis/dsh-iris/compare/v0.2.3...v0.2.4
package/README.en.md CHANGED
@@ -22,7 +22,7 @@ dsh-iris gives agents, the Iris workbench and a standalone CLI image, video, mus
22
22
 
23
23
  Music uses a Chat Completions interface with inline audio, confirmed model uses and function policies, configurable 502 retries, and playback/export. See [Music setup and usage](docs/MUSIC.md) (Chinese).
24
24
 
25
- Iris 0.2.6 can [reference an existing DSH provider connection](docs/DSH_PROVIDER_CONNECTIONS.md) (Chinese), resolving its endpoint and API key per operation without storing a second key. It adds [automatic, selected-only, and disabled function pools](docs/FUNCTION_MODEL_POOLS.md) (Chinese): confirming a model's uses can enable automatic selection without a second assignment step. Discovery alone never expands the pool; existing lists and empty configurations retain their behavior. Selection uses stable priority with safe failover, without a round-robin cursor.
25
+ Iris 0.2.7 can [reference an existing DSH provider connection](docs/DSH_PROVIDER_CONNECTIONS.md) (Chinese), resolving its endpoint and API key per operation without storing a second key. It adds [automatic, selected-only, and disabled function pools](docs/FUNCTION_MODEL_POOLS.md) (Chinese): confirming a model's uses can enable automatic selection without a second assignment step. Discovery alone never expands the pool; existing lists and empty configurations retain their behavior. Selection uses stable priority with safe failover, without a round-robin cursor.
26
26
 
27
27
  Provider directories refresh on demand with a 10-minute successful cache. Confirmed model-not-found failures exclude that connection's model and capability from subsequent requests while preserving configured models and historical success. Directory details start collapsed; failed refreshes retain the last complete snapshot, and new models still require approval of their uses.
28
28
 
@@ -50,15 +50,21 @@ These captures come from an Android browser connected to DSH under Termux/PRoot
50
50
 
51
51
  The project is at an early stage; interfaces and configuration formats may still change between releases.
52
52
 
53
- This documentation covers **Iris 0.2.6**. Pin the version when installing or upgrading, and check the host compatibility range below first.
53
+ This document describes **Iris 0.2.7**. Pin the version when installing or upgrading, and check the host compatibility range below first.
54
54
 
55
- Iris 0.2.6 provides a DSH plugin and a headless CLI sharing tasks, artifacts and model routing. It adds connection reuse, optional automatic function pools, directory synchronization and capability checks. Image import, native Qwen editing, music, three agent skills and synchronous delivery recovery remain available. Core internals have not been frozen as a public SDK.
55
+ Iris 0.2.7 provides a DSH plugin and a headless CLI sharing tasks, artifacts and model routing. This release fixes host screenshots and adds persistent image workflows, a local API, and explicit Core Skill installation. Connection reuse, function pools, directory synchronization, and existing media features remain available. Core internals have not been frozen as a public SDK.
56
56
 
57
- > Version support: Iris 0.2.6 retains the 12 exact DSH versions declared in 0.2.5; see the [compatibility matrix](docs/DSH_COMPATIBILITY.md). Node.js must be at least 22.0.0 and meet any higher host requirement. Connection reuse also requires the relevant public host services.
57
+ Version 0.2.7 includes host screenshot adaptation, address resolution, an explicit screenshot check, an internal application runtime with [DSH instance ownership](docs/DSH_RUNTIME.md), a [first persistent image-edit workflow](docs/IMAGE_FLOW.md), a [local API that is read-only by default](docs/LOCAL_API.md), and [explicit local uploads and Run creation](docs/LOCAL_CREATION.md) (Chinese); see the [0.2.7 release notes](docs/releases/0.2.7.md). The source checkout retains a [standalone web demo for 0.3.0](docs/WEB_PROTOTYPE.md) (Chinese) to show the intended workflow. UI and interaction polish is deferred to the 0.3.0 development phase. The demo uses built-in samples without model calls or charges; execution/cancellation APIs and real workbench integration remain on the [0.3 roadmap](docs/ROADMAP.md#03-及以后).
58
58
 
59
- ## What's new in 0.2.6 and existing features
59
+ > Version support: Iris 0.2.7 retains the 12 exact DSH versions declared in 0.2.5; see the [compatibility matrix](docs/DSH_COMPATIBILITY.md). Node.js must be at least 22.0.0 and meet any higher host requirement. Connection reuse also requires the relevant public host services.
60
60
 
61
- **Choose a connection, confirm model uses, then call a function.** Automatic pools use stable priority and safe failover, without cross-request round-robin.
61
+ ## What's new in 0.2.7 and existing features
62
+
63
+ **Improve host screenshots and use image workflows and Skills outside DSH.** Function pools retain stable priority and safe failover, without cross-request round-robin.
64
+
65
+ - **Host screenshots:** consume PNG data through Core, respect browser download-directory restrictions, use actual listening metadata, and offer an explicit screenshot check.
66
+ - **Image workflows and local API:** persist CLI steps, tasks, comparisons, and selection. The API is read-only by default; uploads and creation require opt-in, while HTTP execution/cancellation remain future work.
67
+ - **Core Skill installation:** distribute `iris-core` with CLI image guidance; explicitly install to Kimi or a supplied scan directory, preserve local changes, and archive managed updates/removals.
62
68
 
63
69
  - **Persistent image import:** save an existing image through the workbench, agent, or CLI for later vision, editing, and export. The source file stays intact and no model is called.
64
70
  - **Native Qwen editing:** use supported DashScope Qwen image editing models as well as chat image models; edited images retain a link to their source.
@@ -68,7 +74,7 @@ Iris 0.2.6 provides a DSH plugin and a headless CLI sharing tasks, artifacts and
68
74
 
69
75
  **Upgrades preserve your allowed scope:** existing lists and missing or empty assignments do not become automatic. New directory entries still require confirmation of their uses; absence alone does not prove a model unavailable. Explicitly add host vision or enable it in the automatic vision pool. Existing tasks retain their bindings. See [function pools](docs/FUNCTION_MODEL_POOLS.md).
70
76
 
71
- See the [0.2.6 release notes](docs/releases/0.2.6.md) for upgrade steps and validation limits, and the [CHANGELOG](CHANGELOG.md). Older-host support was restored in [0.2.5](docs/releases/0.2.5.md); image import arrived in [0.2.4](docs/releases/0.2.4.md), and music and Skills in [0.2.3](docs/releases/0.2.3.md).
77
+ See the [0.2.7 release notes](docs/releases/0.2.7.md) for upgrade steps and validation limits, and the [CHANGELOG](CHANGELOG.md). Older-host support was restored in [0.2.5](docs/releases/0.2.5.md); image import arrived in [0.2.4](docs/releases/0.2.4.md), and music and Skills in [0.2.3](docs/releases/0.2.3.md).
72
78
 
73
79
  Future work includes additional protocol adapters and creative workflows; see the [roadmap](docs/ROADMAP.md#后续功能方向).
74
80
 
@@ -80,11 +86,11 @@ Future work includes additional protocol adapters and creative workflows; see th
80
86
  | Entry point | Choose a pinned release under "Quickest start" below | `dsh-iris run ...` — see [Headless CLI](docs/HEADLESS_CLI.md) |
81
87
  | Data location | `$DSH_HOME/iris/v1/` (Core data root `core-v0`) | Any absolute path passed via `--data-root` |
82
88
  | Automatic observation | Yes (bounded DSH host ticks + restart takeover) | Explicit `task observe`; 0.2.2 also offers bounded `task wait` |
83
- | Dependencies | A DSH version in the compatibility matrix, and Node.js ≥ 22 meeting host requirements | Node.js ≥ 22 and `sharp` only (`ffmpeg` for video frames) |
89
+ | Dependencies | A DSH version in the compatibility matrix, and Node.js ≥ 22 meeting host requirements | Node.js ≥ 22 and `sharp` (`ffmpeg` and `ffprobe` for video frames) |
84
90
 
85
- Both paths read and write the same Core Task/Artifact facts: point the CLI at the `core-v0` data root of a DSH profile to inspect/export DSH-generated artifacts, and vice versa. Only one writer is allowed per data root.
91
+ Both paths read and write the same Core Task/Artifact facts: point the CLI at the `core-v0` data root of a DSH profile to inspect or export artifacts, and vice versa. Read-only inspection can coexist with a writer; export is a write operation and returns busy if that data root already has a writer.
86
92
 
87
- > Install size note: image processing depends on `sharp` and its prebuilt binaries (~27 MB). On uncommon platforms (e.g. some ARM proot environments) a failed sharp install is detected by `dsh-iris doctor`; image actions become unavailable while everything else keeps working.
93
+ > Image processing depends on `sharp` and its platform binaries. Failure to load `sharp` can also prevent the CLI or plugin from starting. Repair the dependency installation first, then run `dsh-iris doctor` to check the environment.
88
94
 
89
95
  ## Optional legacy configuration import
90
96
 
@@ -94,34 +100,34 @@ If you already have an ai-paint configuration, set `IRIS_IMPORT_WORKBENCH_CONFIG
94
100
 
95
101
  ## Quickest start
96
102
 
97
- Check the host with `dsh --version` and ensure `pnpm` is on your PATH. Iris and DSH have independent version numbers; Iris 0.2.6 retains the twelve exact hosts listed in the [compatibility matrix](docs/DSH_COMPATIBILITY.md).
103
+ Check the host with `dsh --version` and ensure `pnpm` is on your PATH. Iris and DSH have independent version numbers; Iris 0.2.7 retains the twelve exact hosts listed in the [compatibility matrix](docs/DSH_COMPATIBILITY.md).
98
104
 
99
105
  | Your DSH | Iris choice | Source |
100
106
  |---|---|---|
101
- | Exact 0.1.2 / 0.1.5 / 0.1.7 / 0.2 versions in the matrix | `0.2.6` | Pinned npm version |
107
+ | Exact 0.1.2 / 0.1.5 / 0.1.7 / 0.2 versions in the matrix | `0.2.7` | Pinned npm version |
102
108
  | Versions outside the matrix | Not yet validated | Check the actual host interfaces |
103
109
 
104
- ### DSH plugin: install 0.2.6
110
+ ### DSH plugin: install 0.2.7
105
111
 
106
112
  Install the pinned release into your Web profile:
107
113
 
108
114
  ```bash
109
- dsh plugin --profile web add @mokuyoaxis/dsh-iris@0.2.6
115
+ dsh plugin --profile web add @mokuyoaxis/dsh-iris@0.2.7
110
116
  dsh web
111
117
  ```
112
118
 
113
119
  The plugin loads on the next DSH start. The web UI defaults to `http://127.0.0.1:3080`; open the complete URL if startup output includes authentication parameters. Reload the backend and refresh the web client after updating.
114
120
 
115
- ### Standalone CLI: install 0.2.6
121
+ ### Standalone CLI: install 0.2.7
116
122
 
117
123
  Install in your own project without DSH:
118
124
 
119
125
  ```bash
120
- npm install @mokuyoaxis/dsh-iris@0.2.6
126
+ npm install @mokuyoaxis/dsh-iris@0.2.7
121
127
  npx dsh-iris --help
122
128
  ```
123
129
 
124
- Download the package separately with `npm pack @mokuyoaxis/dsh-iris@0.2.6`. Users who need historical Iris0.1.4 can still pin it; see the matrix for its DSH/Node requirements. It lacks the current Core media CLI and later features.
130
+ Download the package separately with `npm pack @mokuyoaxis/dsh-iris@0.2.7`. Users who need historical Iris0.1.4 can still pin it; see the matrix for its DSH/Node requirements. It lacks the current Core media CLI and later features.
125
131
 
126
132
  ### Choosing a version in dsh-market
127
133
 
@@ -168,7 +174,7 @@ After installing the plugin, add a provider in the Iris workbench and assign mod
168
174
 
169
175
  ## DeepSeek Harness adaptation
170
176
 
171
- dsh-iris remains a native DSH plugin. Iris 0.2.6 registers 17 agent tools, including import, editing, and music, and uses host routing, attachments, models, and lifecycle services. The web client contributes settings, composer, and global-overlay UI. Iris does not start another service or listen on an extra port.
177
+ dsh-iris remains a native DSH plugin. Iris 0.2.7 registers 17 agent tools, including import, editing, and music, and uses host routing, attachments, models, and lifecycle services. The web client contributes settings, composer, and global-overlay UI. Iris does not start another service or listen on an extra port.
172
178
 
173
179
  Save an uploaded or local image as a persistent Artifact for viewing, editing and export; see [image import](docs/IMAGE_IMPORT.md) (Chinese).
174
180
 
@@ -181,7 +187,8 @@ Save an uploaded or local image as a persistent Artifact for viewing, editing an
181
187
  | `0.2.3` | Only `0.2.0-rc.2` | Adds music and a third skill; check actual loading with Host Doctor after upgrading |
182
188
  | `0.2.4` | `0.2.0-rc.2` or `0.2.1-alpha.4` | 17 tools / 3 skills; actual SDK, loader, and client slot isolation checks; full browser and hot reload checks remain separate |
183
189
  | `0.2.5` | 12 exact versions across the 0.1.2 / 0.1.5 / 0.1.7 / 0.2 lines; see the matrix | Official SDK isolation checks including Music and Qwen dispatch; old profiles also load the actual installed package |
184
- | `0.2.6` | Retains the 12 exact versions declared in 0.2.5 | Adds connection reuse, function pools and directories; connection reuse requires public host services; see this version's release notes |
190
+ | `0.2.7` | Retains the 12 exact versions declared in 0.2.6 | Screenshot fixes, instance runtime, image workflows, local API, and Core Skill; standalone Web remains a source-only demo |
191
+ | `0.2.6` | Retains the 12 exact versions declared in 0.2.5 | Adds connection reuse, function pools and directories; connection reuse requires public host services; see that release's notes |
185
192
 
186
193
  See the [compatibility matrix](docs/DSH_COMPATIBILITY.md) for the current scope and [rc.2 adaptation notes](docs/DSH_RC2_ADAPTATION.md) for historical changes. npm 0.1.4 does not contain these later adaptations.
187
194
 
@@ -331,6 +338,8 @@ Legacy media links carry random capability tokens. Core media uses random Artifa
331
338
 
332
339
  ## Agent Skills
333
340
 
341
+ Version 0.2.7 adds a host-independent [`iris-core`](skills/iris-core/SKILL.md) for image import, generation, editing, and retrieval through the existing standalone CLI. Instructions are in English with Chinese titles. Explicit `skills install/status/uninstall` commands support Kimi or a supplied scan directory, preserve local changes, and archive managed updates/removals. See [loading and scope](docs/CORE_SKILLS.md). These resources and commands ship in npm 0.2.7. The three DSH skills below remain registered as before; a formal MCP integration is still future work.
342
+
334
343
  The DSH skill registry receives three bundled skills:
335
344
 
336
345
  | Skill | When to use |
package/README.md CHANGED
@@ -44,15 +44,21 @@ dsh-iris 为 Agent、Iris 工作台和独立 CLI 提供图像、视频、音乐
44
44
 
45
45
  项目目前处于早期版本,接口和配置格式仍可能随版本迭代调整。
46
46
 
47
- 当前文档对应 **Iris 0.2.6**。安装与升级请固定版本,并先核对下方的宿主兼容范围。
47
+ 本文对应 **Iris 0.2.7**。安装与升级请固定版本,并先核对下方的宿主兼容范围。
48
48
 
49
- Iris 0.2.6 同时提供 DSH 插件与无 DSH 的 CLI,共用任务、产物和模型路由。新增连接复用、可选功能自动池、目录同步和能力检测;图片导入、原生 Qwen 改图、音乐、三项 Agent Skills 和同步交付恢复继续可用。Core 内部接口尚未冻结为公共 SDK。
49
+ Iris 0.2.7 同时提供 DSH 插件与无 DSH 的 CLI,共用任务、产物和模型路由。本版修复宿主截图集成,并增加持久图片流程、本地 API 和显式 Core Skill 安装;连接复用、功能池、目录同步与既有媒体能力继续可用。Core 内部接口尚未冻结为公共 SDK。
50
50
 
51
- > 版本支持:Iris 0.2.6 沿用 0.2.5 的 12 个准确 DSH 版本,见 [兼容矩阵](docs/DSH_COMPATIBILITY.md)。保留旧 DSH 时仍需 Node.js ≥22.0.0,宿主若要求更高,以宿主为准。连接复用另需宿主提供对应公开接口。
51
+ 0.2.7 包含宿主截图适配、实际截图地址、主动截图自检、内部应用运行时与 [DSH 实例装配](docs/DSH_RUNTIME.md)、[首版持久改图流程](docs/IMAGE_FLOW.md)、[默认只读本地 API](docs/LOCAL_API.md)及主动启用的 [本地上传与创建](docs/LOCAL_CREATION.md),见 [0.2.7 发布说明](docs/releases/0.2.7.md)。源码树保留 [面向 0.3.0 的独立 Web Demo](docs/WEB_PROTOTYPE.md),用于展示操作方向;界面与交互细节将在 0.3.0 阶段统一润色。Demo 使用内置样例,不连接模型、不计费;API 执行/取消与真实工作台联调继续按 [0.3 路线图](docs/ROADMAP.md#03-及以后)推进。
52
52
 
53
- ## 0.2.6 更新重点与现有能力
53
+ > 版本支持:Iris 0.2.7 沿用 0.2.5 的 12 个准确 DSH 版本,见 [兼容矩阵](docs/DSH_COMPATIBILITY.md)。保留旧 DSH 时仍需 Node.js ≥22.0.0,宿主若要求更高,以宿主为准。连接复用另需宿主提供对应公开接口。
54
54
 
55
- **选一次连接、确认模型用途,再按功能调用。** 自动池采用稳定顺序和安全切换,没有跨请求轮转。
55
+ ## 0.2.7 更新重点与现有能力
56
+
57
+ **截图适配更完整,图片流程和 Skills 可脱离 DSH 使用。** 功能池继续采用稳定顺序和安全切换,没有跨请求轮转。
58
+
59
+ - **宿主截图**:直接接收 PNG 并保存为 Core 作品,兼容浏览器下载目录限制;按实际监听地址导航,提供主动截图自检。
60
+ - **图片流程与本地 API**:CLI 持久保存步骤、任务、比较和选择;API 默认只读,上传与创建需显式开启,HTTP 执行/取消尚未开放。
61
+ - **Core Skill 安装**:随包 `iris-core` 通过 CLI 指导图片创作,可显式安装到 Kimi 或指定扫描目录,保护本地修改并保留备份。
56
62
 
57
63
  - **图片导入**:通过工作台、Agent或CLI把已有图片保存为持久作品,继续看图、改图和导出;保留源文件,不调用模型。
58
64
  - **原生 Qwen 改图**:除聊天生图外,支持DashScope Qwen带图编辑,修改版另存为关联原图的新作品。
@@ -62,7 +68,7 @@ Iris 0.2.6 同时提供 DSH 插件与无 DSH 的 CLI,共用任务、产物和
62
68
 
63
69
  **升级保持已有调用范围**:旧名单和缺失/空分配不会自动启用;需主动选择自动池。新发现模型仍要确认用途,目录未列出不等于下线。DSH 默认视觉需明确加入名单或开启自动池的宿主视觉选项;已有任务按原绑定恢复。详见 [功能模型池](docs/FUNCTION_MODEL_POOLS.md)。
64
70
 
65
- 升级与验证边界见 [0.2.6 发布说明](docs/releases/0.2.6.md) 和 [CHANGELOG](CHANGELOG.md)。旧宿主支持见 [0.2.5](docs/releases/0.2.5.md),图片续作见 [0.2.4](docs/releases/0.2.4.md),音乐与 Skills 见 [0.2.3](docs/releases/0.2.3.md)。
71
+ 升级与验证边界见 [0.2.7 发布说明](docs/releases/0.2.7.md) 和 [CHANGELOG](CHANGELOG.md)。旧宿主支持见 [0.2.5](docs/releases/0.2.5.md),图片续作见 [0.2.4](docs/releases/0.2.4.md),音乐与 Skills 见 [0.2.3](docs/releases/0.2.3.md)。
66
72
 
67
73
  音乐的接口格式、模型分配与使用示例见 [Music](docs/MUSIC.md)。其他协议与后续能力见 [路线图](docs/ROADMAP.md#后续功能方向)。
68
74
 
@@ -74,11 +80,11 @@ Iris 0.2.6 同时提供 DSH 插件与无 DSH 的 CLI,共用任务、产物和
74
80
  | 入口 | 按下文「最快开始」选择固定版本 | `dsh-iris run ...`,见 [Headless CLI](docs/HEADLESS_CLI.md) |
75
81
  | 数据位置 | `$DSH_HOME/iris/v1/`(Core 数据根 `core-v0`) | 显式 `--data-root` 指定的任意绝对路径 |
76
82
  | 自动观察 | 有(DSH Host 有界节拍 + 重启接管) | 显式 `task observe`;0.2.2 另提供有界 `task wait` |
77
- | 依赖 | 兼容矩阵中的DSH、Node.js ≥22(满足宿主要求) | 仅 Node.js ≥22与`sharp`(视频抽帧另需`ffmpeg`) |
83
+ | 依赖 | 兼容矩阵中的DSH、Node.js ≥22(满足宿主要求) | Node.js ≥22与`sharp`(视频抽帧另需`ffmpeg`、`ffprobe`) |
78
84
 
79
- 两条路径读写同一份 Core Task/Artifact 事实:CLI 指向 DSH profile 的 `core-v0` 数据根即可 inspect/export DSH 生成的作品,反之亦然。同一数据根只允许一个写者。
85
+ 两条路径读写同一份 Core Task/Artifact 事实:CLI 可指向 DSH profile 的 `core-v0` 数据根检查或导出作品,反之亦然。只读检查可与写者共存;导出也属于写操作,同一数据根已有写者时会返回 busy。
80
86
 
81
- > 安装体积提示:图像处理依赖 `sharp` 及其预编译二进制(约 27MB)。冷门平台(如部分 ARM proot 环境)安装失败时 `dsh-iris doctor` 可检出,图像类动作不可用,其余能力不受影响。
87
+ > 图像处理依赖 `sharp` 及对应平台的二进制。`sharp` 无法加载时也可能阻止 CLI 或插件启动;请先修复依赖安装,再运行 `dsh-iris doctor` 检查环境。
82
88
 
83
89
  ## 可选的旧配置导入
84
90
 
@@ -94,34 +100,34 @@ Iris 0.2.6 同时提供 DSH 插件与无 DSH 的 CLI,共用任务、产物和
94
100
 
95
101
  ## 最快开始
96
102
 
97
- 先用 `dsh --version` 核对宿主版本,并确认 `pnpm` 在 PATH 中。Iris 与 DSH 的版本号独立;0.2.6 沿用 [兼容矩阵](docs/DSH_COMPATIBILITY.md) 列出的 12 个准确版本。
103
+ 先用 `dsh --version` 核对宿主版本,并确认 `pnpm` 在 PATH 中。Iris 与 DSH 的版本号独立;0.2.7 沿用 [兼容矩阵](docs/DSH_COMPATIBILITY.md) 列出的 12 个准确版本。
98
104
 
99
105
  | 你的 DSH | Iris 选择 | 安装来源 |
100
106
  |---|---|---|
101
- | 矩阵中的0.1.2/0.1.5/0.1.7/0.2线准确版本 | `0.2.6` | 固定 npm 版本 |
107
+ | 矩阵中的0.1.2/0.1.5/0.1.7/0.2线准确版本 | `0.2.7` | 固定 npm 版本 |
102
108
  | 未列入矩阵的版本 | 尚未验收 | 按实际宿主接口另行检查 |
103
109
 
104
- ### DSH插件:安装0.2.6
110
+ ### DSH插件:安装0.2.7
105
111
 
106
112
  安装固定版本到 Web profile:
107
113
 
108
114
  ```bash
109
- dsh plugin --profile web add @mokuyoaxis/dsh-iris@0.2.6
115
+ dsh plugin --profile web add @mokuyoaxis/dsh-iris@0.2.7
110
116
  dsh web
111
117
  ```
112
118
 
113
119
  插件在下一次 DSH 启动时装载。Web UI 默认位于 `http://127.0.0.1:3080`;启动输出带认证参数时请打开完整 URL。更新后需重新加载后端并刷新客户端。
114
120
 
115
- ### 独立CLI:安装0.2.6
121
+ ### 独立CLI:安装0.2.7
116
122
 
117
123
  在自己的项目中安装,无需 DSH:
118
124
 
119
125
  ```bash
120
- npm install @mokuyoaxis/dsh-iris@0.2.6
126
+ npm install @mokuyoaxis/dsh-iris@0.2.7
121
127
  npx dsh-iris --help
122
128
  ```
123
129
 
124
- 需要单独下载时运行`npm pack @mokuyoaxis/dsh-iris@0.2.6`。仍需保留历史Iris0.1.4时可以固定其版本;历史DSH/Node范围见兼容矩阵,0.1.4不包含当前Core媒体CLI及后续新功能。
130
+ 需要单独下载时运行`npm pack @mokuyoaxis/dsh-iris@0.2.7`。仍需保留历史Iris0.1.4时可以固定其版本;历史DSH/Node范围见兼容矩阵,0.1.4不包含当前Core媒体CLI及后续新功能。
125
131
 
126
132
  ### dsh-market 安装时的版本选择
127
133
 
@@ -169,7 +175,7 @@ DSH 的 profile 与插件命令由[官方安装说明](https://github.com/deepse
169
175
 
170
176
  ## DeepSeek Harness 适配
171
177
 
172
- dsh-iris仍按DSH原生插件装载。0.2.6服务端注册17个Agent工具,包含图片导入、改图与音乐;使用宿主提供的路由、附件、模型和生命周期能力。Web客户端接入设置页、会话输入区与全局悬浮层,不启动额外服务或监听端口。
178
+ dsh-iris仍按DSH原生插件装载。0.2.7服务端注册17个Agent工具,包含图片导入、改图与音乐;使用宿主提供的路由、附件、模型和生命周期能力。Web客户端接入设置页、会话输入区与全局悬浮层,不启动额外服务或监听端口。
173
179
 
174
180
  上传图片或本地图片可保存为持久作品,继续看图、改图及导出,见 [图片导入](docs/IMAGE_IMPORT.md)。
175
181
 
@@ -182,7 +188,8 @@ dsh-iris仍按DSH原生插件装载。0.2.6服务端注册17个Agent工具,包
182
188
  | `0.2.3` | 仅 `0.2.0-rc.2` | 增加音乐工具和第三项 Skill;升级后用宿主诊断检查实际装载 |
183
189
  | `0.2.4` | `0.2.0-rc.2` 或 `0.2.1-alpha.4` | 17工具/3项Skill;两版实际SDK、加载器与界面插槽隔离验收,完整浏览器/热更新另验 |
184
190
  | `0.2.5` | 0.1.2/0.1.5/0.1.7/0.2线的12个准确版本,见兼容矩阵 | 每版官方SDK隔离检查,包含Music与Qwen工具链;旧profile另通过实际安装包装载 |
185
- | `0.2.6` | 沿用0.2.5的12个准确版本 | 新增连接复用、功能池与目录;连接引用按宿主公开服务检测,具体边界见本版发布说明 |
191
+ | `0.2.7` | 沿用0.2.6的12个准确版本 | 截图修复、实例运行时、图片流程、本地 API 和 Core Skill;独立 Web 仍为源码 Demo |
192
+ | `0.2.6` | 沿用0.2.5的12个准确版本 | 新增连接复用、功能池与目录;连接引用按宿主公开服务检测,具体边界见该版发布说明 |
186
193
 
187
194
  当前声明与验收范围见 [版本兼容](docs/DSH_COMPATIBILITY.md),早期rc.2修正见 [历史适配记录](docs/DSH_RC2_ADAPTATION.md);npm0.1.4不包含这些后续适配。
188
195
 
@@ -332,6 +339,8 @@ npx @mokuyoaxis/dsh-iris doctor --json
332
339
 
333
340
  ## Agent Skills
334
341
 
342
+ 0.2.7 新增宿主无关的 [`iris-core`](skills/iris-core/SKILL.md),通过现有独立 CLI 完成图片导入、生成、编辑与原任务/作品取回;英文正文附中文标题。其他 harness 可用 `dsh-iris skills install --target kimi` 或明确扫描目录安装,配套状态检查、冲突保护与可恢复卸载,见 [Core Skill 说明](docs/CORE_SKILLS.md)。这些资源和命令随 npm 0.2.7 分发,DSH 继续使用下方三项 Skills;正式 MCP 仍待实现。
343
+
335
344
  插件通过 DSH Skill registry 注册三项随包 Skill:
336
345
 
337
346
  | Skill | 适用场景 |
package/bin/dsh-iris.js CHANGED
@@ -3,6 +3,7 @@ import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { createCommandService } from '../lib/command-service.js';
5
5
  import { createCoreRuntime } from '../lib/core-runtime.js';
6
+ import { createApplicationRuntime } from '../lib/application-runtime.js';
6
7
  import { recoverCoreWriterLease } from '../lib/core-lease-recovery.js';
7
8
  import { doctor, formatDoctorReport } from '../lib/doctor.js';
8
9
  import { loadProviderCatalog, providerCatalogSnapshot, catalogCapabilitySnapshot, imageCandidatesFromCatalog, musicCandidatesFromCatalog, providerForTaskFromCatalog, providerTaskBinding, transcribeCandidatesFromCatalog, ttsCandidatesFromCatalog, videoCandidatesFromCatalog } from '../lib/provider-catalog.js';
@@ -20,6 +21,8 @@ import { probeVisionModel } from '../lib/vision-model-routing.js';
20
21
  import { RED_TEST_IMAGE } from '../lib/vision.js';
21
22
  import { parseModelRef, modelRef as qualifiedModelRef } from '../lib/models.js';
22
23
  import { coreTaskWaitFinished } from '../lib/task-wait.js';
24
+ import { createLocalReadApi, writeLocalApiTokenFile } from '../lib/local-api.js';
25
+ import { manageCoreSkill } from '../lib/skill-installation.js';
23
26
 
24
27
  function trackedProviderAdapter(provider, options, task) {
25
28
  return createConfiguredProviderAdapter(provider, {
@@ -29,10 +32,10 @@ function trackedProviderAdapter(provider, options, task) {
29
32
  });
30
33
  }
31
34
 
32
- async function generationCatalog(options, capability, modelRef) {
35
+ async function generationCatalog(options, capability, modelRef, signal = cliAbortController.signal) {
33
36
  const file = required(options['provider-config'], 'provider-config');
34
37
  const catalog = loadProviderCatalog(file);
35
- if (!modelRef) await refreshCatalogDirectories(file, catalog, capability, { signal: cliAbortController.signal });
38
+ if (!modelRef) await refreshCatalogDirectories(file, catalog, capability, { signal });
36
39
  return catalog;
37
40
  }
38
41
 
@@ -48,7 +51,13 @@ function usage() {
48
51
  return [
49
52
  'Usage:',
50
53
  ' JSON input: --input <json>, --input-file <path>, or --input - (stdin).',
54
+ ' dsh-iris skills <install|status|uninstall> --target kimi [--directory <absolute-skills-directory>]',
55
+ ' dsh-iris skills <install|status|uninstall> --directory <absolute-skills-directory>',
56
+ ' install/uninstall accept --dry-run. Copy only iris-core; preserve local changes and archive managed updates/removals outside the scan directory. status is read-only and does not verify host discovery.',
51
57
  ' dsh-iris doctor [--data-root <absolute-path>] [--json]',
58
+ ' dsh-iris api init --data-root <empty-existing-flow-root>',
59
+ ' dsh-iris api serve --data-root <existing-flow-root> --token-file <new-absolute-path-outside-root> [--provider-config <absolute-path>] [--port <0..65535>] [--allow-local-writes]',
60
+ ' Explicit loopback-only API, read-only by default. Local writes only upload images/create Runs, without generation. The session token is saved privately; stdout contains no token.',
52
61
  ' dsh-iris runtime recover --data-root <absolute-path> --confirm-stale-pid <pid>',
53
62
  ' dsh-iris providers list --provider-config <absolute-path>',
54
63
  ' dsh-iris capabilities list --provider-config <absolute-path>',
@@ -87,12 +96,19 @@ function usage() {
87
96
  ' dsh-iris artifact export-many --data-root <absolute-path> --input <json> --output <absolute-directory>',
88
97
  ' dsh-iris artifact export <id> --data-root <absolute-path> --output <absolute-path>',
89
98
  ' dsh-iris artifact rebuild --data-root <absolute-path>',
99
+ ' dsh-iris flow init --data-root <new-absolute-path>',
100
+ ' dsh-iris flow create --data-root <absolute-path> --provider-config <absolute-path> --input <image-edit-json>',
101
+ ' Import the source image after flow init; use explicit model_ref and n=1. create saves private inputs but never submits.',
102
+ ' dsh-iris flow <list|inspect> [run-id] --data-root <absolute-path>',
103
+ ' dsh-iris flow continue <run-id> --data-root <absolute-path> [--provider-config <absolute-path>]',
104
+ ' dsh-iris flow select <run-id> --data-root <absolute-path> --input <json with artifact_id>',
105
+ ' dsh-iris flow export <run-id> --data-root <absolute-path> --output <absolute-path>',
90
106
  ' dsh-iris core <delete|cleanup> --data-root <absolute-path> [--input <json>] [--confirm-delete]',
91
107
  ' Default is a read-only preview. delete selects task_ids/artifact_ids; cleanup executes only explicitly selected preview paths.',
92
108
  ' dsh-iris core transactions --data-root <absolute-path>',
93
109
  ' dsh-iris core restore <transaction-id> --data-root <absolute-path>',
94
110
  '',
95
- 'Commands never start DSH. run image and the task observe/redeliver/cancel/retry verbs contact a provider only when explicitly requested; credentials come from a private file. observe polls once, redeliver only re-downloads, and cancel is recorded as canceled only when the provider explicitly confirms; none of them ever submits an existing task again. task retry creates a NEW task that may incur duplicate generation billing and requires --confirm-billing; the prompt must be supplied again because Core never persists prompts.'
111
+ 'Commands never start DSH. run image and the task observe/redeliver/cancel/retry verbs contact a provider only when explicitly requested; credentials come from a private file. observe polls once, redeliver only re-downloads, and cancel is recorded as canceled only when the provider explicitly confirms; none of them ever submits an existing task again. task retry creates a NEW task that may incur duplicate generation billing and requires --confirm-billing; the prompt must be supplied again because Task records never persist prompts. Flow inputs do retain the prompt privately. flow continue may perform the first submission or observe/redeliver the original task, but never resubmits a task with an Attempt.'
96
112
  ].join('\n');
97
113
  }
98
114
 
@@ -193,12 +209,105 @@ async function withRuntime(mode, dataRoot, callback, ports = {}) {
193
209
  return withRawRuntime(mode, dataRoot, (runtime) => callback(createCommandService(runtime, ports)));
194
210
  }
195
211
 
212
+ async function withApplication(dataRoot, callback, resolveImageCandidates, resolveTaskAdapter) {
213
+ const application = createApplicationRuntime({ dataRoot: required(dataRoot, 'data-root'), resolveImageCandidates, resolveTaskAdapter });
214
+ const unregister = registerRuntime(application);
215
+ try { return await callback(application); }
216
+ finally { try { await application.dispose(); } finally { unregister(); } }
217
+ }
218
+
196
219
  async function main(args) {
197
220
  if (!args.length || args.includes('--help') || args.includes('-h')) {
198
221
  console.log(usage());
199
222
  return args.length ? 0 : 2;
200
223
  }
201
224
 
225
+ if (args[0] === 'skills') {
226
+ const rest = args.slice(2), dryRunFlags = rest.filter(value => value === '--dry-run');
227
+ if (dryRunFlags.length > 1) throw new CliUsageError('参数重复:--dry-run');
228
+ const options = flags(rest.filter(value => value !== '--dry-run'), ['target', 'directory']);
229
+ let result;
230
+ try {
231
+ result = manageCoreSkill({ action: args[1], target: options.target, directory: options.directory, dryRun: dryRunFlags.length === 1 });
232
+ } catch (error) {
233
+ if (error.code === 'IRIS_SKILL_OPTIONS_INVALID') throw new CliUsageError(error.message);
234
+ throw error;
235
+ }
236
+ console.log(JSON.stringify(result, null, 2));
237
+ return args[1] === 'status' && result.status !== 'current' ? 1 : 0;
238
+ }
239
+
240
+ if (args[0] === 'api') {
241
+ if (args[1] === 'init') {
242
+ const options = flags(args.slice(2), ['data-root']);
243
+ const suppliedRoot = required(options['data-root'], 'data-root');
244
+ if (!path.isAbsolute(suppliedRoot)) throw new CliUsageError('data-root 必须是已初始化的空 Flow 根绝对路径');
245
+ let dataRoot;
246
+ try { dataRoot = fs.realpathSync(suppliedRoot); }
247
+ catch (_) { throw new CliUsageError('请先在新的专用数据根执行 flow init'); }
248
+ const initialized = await withApplication(dataRoot, application => application.execute('creation.init', {}, { signal: cliAbortController.signal }));
249
+ console.log(JSON.stringify(initialized));
250
+ return 0;
251
+ }
252
+ if (args[1] !== 'serve') throw new CliUsageError('不支持的 api 子命令');
253
+ const rest = args.slice(2), localWriteFlags = rest.filter(value => value === '--allow-local-writes');
254
+ if (localWriteFlags.length > 1) throw new CliUsageError('参数重复:--allow-local-writes');
255
+ const allowLocalWrites = localWriteFlags.length === 1;
256
+ const options = flags(rest.filter(value => value !== '--allow-local-writes'), ['data-root', 'token-file', 'provider-config', 'port']);
257
+ const suppliedRoot = required(options['data-root'], 'data-root');
258
+ const tokenFile = required(options['token-file'], 'token-file');
259
+ if (!path.isAbsolute(suppliedRoot) || !path.isAbsolute(tokenFile)) throw new CliUsageError('data-root 与 token-file 必须是绝对路径');
260
+ if (options.port !== undefined && (!/^\d+$/.test(options.port) || Number(options.port) > 65535)) throw new CliUsageError('port 必须是 0–65535 的整数');
261
+ // Pin the physical root for both the server and the outside-root credential check.
262
+ let dataRoot;
263
+ try { dataRoot = fs.realpathSync(suppliedRoot); }
264
+ catch (_) { throw new CliUsageError('data-root 必须是已初始化且可读取的 Flow 根'); }
265
+ const api = createLocalReadApi({ dataRoot, providerConfigPath: options['provider-config'], allowLocalWrites });
266
+ const unregister = registerRuntime(api);
267
+ try {
268
+ const started = await api.start({ port: options.port === undefined ? 0 : Number(options.port) });
269
+ writeLocalApiTokenFile(tokenFile, started.accessToken, dataRoot);
270
+ console.log(JSON.stringify({ contractVersion: 0, readOnly: !allowLocalWrites, origin: started.origin, tokenFile,
271
+ message: allowLocalWrites ? '本地创建 API 已启动;只上传/创建,不执行生成。会话 Token 在指定私有文件中,退出后失效。'
272
+ : '只读本地 API 已启动;会话 Token 在指定私有文件中,退出后失效。' }));
273
+ await api.closed;
274
+ return 0;
275
+ } finally { try { await api.dispose(); } finally { unregister(); } }
276
+ }
277
+
278
+ if (args[0] === 'flow') {
279
+ const verb = args[1];
280
+ if (!['init', 'create', 'list', 'inspect', 'continue', 'select', 'export'].includes(verb)) throw new CliUsageError('不支持的 flow 子命令');
281
+ const needsId = ['inspect', 'continue', 'select', 'export'].includes(verb);
282
+ if (needsId && (!args[2] || args[2].startsWith('--'))) throw new CliUsageError('缺少 Run ID');
283
+ const allowed = ['data-root', ...(['create', 'continue'].includes(verb) ? ['provider-config'] : []),
284
+ ...(['create', 'select'].includes(verb) ? ['input'] : []), ...(verb === 'export' ? ['output'] : [])];
285
+ const options = flags(args.slice(needsId ? 3 : 2), allowed);
286
+ const input = ['create', 'select'].includes(verb) ? jsonInput(options.input) : {};
287
+ if (verb === 'select' && Object.keys(input).some(key => key !== 'artifact_id')) throw new CliUsageError('flow select 只接受 artifact_id');
288
+ if (needsId) input.run_id = args[2];
289
+ if (verb === 'export') input.output_path = required(options.output, 'output');
290
+ const resolveImageCandidates = options['provider-config'] ? async ({ modelRef, signal }) => {
291
+ const catalog = await generationCatalog(options, 'image-gen', modelRef, signal);
292
+ return imageCandidatesFromCatalog(catalog, modelRef).map(route => ({ adapter: trackedProviderAdapter(route.provider, options),
293
+ model: route.modelRef, selectionReason: route.selectionReason, providerBinding: providerTaskBinding(route.provider) }));
294
+ } : undefined;
295
+ const resolveTaskAdapter = options['provider-config'] ? task => {
296
+ const catalog = loadProviderCatalog(options['provider-config']);
297
+ return trackedProviderAdapter(providerForTaskFromCatalog(catalog, task), options, task);
298
+ } : undefined;
299
+ let result;
300
+ try {
301
+ result = await withApplication(options['data-root'], application =>
302
+ application.execute('flow.' + verb, input, { signal: cliAbortController.signal }), resolveImageCandidates, resolveTaskAdapter);
303
+ } catch (error) {
304
+ if (error instanceof GenerationInputError) throw new CliUsageError(error.message);
305
+ throw error;
306
+ }
307
+ console.log(JSON.stringify(result, null, 2));
308
+ return 0;
309
+ }
310
+
202
311
  if (args[0] === 'doctor') {
203
312
  const rest = args.slice(1);
204
313
  if (rest.filter((arg) => arg === '--json').length > 1) throw new CliUsageError('参数重复:--json');
@@ -358,8 +467,9 @@ async function main(args) {
358
467
  const options = flags(args.slice(2), ['data-root', 'input']);
359
468
  const input = jsonInput(options.input);
360
469
  const command = args[1] === 'diff' ? 'media.diff' : 'media.frames';
361
- const result = await withRuntime('writer', options['data-root'], (commands) =>
362
- commands.execute(command, input));
470
+ const result = args[1] === 'diff'
471
+ ? await withApplication(options['data-root'], application => application.execute(command, input, { signal: cliAbortController.signal }))
472
+ : await withRuntime('writer', options['data-root'], commands => commands.execute(command, input));
363
473
  console.log(JSON.stringify(result, null, 2));
364
474
  return 0;
365
475
  }
@@ -375,21 +485,24 @@ async function main(args) {
375
485
 
376
486
  if (args[0] === 'run' && args[1] === 'image') {
377
487
  const options = flags(args.slice(2), ['data-root', 'provider-config', 'input']);
378
- const { providerInput, modelRef, sourceArtifactId } = generationInput('image', jsonInput(options.input));
379
- const catalog = await generationCatalog(options, 'image-gen', modelRef);
380
- const routes = imageCandidatesFromCatalog(catalog, modelRef);
381
- await withRawRuntime('writer', options['data-root'], async (runtime) => {
382
- const candidates = routes.map((route) => ({
488
+ const input = jsonInput(options.input);
489
+ const resolveImageCandidates = async ({ modelRef, signal }) => {
490
+ const catalog = await generationCatalog(options, 'image-gen', modelRef, signal);
491
+ return imageCandidatesFromCatalog(catalog, modelRef).map(route => ({
383
492
  adapter: trackedProviderAdapter(route.provider, options), model: route.modelRef,
384
493
  selectionReason: route.selectionReason,
385
494
  providerBinding: providerTaskBinding(route.provider)
386
495
  }));
387
- const result = await createProviderTaskRunner(runtime).submit({
388
- capability: 'image', candidates,
389
- providerInput, sourceArtifactId
390
- });
391
- console.log(JSON.stringify(result, null, 2));
392
- });
496
+ };
497
+ let result;
498
+ try {
499
+ result = await withApplication(options['data-root'], application =>
500
+ application.generateImage(input, { signal: cliAbortController.signal }), resolveImageCandidates);
501
+ } catch (error) {
502
+ if (error instanceof GenerationInputError) throw new CliUsageError(error.message);
503
+ throw error;
504
+ }
505
+ console.log(JSON.stringify(result, null, 2));
393
506
  return 0;
394
507
  }
395
508
 
@@ -567,8 +680,8 @@ async function main(args) {
567
680
 
568
681
  if (args[0] === 'task' && args[1] === 'inspect' && args[2]) {
569
682
  const options = flags(args.slice(3), ['data-root']);
570
- const result = await withRuntime('reader', options['data-root'], (commands) =>
571
- commands.execute('task.inspect', { task_id: args[2] }));
683
+ const result = await withApplication(options['data-root'], application =>
684
+ application.execute('task.inspect', { task_id: args[2] }, { signal: cliAbortController.signal }));
572
685
  console.log(JSON.stringify(result, null, 2));
573
686
  return 0;
574
687
  }
@@ -584,15 +697,16 @@ async function main(args) {
584
697
 
585
698
  if (args[0] === 'artifact' && args[1] === 'import') {
586
699
  const options = flags(args.slice(2), ['data-root', 'input']);
587
- const result = await withRuntime('writer', options['data-root'], commands => commands.execute('artifact.import', jsonInput(options.input)));
700
+ const result = await withApplication(options['data-root'], application =>
701
+ application.execute('artifact.import', jsonInput(options.input), { signal: cliAbortController.signal }));
588
702
  console.log(JSON.stringify(result, null, 2));
589
703
  return 0;
590
704
  }
591
705
 
592
706
  if (args[0] === 'artifact' && args[1] === 'inspect' && args[2]) {
593
707
  const options = flags(args.slice(3), ['data-root']);
594
- const result = await withRuntime('reader', options['data-root'], (commands) =>
595
- commands.execute('artifact.inspect', { artifact_id: args[2] }));
708
+ const result = await withApplication(options['data-root'], application =>
709
+ application.execute('artifact.inspect', { artifact_id: args[2] }, { signal: cliAbortController.signal }));
596
710
  console.log(JSON.stringify(result, null, 2));
597
711
  return 0;
598
712
  }
@@ -600,8 +714,8 @@ async function main(args) {
600
714
  if (args[0] === 'artifact' && args[1] === 'list') {
601
715
  const options = flags(args.slice(2), ['data-root', 'offset', 'limit', 'kind', 'media-type', 'task-id']);
602
716
  const input = Object.fromEntries(Object.entries(options).filter(([key]) => key !== 'data-root').map(([key, value]) => [key.replaceAll('-', '_'), ['offset', 'limit'].includes(key) ? Number(value) : value]));
603
- const result = await withRuntime('reader', options['data-root'], (commands) =>
604
- commands.execute('artifact.list', input));
717
+ const result = await withApplication(options['data-root'], application =>
718
+ application.execute('artifact.list', input, { signal: cliAbortController.signal }));
605
719
  console.log(JSON.stringify(result, null, 2));
606
720
  return 0;
607
721
  }
@@ -626,11 +740,11 @@ async function main(args) {
626
740
 
627
741
  if (args[0] === 'artifact' && args[1] === 'export' && args[2]) {
628
742
  const options = flags(args.slice(3), ['data-root', 'output']);
629
- const result = await withRuntime('writer', options['data-root'], (commands) =>
630
- commands.execute('artifact.export', {
743
+ const result = await withApplication(options['data-root'], application =>
744
+ application.execute('artifact.export', {
631
745
  artifact_id: args[2],
632
746
  output_path: required(options.output, 'output')
633
- }));
747
+ }, { signal: cliAbortController.signal }));
634
748
  console.log(JSON.stringify(result, null, 2));
635
749
  return 0;
636
750
  }