@huanlin/dsh-plugin-aigc-canvas 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -111
- package/lib/canvas-registry.d.ts +142 -0
- package/lib/client.js +49 -1674
- package/lib/client.js.map +1 -1
- package/lib/config.d.ts +77 -0
- package/lib/context-types.d.ts +120 -0
- package/lib/index.d.ts +12 -0
- package/lib/index.js +832 -5917
- package/lib/invariant.d.ts +15 -0
- package/lib/invariant.js +2 -2
- package/lib/media-edit.d.ts +38 -0
- package/lib/provider-http.d.ts +81 -0
- package/lib/provider-store.d.ts +50 -0
- package/lib/tools.d.ts +47 -0
- package/lib/trust-fence.d.ts +25 -0
- package/lib/wire.d.ts +41 -0
- package/package.json +24 -29
- package/LICENSE +0 -664
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
本项目已被dshfind.com收录
|
|
2
|
+
|
|
1
3
|
# dsh-aigc-canvas
|
|
2
4
|
|
|
3
|
-
> DSH
|
|
4
|
-
> `aigc_http_request` 调用任意 HTTP AIGC API(endpoint + apiKey 自动附加),生成的文件用
|
|
5
|
-
> `aigc_canvas_place` 摆到无限画布上,可用 `aigc_media_edit` (ffmpeg) 后处理。
|
|
5
|
+
> DSH 插件:一个节点-连线式的 AIGC 画布。向模型暴露文生图 / 文生视频 / 首尾帧生视频 / 多参考生视频 / 音频生成五类工具,生成的图片 / 视频 / 音频以及提示词都作为画布元素(uuid 寻址)存在,生成完成后自动按多对一(promise + 所有参考元素 → 输出)在画布上连线。
|
|
6
6
|
|
|
7
7
|
## 安装
|
|
8
8
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
dsh plugin --profile <profile> add link:D:\Projects\deepseek-harness\dsh-aigc-canvas
|
|
12
12
|
|
|
13
13
|
# 从 git 安装(发布到 dsh-external 组织后):
|
|
14
|
-
dsh plugin --profile <profile> add github:
|
|
14
|
+
dsh plugin --profile <profile> add github:dsh-external/dsh-aigc-canvas
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
预构建 `lib/` 入库策略(含 `@deepseek-ai/*` private peer deps,必须预构建),`github:` 安装开箱即用,无需 `allowBuilds`。
|
|
@@ -22,128 +22,60 @@ dsh plugin --profile <profile> add github:huanlinoto/dsh-plugin-aigc-canvas
|
|
|
22
22
|
|
|
23
23
|
| 字段 | 类型 | 默认值 | 说明 |
|
|
24
24
|
|------|------|--------|------|
|
|
25
|
-
| `
|
|
26
|
-
| `
|
|
27
|
-
| `
|
|
28
|
-
|
|
29
|
-
### `AigcProvider` 字段
|
|
30
|
-
|
|
31
|
-
| 字段 | 类型 | 默认值 | 说明 |
|
|
32
|
-
|------|------|--------|------|
|
|
33
|
-
| `id` | string | 必填 | provider 标识符(小写字母+数字+连字符,字母开头)。作为 `provider_id` 传给工具 |
|
|
34
|
-
| `name` | string | `''` | 显示名(如 "Volcano Engine") |
|
|
35
|
-
| `endpoint` | string | `stub://aigc-backend` | API endpoint URL。`stub://aigc-backend` = 内置 stub 后端 |
|
|
36
|
-
| `apiKey` | string | `''` | API key(仅内存;通过 GUI 或 cordis.yml 设置) |
|
|
37
|
-
| `instructions` | string | `''` | Agent 通过 `aigc_get_provider_info` 读取的调用说明(预览)+ `aigc_provider_get_instructions` 读全量 |
|
|
38
|
-
| `auth.scheme` | `'bearer'` \| `'header'` \| `'query'` | `'bearer'` | apiKey 附加方式 |
|
|
39
|
-
| `auth.name` | string | `''` | `header` 方式的 header 名(默认 `x-api-key`)或 `query` 方式的参数名(默认 `api_key`) |
|
|
40
|
-
| `builtin` | boolean | `false` | 是否是 seed 层内置 provider(仅 cordis.yml 标记) |
|
|
25
|
+
| `baseURL` | string | `stub://aigc-backend` | AIGC 后端 API 地址。`stub://aigc-backend` = 内置 stub 后端(无网络,合成 PNG/MP4/MP3 字节)。其他值会触发 `backend-error` 等待未来实现 |
|
|
26
|
+
| `apiKeyEnv` | credential-ref | `AIGC_API_KEY` | API key 的环境变量名 / 凭据引用。stub 后端不读 |
|
|
27
|
+
| `requestTimeoutMs` | number | `60000` | 单次后端请求超时(ms) |
|
|
28
|
+
| `mediaSizeLimit` | number | `104857600` (100 MiB) | 单个媒体文件大小上限(媒体路由校验) |
|
|
41
29
|
|
|
42
30
|
## 工具
|
|
43
31
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
| 工具 | 用途 |
|
|
47
|
-
|------|------|
|
|
48
|
-
| `aigc_get_provider_info` | 列出所有 provider(id/name/endpoint/instructions 预览/capabilities/capabilityMap/stub 标志)。**最先调用** |
|
|
49
|
-
| `aigc_http_request` | 向 provider API 发 HTTP 请求(endpoint + apiKey 自动附加)。二进制响应落盘返回 `file_path`,JSON/文本内联返回。有 EndpointSpec 时按 spec 处理响应 |
|
|
50
|
-
| `aigc_provider_set_instructions` | 探测 API 后记录 provider 的调用说明(每 provider 限 1000 字,旧式自由文本) |
|
|
51
|
-
| `aigc_provider_get_instructions` | 拉取一个 provider 的完整 instructions(`aigc_get_provider_info` 只返回前 200 字预览) |
|
|
52
|
-
| `aigc_provider_set_endpoints` | 探测 API 后记录结构化 EndpointSpec[] catalog(自动派生 instructions,支持 spec 驱动响应处理) |
|
|
53
|
-
| `aigc_get_endpoint_details` | 拉取一个 provider+capability 的完整 EndpointSpec[](path/method/params/response shape) |
|
|
54
|
-
| `aigc_probe_endpoint` | 向一个 endpoint 发最小测试请求,自动嗅探响应格式(ResponseKind + path) |
|
|
55
|
-
| `aigc_reroll` | 基于已有元素的 `meta.originalRequest` 重新生成(支持 seed/prompt_delta/prompt_replace/size patch,count>1 时生成变体簇) |
|
|
56
|
-
| `aigc_canvas_place` | 把文件摆到画布上(可选 x/y,可自动布局;可附 references + relation 自动连边) |
|
|
57
|
-
| `aigc_canvas_link` / `aigc_canvas_unlink` | 创建/删除两个元素之间的边(filePath 寻址,relation 必填:input/first_frame/style/variation_of/...) |
|
|
58
|
-
| `aigc_canvas_list_elements` | 只读:返回当前会话画布的完整快照(elements + edges with relation) |
|
|
59
|
-
| `aigc_media_edit` | ffmpeg 编辑(concat/clip/extract_audio/extract_frame/speed/resize/reverse/add_audio/images_to_video) |
|
|
60
|
-
|
|
61
|
-
### `aigc_get_provider_info`
|
|
62
|
-
|
|
63
|
-
列出所有已配置的 provider。返回每个 provider 的 `id`、`name`、`endpoint`、`instructions`(前 200 字预览 + `instructions_total_chars`)、`isStub`、`isDefault`。
|
|
64
|
-
|
|
65
|
-
参数:无。
|
|
66
|
-
|
|
67
|
-
### `aigc_http_request`
|
|
68
|
-
|
|
69
|
-
向 provider API 发送一个 HTTP 请求。provider 的 `endpoint` 和 `apiKey` 自动附加(Agent 永远看不到 apiKey)。`path` 相对 provider endpoint,如 `/v1/images/generations`;同源的绝对 URL 也接受(用于下载 provider 返回的下载链接)。
|
|
32
|
+
模型可见的六个工具,均在调用代理的会话作用域内执行(模型不需要传 `sessionId`):
|
|
70
33
|
|
|
71
|
-
|
|
34
|
+
### `aigc_text_to_image`
|
|
72
35
|
|
|
73
|
-
|
|
36
|
+
文生图。把 prompt 也作为 prompt 元素存入画布,生成图片作为 image 元素,自动连线 prompt → image。
|
|
74
37
|
|
|
75
|
-
参数:`
|
|
38
|
+
参数:`prompt`(必填)、`negative_prompt`、`width`(默认 1024)、`height`(默认 1024)、`seed`。
|
|
76
39
|
|
|
77
|
-
### `
|
|
40
|
+
### `aigc_text_to_video`
|
|
78
41
|
|
|
79
|
-
|
|
42
|
+
文生视频。同上,prompt 元素 → video 元素。
|
|
80
43
|
|
|
81
|
-
参数:`
|
|
44
|
+
参数:`prompt`(必填)、`negative_prompt`、`width`(默认 1280)、`height`(默认 720)、`duration_seconds`(默认 5)、`seed`。
|
|
82
45
|
|
|
83
|
-
### `
|
|
46
|
+
### `aigc_first_last_frame_to_video`
|
|
84
47
|
|
|
85
|
-
|
|
48
|
+
首尾帧 + 文生视频。`first_frame_uuid` / `last_frame_uuid` 必须是已存在的 image 元素 uuid(由 `aigc_text_to_image` 返回)。prompt 元素 + 两个 image 元素都 → video 元素(三条边)。
|
|
86
49
|
|
|
87
|
-
参数:`
|
|
50
|
+
参数:`prompt`(必填)、`first_frame_uuid`(必填)、`last_frame_uuid`(必填)、`width`、`height`、`duration_seconds`、`seed`。
|
|
88
51
|
|
|
89
|
-
### `
|
|
52
|
+
### `aigc_multi_reference_to_video`
|
|
90
53
|
|
|
91
|
-
|
|
54
|
+
多参考生视频。`reference_uuids` 是 image / video / audio 元素的 uuid 数组(至少一个)。prompt 元素 + 所有参考元素都 → video 元素。
|
|
92
55
|
|
|
93
|
-
|
|
94
|
-
- `references` 是已有元素的 filePath 数组,自动从每个参考向新元素连边
|
|
95
|
-
- `description` 是 ≤ 40 字的极简描述(名词/形容词/短语,如 "orange cat"),显示在卡片上
|
|
56
|
+
参数:`prompt`(必填)、`reference_uuids`(必填,非空数组)、`width`、`height`、`duration_seconds`、`seed`。
|
|
96
57
|
|
|
97
|
-
|
|
58
|
+
### `aigc_generate_audio`
|
|
98
59
|
|
|
99
|
-
|
|
60
|
+
文本生成音频。prompt 元素 → audio 元素。
|
|
100
61
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
参数:`source`(必填)、`target`(必填)。
|
|
62
|
+
参数:`prompt`(必填)、`duration_seconds`(默认 10)、`seed`。
|
|
104
63
|
|
|
105
64
|
### `aigc_canvas_list_elements`
|
|
106
65
|
|
|
107
|
-
|
|
66
|
+
只读:返回当前会话画布的完整快照(elements + edges)。用于长工具序列后恢复状态、查找要传给后续调用的 uuid。
|
|
108
67
|
|
|
109
68
|
参数:无。
|
|
110
69
|
|
|
111
|
-
### `aigc_media_edit`
|
|
112
|
-
|
|
113
|
-
通过 ffmpeg 编辑媒体文件。`operation` 选定操作,所有输入文件必须已存在于会话 canvas 目录内,输出落盘并返回 `file_path`。
|
|
114
|
-
|
|
115
|
-
| 操作 | inputs | output_ext | 关键参数 |
|
|
116
|
-
|------|--------|------------|---------|
|
|
117
|
-
| `concat` | ≥ 2 视频 | mp4 | — |
|
|
118
|
-
| `clip` | 1 视频 | mp4 | `start`/`end` 或 `start`/`duration`(秒) |
|
|
119
|
-
| `extract_audio` | 1 视频 | mp3 | — |
|
|
120
|
-
| `extract_frame` | 1 视频 | png | `timestamp`(秒) |
|
|
121
|
-
| `speed` | 1 视频 | mp4 | `speed`(2 = 2x,0.5 = 半速) |
|
|
122
|
-
| `resize` | 1 视频 | mp4 | `width` 和/或 `height`(像素) |
|
|
123
|
-
| `reverse` | 1 视频 | mp4 | — |
|
|
124
|
-
| `add_audio` | 1 视频 + 1 音频 | mp4 | — |
|
|
125
|
-
| `images_to_video` | ≥ 1 图片 | mp4 | `fps`(默认 2) |
|
|
126
|
-
|
|
127
|
-
**ffmpeg 查找顺序**:
|
|
128
|
-
1. `AIGC_FFMPEG_PATH` 环境变量(显式覆盖,适用于非标准安装路径)
|
|
129
|
-
2. `ffmpeg` on PATH(macOS/Linux 和大多数 Windows 的正常情况)
|
|
130
|
-
3. 平台特定常见安装位置:
|
|
131
|
-
- **Windows**:`C:\ffmpeg\bin\ffmpeg.exe`、`C:\Program Files\ffmpeg\bin\ffmpeg.exe`、`C:\Program Files (x86)\ffmpeg\bin\ffmpeg.exe`、`${CONDA_PREFIX}\Scripts\ffmpeg.exe`
|
|
132
|
-
- **macOS/Linux**:`/usr/bin/ffmpeg`、`/usr/local/bin/ffmpeg`、`/opt/homebrew/bin/ffmpeg`
|
|
133
|
-
|
|
134
|
-
找不到时抛 `backend-error`,错误信息指引安装方式。
|
|
135
|
-
|
|
136
70
|
## 画布视图
|
|
137
71
|
|
|
138
72
|
通过 better-sidebar 的服务消费(`ctx.betterSidebar.registerTab`)注册一个 `aigc-canvas:main` tab。tab 是单实例(每个会话一个),打开后:
|
|
139
73
|
|
|
140
74
|
- 通过 WebSocket `/aigc-canvas/ws/canvas?sessionId=...` 订阅 host 端的画布变更推送
|
|
141
75
|
- 首次加载会先 HTTP `POST /aigc-canvas/api/canvas.list` priming 一次快照
|
|
142
|
-
- 节点按 vertical flow 排列,每个节点的入边在节点上方以 chip 形式显示
|
|
76
|
+
- 节点按 vertical flow 排列,每个节点的入边在节点上方以 chip 形式显示(短 uuid)
|
|
143
77
|
- 不同 kind 用左侧色条区分:prompt 蓝 / image 绿 / video 橙 / audio 紫
|
|
144
|
-
-
|
|
145
|
-
- WS 断开后自动重连
|
|
146
|
-
- **请求日志面板**:header 上的 📊 按钮打开浮层,显示本会话所有 `aigc_http_request` + `aigc_media_edit` 调用(时间/provider/path/状态/耗时/大小),点条目展开详情(请求头 + 请求体 + 响应预览,apiKey 已脱敏),可"在画布上定位"产物元素
|
|
78
|
+
- WS 断开后 2 秒自动重连
|
|
147
79
|
|
|
148
80
|
> better-sidebar 未安装时,host 半的工具 + 元素表仍然正常工作,只是没有 UI 可视化(未来的 host-side 消费者可以通过 `ctx.aigcCanvas` 服务读取状态)。
|
|
149
81
|
|
|
@@ -161,25 +93,25 @@ dsh plugin --profile <profile> add github:huanlinoto/dsh-plugin-aigc-canvas
|
|
|
161
93
|
<cwd>/.dsh-aigc-canvas/<sessionId>/<uuid>.<ext>
|
|
162
94
|
```
|
|
163
95
|
|
|
164
|
-
|
|
96
|
+
扩展名按 kind 决定:`prompt` → `.txt`(实际上 prompt 元素不写文件,只在 JSON 中存 `promptText`)、`image` → `.png`、`video` → `.mp4`、`audio` → `.mp3`。
|
|
165
97
|
|
|
166
|
-
刷新浏览器 / 重启 DSH 后,会话画布从 `canvas.json`
|
|
98
|
+
刷新浏览器 / 重启 DSH 后,会话画布从 `canvas.json` 重新水合,媒体文件保留在原位。
|
|
167
99
|
|
|
168
100
|
## 开发
|
|
169
101
|
|
|
170
102
|
```sh
|
|
171
103
|
pnpm install # 安装开发依赖(schemastery、typescript、vitest、tsdown)
|
|
172
104
|
pnpm run typecheck # tsc --noEmit 类型检查
|
|
173
|
-
pnpm test # vitest run 单元测试(canvas-registry / tools / wire
|
|
174
|
-
pnpm run build # tsdown 构建 → lib/index.js + lib/invariant.js + lib/client.js
|
|
105
|
+
pnpm test # vitest run 单元测试(canvas-registry / tools / wire)
|
|
106
|
+
pnpm run build # tsdown 构建 → lib/index.js + lib/invariant.js + lib/client.js
|
|
175
107
|
pnpm watch # tsdown --watch(client bundle 热重建)
|
|
176
108
|
```
|
|
177
109
|
|
|
178
110
|
构建产物:
|
|
179
111
|
- `lib/index.js` — host 入口(cordis 插件,提供 `ctx.aigcCanvas` 服务 + 路由 + 工具)
|
|
180
112
|
- `lib/invariant.js` — 包级 invariant 伴生
|
|
181
|
-
- `lib/client.js` — 浏览器 bundle(`window.__ModuleLoader__.load` 闭包工厂,id = `@
|
|
182
|
-
- `lib/index.d.ts` 等 — TypeScript 声明(由 `tsc -p tsconfig.
|
|
113
|
+
- `lib/client.js` — 浏览器 bundle(`window.__ModuleLoader__.load` 闭包工厂,id = `@dsh-external/dsh-aigc-canvas`)
|
|
114
|
+
- `lib/index.d.ts` 等 — TypeScript 声明(由 `tsc -p tsconfig.json` 产出,不在 tsdown 流程内)
|
|
183
115
|
|
|
184
116
|
## 目录结构
|
|
185
117
|
|
|
@@ -193,23 +125,19 @@ dsh-aigc-canvas/
|
|
|
193
125
|
│ ├── wire.ts # HTTP helpers + AigcError
|
|
194
126
|
│ ├── trust-fence.ts # Host 头信任围栏(从 better-sidebar 拷贝)
|
|
195
127
|
│ ├── canvas-registry.ts # 元素表 + 边 + 持久化(host-owned state)
|
|
196
|
-
│ ├──
|
|
197
|
-
│ ├──
|
|
198
|
-
│ ├── media-edit.ts # ffmpeg 编辑引擎
|
|
199
|
-
│ ├── tools.ts # 9 个 defineTool
|
|
128
|
+
│ ├── aigc-backend.ts # 抽象 AIGC 后端客户端(stub 实现)
|
|
129
|
+
│ ├── tools.ts # 6 个 defineTool
|
|
200
130
|
│ ├── types.d.ts # @deepseek-ai/dsh-tools + cordis 环境类型声明
|
|
201
131
|
│ └── client/
|
|
202
132
|
│ ├── index.tsx # client 入口:注册 better-sidebar tab
|
|
203
133
|
│ ├── CanvasView.tsx # 画布主视图
|
|
204
134
|
│ ├── CanvasNode.tsx # 节点组件
|
|
205
|
-
│ ├── SettingsPage.tsx # provider 设置页
|
|
206
135
|
│ ├── store.ts # CanvasStore(WS 订阅 + useSyncExternalStore)
|
|
207
136
|
│ ├── api.ts # HTTP/WS 客户端
|
|
208
137
|
│ ├── locales.ts # i18n(zh/en)
|
|
209
138
|
│ └── canvas.module.css # 画布样式
|
|
210
139
|
├── tests/
|
|
211
140
|
│ ├── canvas-registry.spec.ts
|
|
212
|
-
│ ├── provider-store.spec.ts
|
|
213
141
|
│ ├── tools.spec.ts
|
|
214
142
|
│ └── wire.spec.ts
|
|
215
143
|
├── cordis.patch.yml # bundle 层:插入插件行
|
|
@@ -222,15 +150,30 @@ dsh-aigc-canvas/
|
|
|
222
150
|
└── README.md
|
|
223
151
|
```
|
|
224
152
|
|
|
153
|
+
## 后端实现路线(stub → 真实)
|
|
154
|
+
|
|
155
|
+
当前 `aigc-backend.ts` 的 stub 在 `baseURL === 'stub://aigc-backend'`(或为空)时返回合成字节;其他 `baseURL` 立即抛 `backend-error`(HTTP 501)。
|
|
156
|
+
|
|
157
|
+
要接入真实后端:
|
|
158
|
+
|
|
159
|
+
1. 在 `aigc-backend.ts` 的每个 `generate*` 方法的 `!this.isStub` 分支里替换为真实 `fetch` 调用(参考 dsh-mineru 的 `MinerUClient`)。
|
|
160
|
+
2. 后端 API 形状建议保持 `{ prompt, ..., signal } → { mediaBytes, meta }` 的统一契约,这样 `tools.ts` 不需要改动。
|
|
161
|
+
3. 工具的参数 schema 已包含 `width / height / duration_seconds / seed` 等通用字段,后端可以选择性消费。
|
|
162
|
+
4. 媒体格式: stub 用最小合法 PNG / ftyp-only MP4;真实后端返回的字节由 host 直接落盘 + 浏览器渲染,不需要转码。
|
|
163
|
+
|
|
225
164
|
## 安全边界
|
|
226
165
|
|
|
227
166
|
- 路由受 Host 头信任围栏保护(与 `/api` 一致;`0.0.0.0` 部署时由 `dsh web` 启动器动态派生的 LAN IP 列表生效)
|
|
228
167
|
- `/aigc-canvas/file` 仅限会话 canvas 目录内的媒体文件
|
|
229
|
-
- `/aigc-canvas/api/*` JSON API 受同一 Host 头信任围栏保护
|
|
230
168
|
- 工具执行绑定到调用代理的会话 id(`exec.agent.session.id`),模型不能跨会话读取 / 引用其他会话的元素
|
|
231
|
-
- `
|
|
232
|
-
|
|
233
|
-
|
|
169
|
+
- `apiKeyEnv` 通过 `ctx.get('credentials')` 懒解析,先于 `process.env`
|
|
170
|
+
|
|
171
|
+
## 已知限制(v0.1 阶段)
|
|
172
|
+
|
|
173
|
+
- 后端 stub,无真实 AIGC 调用
|
|
174
|
+
- 画布视图是垂直 flow,不是真正的图布局(force-directed / DAG 布局待后续)
|
|
175
|
+
- 边以 chip 形式标注在节点上方,没有 SVG 连线(避免引入 react-flow 等运行时依赖)
|
|
176
|
+
- 元素不支持删除 / 编辑(只能通过删除 `canvas.json` + 重启会话来重置画布)
|
|
234
177
|
|
|
235
178
|
## 规范符合性
|
|
236
179
|
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/** Discriminated union of element kinds the canvas stores. */
|
|
2
|
+
export type AigcElementKind = 'prompt' | 'image' | 'video' | 'audio';
|
|
3
|
+
/** File extension for each media kind (no leading dot). */
|
|
4
|
+
export declare function extensionFor(kind: AigcElementKind): string;
|
|
5
|
+
/** MIME type for each media kind (for the file route). */
|
|
6
|
+
export declare function mimeTypeFor(kind: AigcElementKind): string;
|
|
7
|
+
/** One node on the canvas. */
|
|
8
|
+
export interface AigcElement {
|
|
9
|
+
/** Stable opaque handle (internal; used for edges). */
|
|
10
|
+
uuid: string;
|
|
11
|
+
/** Owning conversation id. */
|
|
12
|
+
sessionId: string;
|
|
13
|
+
/** Discriminator. */
|
|
14
|
+
kind: AigcElementKind;
|
|
15
|
+
/** Display title (short human-readable label). */
|
|
16
|
+
title: string;
|
|
17
|
+
/** Canvas position, world coordinates (infinite free canvas). */
|
|
18
|
+
x: number;
|
|
19
|
+
/** Canvas position, world coordinates (infinite free canvas). */
|
|
20
|
+
y: number;
|
|
21
|
+
/** Creation time (ms since epoch). */
|
|
22
|
+
createdAt: number;
|
|
23
|
+
/** Tool that produced this element. */
|
|
24
|
+
producedBy: string;
|
|
25
|
+
/**
|
|
26
|
+
* Absolute path to the element file on disk. For prompt elements: the
|
|
27
|
+
* `.txt` file containing the prompt text. For media elements: the media
|
|
28
|
+
* file. This is the **primary external identifier** — tools return and
|
|
29
|
+
* accept this path.
|
|
30
|
+
*/
|
|
31
|
+
filePath: string;
|
|
32
|
+
/** For prompt elements: the prompt text (mirrored in the .txt file). */
|
|
33
|
+
promptText?: string;
|
|
34
|
+
/** For media elements: byte size of the media file. */
|
|
35
|
+
mediaSize?: number;
|
|
36
|
+
/** Freeform metadata bag (dimensions, duration, model, seed, ...). */
|
|
37
|
+
meta?: Record<string, unknown>;
|
|
38
|
+
/**
|
|
39
|
+
* Ultra-short model-supplied description of the element (a noun, an
|
|
40
|
+
* adjective, or a short phrase — e.g. "orange cat", "sunset beach",
|
|
41
|
+
* "fast cut"). Bounded to ~40 chars; shown on the node card under the
|
|
42
|
+
* title and injected into context when the element is referenced.
|
|
43
|
+
*/
|
|
44
|
+
description?: string;
|
|
45
|
+
}
|
|
46
|
+
/** One edge: source element → target element (multi-to-one fan-in). */
|
|
47
|
+
export interface AigcEdge {
|
|
48
|
+
/** Source element uuid (an input — prompt or reference). */
|
|
49
|
+
source: string;
|
|
50
|
+
/** Target element uuid (the produced output). */
|
|
51
|
+
target: string;
|
|
52
|
+
}
|
|
53
|
+
/** The serializable canvas state for one session. */
|
|
54
|
+
export interface AigcCanvasState {
|
|
55
|
+
sessionId: string;
|
|
56
|
+
elements: AigcElement[];
|
|
57
|
+
edges: AigcEdge[];
|
|
58
|
+
}
|
|
59
|
+
/** Listener callback receives the session id that changed. */
|
|
60
|
+
export type AigcCanvasListener = (sessionId: string) => void;
|
|
61
|
+
/** The registry service published as `ctx.aigcCanvas`. */
|
|
62
|
+
export interface AigcCanvasService {
|
|
63
|
+
/** Add a prompt element (writes a .txt file). Returns the new element. */
|
|
64
|
+
addPrompt(sessionId: string, params: {
|
|
65
|
+
title: string;
|
|
66
|
+
promptText: string;
|
|
67
|
+
producedBy: string;
|
|
68
|
+
x?: number;
|
|
69
|
+
y?: number;
|
|
70
|
+
meta?: Record<string, unknown>;
|
|
71
|
+
description?: string;
|
|
72
|
+
}, cwd: string): Promise<AigcElement>;
|
|
73
|
+
/** Add a media element (image/video/audio) with the given bytes on disk. */
|
|
74
|
+
addMedia(sessionId: string, params: {
|
|
75
|
+
kind: 'image' | 'video' | 'audio';
|
|
76
|
+
title: string;
|
|
77
|
+
producedBy: string;
|
|
78
|
+
mediaBytes: Buffer;
|
|
79
|
+
x?: number;
|
|
80
|
+
y?: number;
|
|
81
|
+
meta?: Record<string, unknown>;
|
|
82
|
+
description?: string;
|
|
83
|
+
}, cwd: string): Promise<AigcElement>;
|
|
84
|
+
/**
|
|
85
|
+
* Register an element for a file that already exists on disk inside the
|
|
86
|
+
* session canvas directory (written by the model's http tool). The file
|
|
87
|
+
* is not copied; the element references it in place.
|
|
88
|
+
*
|
|
89
|
+
* x/y are optional: when omitted the host picks a position automatically
|
|
90
|
+
* (a left-aligned vertical column below the lowest existing element) so
|
|
91
|
+
* newly placed elements land somewhere reasonable instead of all piling
|
|
92
|
+
* at (0, 0).
|
|
93
|
+
*/
|
|
94
|
+
placeFile(sessionId: string, params: {
|
|
95
|
+
kind: 'image' | 'video' | 'audio' | 'prompt';
|
|
96
|
+
filePath: string;
|
|
97
|
+
title: string;
|
|
98
|
+
producedBy: string;
|
|
99
|
+
x?: number;
|
|
100
|
+
y?: number;
|
|
101
|
+
promptText?: string;
|
|
102
|
+
meta?: Record<string, unknown>;
|
|
103
|
+
description?: string;
|
|
104
|
+
/** Uuids of reference elements — when x/y are omitted, the new element is placed to the right of them. */
|
|
105
|
+
referenceUuids?: readonly string[];
|
|
106
|
+
}, cwd: string): Promise<AigcElement>;
|
|
107
|
+
/** Move an element to a new canvas position (persisted + pushed). */
|
|
108
|
+
updatePosition(sessionId: string, uuid: string, x: number, y: number): Promise<AigcElement>;
|
|
109
|
+
/**
|
|
110
|
+
* Delete one element and any edges referencing it. The media file on
|
|
111
|
+
* disk is NOT removed (the model may still reference its filePath);
|
|
112
|
+
* only the canvas registration is dropped.
|
|
113
|
+
*/
|
|
114
|
+
deleteElement(sessionId: string, uuid: string): Promise<void>;
|
|
115
|
+
/** Wire edges from each input uuid to the target uuid (multi-to-one). */
|
|
116
|
+
wireEdges(sessionId: string, inputUuids: readonly string[], targetUuid: string): Promise<void>;
|
|
117
|
+
/** Remove one edge (source → target). Idempotent. */
|
|
118
|
+
unlink(sessionId: string, sourceUuid: string, targetUuid: string): Promise<void>;
|
|
119
|
+
/** Load the persisted state for one session (idempotent; used before sync reads). */
|
|
120
|
+
ensureHydrated(sessionId: string): Promise<void>;
|
|
121
|
+
/** Look up one element by uuid (throws if not found or wrong session). */
|
|
122
|
+
getElement(sessionId: string, uuid: string): AigcElement;
|
|
123
|
+
/** Look up one element by its filePath (throws if not found). */
|
|
124
|
+
getElementByPath(sessionId: string, filePath: string): AigcElement;
|
|
125
|
+
/** Snapshot of one session's full canvas state (elements + edges). */
|
|
126
|
+
snapshot(sessionId: string): AigcCanvasState;
|
|
127
|
+
/** Subscribe to canvas mutations for any session. Returns disposer. */
|
|
128
|
+
subscribe(listener: AigcCanvasListener): () => void;
|
|
129
|
+
/** Subscribe to canvas mutations for one specific session. */
|
|
130
|
+
subscribeSession(sessionId: string, listener: AigcCanvasListener): () => void;
|
|
131
|
+
}
|
|
132
|
+
/** Resolve the per-session canvas directory under the session cwd. */
|
|
133
|
+
export declare function canvasDirFor(cwd: string, sessionId: string): string;
|
|
134
|
+
/** Resolve the per-session canvas JSON path. */
|
|
135
|
+
export declare function canvasJsonPath(cwd: string, sessionId: string): string;
|
|
136
|
+
/** Resolve the per-session file path for one element (by uuid + kind). */
|
|
137
|
+
export declare function elementFilePath(cwd: string, sessionId: string, uuid: string, kind: AigcElementKind): string;
|
|
138
|
+
/**
|
|
139
|
+
* Build the service. The `resolveCwd` callback threads the live session cwd;
|
|
140
|
+
* `mediaSizeLimit` bounds how large a placed file may be.
|
|
141
|
+
*/
|
|
142
|
+
export declare function createAigcCanvasService(resolveCwd: (sessionId: string) => string, mediaSizeLimit?: () => number): AigcCanvasService;
|