cortico-world-image 0.1.4 → 0.3.1

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/ENV_PROMPT.md CHANGED
@@ -18,7 +18,7 @@
18
18
  3. **描述要具体。** 主体、画风、构图、光线、色调、背景,一句到位;别只给两三个词,也别把整段聊天记录塞进去。
19
19
  4. **说人话,不念路径。** 回执里的绝对路径是给工具用的,不是给用户看的。画完就用一句自然的话告诉用户画了什么,像 `pet_say` 那样说话。不要把路径、编号、`blobs`、JSON 念出来,也不要贴 Markdown 图片语法。
20
20
  5. **发到别处要另走一步。** 图默认只在你自己的界面里。
21
- - 想让主人在控制台的「对话」页看到它,画完之后再调 `pet_show_image`:`path` 填 `image_draw` 回执里的绝对路径,`caption` 可选(一句配文)。它只贴图、不出声,也不会引发语音。
22
- - 要发到 QQ 会话,画完之后再调 `qq_send_image`,`image` 参数填 `image_draw` 回执里那个绝对路径。
21
+ - 想让主人在控制台的「对话」页看到它,画完之后调桌宠的 `pet_say`,`images` 填回执里 `[blob …]` 行的句柄(就是 `[blob ` 后面那一串),`script` 给空串就只贴图不出声;想配一句话就写进 `script`(那句话会在气泡里说出来)。图只进「对话」页,不进气泡。
22
+ - 要发到 QQ 会话:把回执里 `[blob …]` 行的句柄交给 QQ 世界的发图工具。官方 QQ 扩展用 `qq_draft`(`image` 填句柄,`to` 填 `group:<群号>` 或 `private:<QQ号>`,再用 `qq_confirm` 确认发送);旧版 QQ 扩展(`qqbot`)用 `qq_send_image`,`image` 填回执里的绝对路径。
23
23
  6. **失败就停。** 如果回执说没配密钥、接口报错、超时,不要换个说法反复重试。把情况告诉用户,让他去控制台「图片工坊 → 状态」里填密钥或改接口地址。
24
24
  7. **别凭空说画好了。** 只有工具回执真的带着图回来,才算画好了。
package/README.md CHANGED
@@ -1,16 +1,18 @@
1
1
  # 图片工坊 · cortico-world-image
2
2
 
3
- 给 Coopanion 桌宠加两件事:**调用图片生成 API 画图,并把图交回对话**;以及**把本地任意一张图贴进控制台的「对话」页**——后者靠一套自愈补丁给桌宠加一个 `pet_show_image` 工具,程序自动更新把这些文件覆写回原样之后,扩展下次启动会自己补回来。
3
+ 给 Coopanion 桌宠加两件事:**调用图片生成 API 画图,并把图交回对话**;以及**把图贴进控制台的「对话」页**——后者用桌宠包自带的 `pet_say.images`(本扩展的工具回执里带着附件句柄),不再给桌宠打补丁。唯一还会被改写的是**第三方 QQ 扩展**里的两处路径解析(可选补丁,程序自动更新覆写回去之后,扩展下次启动会自己补回来)。
4
4
 
5
- 这是一个 `cortico` 的 **World 扩展**(`kind: world`,`api: 5`),只补一个 `image` 世界,
5
+ 这是一个 `cortico` 的 **World 扩展**(`kind: world`,`api: 6`),只补一个 `image` 世界,
6
6
  不依赖任何第三方 npm 包(纯 ESM JS + Node 内置模块)。
7
7
 
8
8
  > **性质说明**:这是给 Coopanion 写的**非官方**扩展,走的是框架公开的扩展接口
9
- > (`kind: world` + `consoleClient` 面板)。唯一伸手动别人的地方是「对话页发图」那一节的
10
- > 自愈补丁——它按锚点改写安装目录下的几个文件(`resources/app` 里的浏览器 bundle、桌宠包
11
- > 的 `src/*.ts`,以及可选的第三方 QQ 扩展),改之前一律留 `.coopanion-orig` 备份,
12
- > 程序自动更新覆写后会自己补回来,细节与还原方式都在那一节里。不愿意让脚本动这些文件,
13
- > 就只用 `image_*` 四个工具,别装那份补丁(补丁是懒触发的,只在扩展启动时检查)。
9
+ > (`kind: world` + `webUiClient` 面板 + 必需的 `describe.json` 自描述)。唯一伸手动别人的地方是「QQ 发图路径」
10
+ > 那两处可选补丁——它按锚点改写**第三方 QQ 扩展**(`cortico-world-qq-better-alont1`)的 `src/world.ts`,改之前留一份
11
+ > `.coopanion-orig` 备份,程序自动更新覆写后会自己补回来(细节与还原方式见那一节)。
12
+ > 补丁只给**正在用的那个 QQ 扩展**打:读 `<安装>\data\home\companion\config.json`,
13
+ > `worlds.qqbot.enabled` 不是 `true`(比如已经切到官方 `cortico-world-qq`)就整段跳过,一个字节都不碰。
14
+ > **桌宠包本身完全不碰**:0.3.0 起改用官方的 `pet_say.images`,早先版本打进去的旧补丁会被自动还原。
15
+ > 不愿意让脚本动那个第三方包,就把 QQ 扩展的补丁关掉(它认不出原文只会记一条失败,不会硬改)。
14
16
  >
15
17
  > MIT 许可,见 [LICENSE](LICENSE)。
16
18
 
@@ -36,72 +38,49 @@
36
38
  > 桌宠窗口的说话气泡也只会渲染纯文本——只有选项卡片的图片位认 `data:image/`。
37
39
  > 硬往气泡里塞图会改框架,不是扩展该做的事。所以本扩展走「工具回执 blob + 自己的控制台页」这条正规通道。
38
40
  >
39
- > 例外是**控制台的「对话」页**:那里现在是打补丁支持了桌宠侧的图片(见下一节),
41
+ > 例外是**控制台的「对话」页**:那里用桌宠包自带的 `pet_say.images` 就能显示图(见下一节),
40
42
  > 但桌宠**头顶气泡**仍然只画文字。
41
43
 
42
- ## 对话页发图:`pet_show_image` + 自愈补丁
44
+ ## 把图贴到「对话」页:官方 `pet_say` 的 `images`
43
45
 
44
- 桌宠多出一个工具:
46
+ 桌宠包从 **0.2.0** 起,`pet_say` 自带 `images` 参数,所以本扩展**不再给桌宠打任何补丁**:
45
47
 
46
- | 工具 | 标签 | 作用 |
47
- | --- | --- | --- |
48
- | `pet_show_image` | `act` | 把一个**本地图片路径**(绝对路径,或相对桌宠工作区,如 `blobs/xx.jpg`)贴到控制台「对话」页,可选一句配文 |
49
-
50
- - 认 PNG / JPEG / WebP / GIF(先嗅文件头,认不出才看后缀),单张上限 8 MB;
51
- - 标签是 `act` 不是 `speak`,**名字也不是 `pet_say`**,所以 CosyVoice TTS 那类只监听 `pet_say`
52
- 的扩展不会把它念出来;
53
- - 图片随事件存成附件(`log:` 句柄),对话页通过面板的 `blob` 方法取图;对话记录里翻回旧消息也还在。
54
-
55
- ### 为什么要打补丁
56
-
57
- 对话页的时间线是**桌宠世界自己**推的流:只有它能写 `source: 'desktop-pet'` 的事件。0.1.22 及更早的框架里:
48
+ ```js
49
+ pet_say({ script: '', images: ['<句柄>'] }) // 只贴图、不说话
50
+ pet_say({ script: '看,画好了。', images: ['<句柄>'] }) // 气泡里说一句,图贴到对话页
51
+ ```
58
52
 
59
- - `node_modules/cortico-world-desktop-pet/src/chat.ts` 的 `chatItem()` 在 `kind: 'say'` 分支只取
60
- `meta.beats`,把事件上的 `blobs` 丢掉;
61
- - 浏览器侧 bundle 里 say 那一段只画 `pet-bubble`/`mood`,没有图片槽位(只有用户侧消息有)。
53
+ - 参数收的是**附件句柄**:就是工具回执里 `[blob <handle> <mime> <name>]` 那一行的 `<handle>`。
54
+ 本扩展的 `image_draw` / `image_edit` / `image_show` 回执本来就带附件,框架会把它渲染成这样一行交给模型。
55
+ - `script` 空串 = 只有图、没台词;图只进控制台「对话」页,**不进气泡**。写一句话就同时说、同时贴。
56
+ - **任意本地文件要两步**:先 `image_show({ path })` 把文件读成附件、拿到句柄,再 `pet_say`。
57
+ 多一次工具调用,并且那张图会被渲染给模型看(约 1k+ token/张,一次性);对话里刚画好的图
58
+ 直接用 `image_draw` 回执里的句柄,不额外花。
59
+ - 0.3.0 起**没有 `pet_show_image` 这个工具了**。
62
60
 
63
- 所以「另写一个扩展往对话页推图」是走不通的(别的世界推的事件不会经过桌宠的
64
- `chat.broadcast`,`chatPage()` 也只读 `source === 'desktop-pet'`)。0.1.23 之前靠补丁把这两段补上;
65
- **0.1.23 框架自己已经内置,那 4 处补丁已经删掉**(见下一节)。现在补丁一共 8 处:
66
- 前 6 处是你这份安装里本来就有的文件(`world.ts` 5 处 + `definition.ts` 1 处,最后 3 处让
67
- `pet_show_image` 也认相对路径),后 2 处(`optional`)落在**第三方 QQ 扩展**上,没装那个扩展就记
68
- `absent`——不算失败、也不进状态页的总数:
61
+ ### 0.3.0 删掉的桌宠补丁(7 处)
69
62
 
70
- | 补丁 id | 文件 | 做什么 |
63
+ | 原来的补丁 id | 文件 | 原本做什么 |
71
64
  | --- | --- | --- |
72
- | `pet-fs-import` | 桌宠 `src/world.ts` | `node:fs` 导入加 `readFileSync`(`{ statSync }` 与 `{ existsSync, statSync }` 两种写法都认) |
65
+ | `pet-fs-import` | 桌宠 `src/world.ts` | `node:fs` 导入加 `readFileSync` |
73
66
  | `pet-path-import` | 桌宠 `src/world.ts` | `node:path` 导入加 `basename/extname/isAbsolute` |
74
67
  | `pet-show-image-tool` | 桌宠 `src/world.ts` | `CHAT_IMAGE_TOOL` 常量、`sniffImageMime()`、`showImage()`、挂进 `tools()` |
75
- | `pet-workspace-option` | 桌宠 `src/world.ts` | 世界选项加 `workspaceDir?: () => string`(装配层注入桌宠工作区) |
76
- | `pet-workspace-relpaths` | 桌宠 `src/world.ts` | `pet_show_image` 的相对路径按 `workspaceDir()` 拼,说明也改成「绝对路径或相对工作区」 |
68
+ | `pet-workspace-option` | 桌宠 `src/world.ts` | 世界选项加 `workspaceDir?: () => string` |
69
+ | `pet-workspace-relpaths` | 桌宠 `src/world.ts` | `pet_show_image` 的相对路径按 `workspaceDir()` 拼,说明同步 |
77
70
  | `pet-workspace-wire` | 桌宠 `src/definition.ts` | 装配层把 `join(ctx.botDir, 'workspace')` 传进世界 |
78
- | `qq-image-relpaths`(可选) | `<安装>\data\extensions\node_modules\cortico-world-qq-better-alont1\src\world.ts` | `qq_send_image` / `qq_view_image` 认「相对桌宠工作区」的路径(见下) |
79
- | `qq-image-hints`(可选) | 同上 | 那两个工具的参数说明也改成「相对桌宠工作区的路径,如 `blobs/xx.jpg`」 |
80
-
81
- 改法一律是**精确锚点 + 幂等标记**(`coopanion-chat-image`):
82
- 认不出锚点就记一条失败并**不动那个文件**,绝不硬猜。第一次改写前会把原文留一份
83
- `<文件>.coopanion-orig`。
84
-
85
- 补丁器不写死源码里的变量名:`tools()` 那处不写死
86
- `DESKTOP_PET_TOOL_DECLS.map(...)`,而是包住 `tools()` 里现有的 `return ...;`。
87
- 所以升级换了写法之后照样能打上。
88
-
89
- ### 框架 0.1.23 之后为什么少了 4 处补丁
90
-
91
- 0.1.23 起「把图发到对话页」是框架原生能力,不再是这个扩展要补的洞:
71
+ | `pet-show-image-describe` | 桌宠 `src/definition.ts` | 把 `pet_show_image` 补进 `describe.tools`(框架 0.2 会核对,缺了记「工具不在 describe.tools 里」) |
92
72
 
93
- - 桌宠 `pet_say` 工具原生就有 `images` 参数(给 `[blob …]` 句柄),`world.ts` 的
94
- `record(meta, text, blobs = [])` 原生把图片附件写进事件;
95
- - `chat.ts` 原生有 `wireImages()`:把事件 `blobs` 里 `image/*` 的映射成 `WireImage`,`ChatItem` 的
96
- say 变体本身带 `images?: WireImage[]`;
97
- - 客户端 bundle 的 say 一段原生就画 `chat-thumb` 缩略图并挂 lightbox。
73
+ **为什么能全删**:0.1.23 起原生就有 `pet_say.images` 与「事件 `blobs` → 对话页缩略图」,
74
+ 0.2.0 起桌宠包的 `pet_say` 会 `host.blob()` 取句柄里的字节、写进事件(`deliver: false`,
75
+ 不会再把图投回模型);而本扩展四个工具的回执本来就带 `blobs`,框架会把它们渲染成 `[blob …]` 句柄。
76
+ `pet_show_image` 当年是唯一「按路径贴图」的入口,现在 `image_show` + `pet_say` 覆盖同一场景——
77
+ 注入的工具、它的相对路径、工作区接线、自描述四处都不再需要,一并删掉。
98
78
 
99
- 原先为此打的 4 处——`client-say-images`(改客户端 bundle)、`pet-say-images`、`pet-say-type`
100
- (都改 `chat.ts`)、`pet-record-blobs`(改 `world.ts` 的 `record()`)——已经删掉。留着不只是多余:
101
- 0.1.23 的 bundle 与新 `chat.ts` 已经换了写法,那几条锚点认不出来,每次启动都会白记三条失败。
102
-
103
- **但 `pet_show_image` 仍然保留**:原生的 `pet_say.images` 只认 `[blob …]` 句柄,不认文件路径,而
104
- `pet_show_image` 正是唯一「按路径贴图」的入口——它的实现与「相对桌宠工作区」那几处补丁照样必需。
79
+ **旧安装怎么收尾**:`revertObsoletePetPatches()` 在每次启动时检查桌宠包的
80
+ `src/world.ts` / `src/definition.ts`——只要文件里还有我们的标记(`coopanion-chat-image` /
81
+ `COOPANION_CHAT_IMAGE_TOOL` / `name: 'pet_show_image'`)且有 `.coopanion-orig` 备份,
82
+ 就用备份覆盖回原文(等于把桌宠包恢复成上游原样),并在「状态」页记一笔。
83
+ 桌宠包自己升级过(标记没了)就一律不动。
105
84
 
106
85
  ### 为什么顺手改了 QQ 扩展(`optional` 那两处)
107
86
 
@@ -110,51 +89,47 @@
110
89
  桌宠手里只有 `blobs/qq-test.jpg` 这种相对工作区的路径,工具先去扩展目录里找,找不到就回一句
111
90
  「本地没有这个文件:…」,图发不出去。
112
91
 
113
- 补上的这一处只做一件事——`resolveLocalImagePath()`:相对路径**先按桌宠工作区
92
+ | 补丁 id | 文件 | 做什么 |
93
+ | --- | --- | --- |
94
+ | `qq-image-relpaths`(可选) | `<安装>\data\extensions\node_modules\cortico-world-qq-better-alont1\src\world.ts` | `qq_send_image` / `qq_view_image` 认「相对桌宠工作区」的路径 |
95
+ | `qq-image-hints`(可选) | 同上 | 那两个工具的参数说明改成「相对桌宠工作区的路径,如 `blobs/xx.jpg`」 |
96
+
97
+ 补上的这处只做一件事——`resolveLocalImagePath()`:相对路径**先按桌宠工作区
114
98
  (`this.config.memoryDir`,装配层给的 `<botDir>/workspace`)解析,工作区没有才退回扩展目录**
115
- (装配层没给工作区时退回老行为);绝对路径原样返回。工具的参数说明也跟着改成
116
- 「绝对路径,或相对桌宠工作区的路径,如 `blobs/xx.jpg`」——模型只看得到说明,说明不改它不敢写短路径。
99
+ (装配层没给工作区时退回老行为);绝对路径原样返回。工具的参数说明也跟着改成同一口径——
100
+ 模型只看得到说明,说明不改它不敢写短路径。
117
101
 
118
- 这个包是 `.pnpm` 装的第三方扩展,升级会换目录、文件也会被覆写,所以走同一套自愈:找不到那份
102
+ 这个包是第三方的,升级会换目录、文件也会被覆写,所以走同一套自愈:找不到那份
119
103
  `world.ts` 记 `absent`(没装这个扩展不算失败、不进总数),认不出它原来的写法记 `failed` 并且
120
104
  **不动那个文件**,原文备份同样是 `world.ts.coopanion-orig`。改的是 TS 源码,**要重启才生效**。
121
105
 
122
- ### `pet_show_image` 的相对路径(`pet-workspace-*` 那三处)
123
-
124
- 同样的毛病在桌宠自己身上(`pet_show_image` 也只认绝对路径,所以「记一句、再发回来」这条最自然的
125
- 用法反而要桌宠去背一长串绝对路径)。桌宠那份世界和 QQ 扩展不一样:它的选项里**没有**工作区字段,
126
- 所以得三处一起改——世界选项加一个可选的 `workspaceDir?: () => string`(`pet-workspace-option`),
127
- `showImage()` 里那行 `const raw = …` 改成 `let` 并加一段「相对路径就拼工作区,没给工作区仍然报错、
128
- 只认绝对路径」(`pet-workspace-relpaths`,同时改工具说明),最后在装配层
129
- `cortico-world-desktop-pet/src/definition.ts` 里把 `join(ctx.botDir, 'workspace')` 传进去
130
- (`pet-workspace-wire`)。
131
-
132
- 三处都是**精确锚点**,认不出原文就记 `failed` 且不动文件;`pet-workspace-relpaths` 的 `done` 判据是
133
- 「真的拼上了工作区」(`raw = join(ws, raw);`),光有工具说明不算打过。装配层没给 `workspaceDir`
134
- (比如桌宠包又改了装配代码、只有前两处打上)时,行为退回老样子:相对路径报错、要求绝对路径——
135
- 不会静默拿到错的路径。
106
+ **切到别的 QQ 扩展就自动让路**:每个补丁都标了它服务的世界 id(`worldId: 'qqbot'`),
107
+ 应用时会读 `<安装>\data\home\companion\config.json` 的 `worlds.qqbot.enabled`——
108
+ 不是 `true`(例如官方 `cortico-world-qq` 顶替了它)就整段跳过,两处都记 `absent`、文件一个字节不动、
109
+ 不请求重启,状态页这张卡片也不显示备份行(0.3.1 起)。读不到配置时**不猜**,照旧检查并打补丁。
136
110
 
137
111
  ### 自愈怎么工作
138
112
 
139
113
  扩展模块的顶层代码**每次启动都会跑**(`loadExtensions()` 无差别 import 所有已装扩展,与
140
114
  `worlds.<id>.enabled` 无关),所以这里不用等用户做任何事:
141
115
 
142
- 1. 启动时打一遍补丁 → 如果这次真的改了文件,状态文件里记 `requested: true`,并在 4 秒后
143
- `POST http://127.0.0.1:<web.port>/api/run/restart` 请控制台重启一次(桌宠侧的 TS 源码要重启才装载);
144
- 2. 重启后再跑:文件已经是打过补丁的样子 → 8 处全部 `done`(没装 QQ 扩展的那 2 处是 `absent`)→ 不再请求重启(**不会反复重启**);
145
- 3. 程序自动更新把文件覆写回原样 → 下一轮启动又补上,再重启一次,回到第 2 步;
116
+ 1. 启动时先**还原**桌宠包上早先版本留下的旧补丁(0.3.0 起不再注入),再检查 QQ 扩展那两处;
117
+ 如果这次真的改了文件,状态文件里记 `requested: true`,并在 4 秒后
118
+ `POST http://127.0.0.1:<web.port>/api/run/restart` 请控制台重启一次(QQ 扩展的 TS 源码要重启才装载);
119
+ 2. 重启后再跑:文件已经是对的样子 → 2 处全部 `done`(没装 QQ 扩展、或 `worlds.qqbot` 没启用时两处都是 `absent`)→ 不再请求重启(**不会反复重启**);
120
+ 3. 程序自动更新(或 QQ 扩展升级)把文件覆写回原样 → 下一轮启动又补上,再重启一次,回到第 2 步;
146
121
  4. 万一「已经请求过重启,这次还是缺」——说明补丁根本落不到进程真正加载的那份文件上,
147
122
  这时记 `stuck`,**故意不再重启**(防死循环),并在「状态」页把 `id — 原因` 列出来给人看。
148
123
 
149
- 状态文件是扩展目录下的 `.chat-image-patch.json`(`{at, appRoot, patched, failed, requested}`)。
124
+ 状态文件是扩展目录下的 `.chat-image-patch.json`(`{at, appRoot, patched, failed, reverted, requested}`)。
150
125
  想彻底关掉这套自愈(比如做回归测试),设环境变量 `COOPANION_CHAT_IMAGE_PATCH=0`。
151
126
 
152
- 控制台「图片工坊 → 状态」页多出一张**发图补丁**卡片:装没装上、8 处在不在、上次检查时间、
127
+ 控制台「图片工坊 → 状态」页有一张**补丁卡片**:QQ 两处打没打上、上次检查时间、
153
128
  每一处的备份(`<文件>.coopanion-orig` 在不在、与当前文件是否一致)、上次还原记录、
154
129
  上次重启请求的结果,外加三个按钮:
155
130
 
156
131
  - **重打补丁**——立刻再对一遍(不改就不请求重启);
157
- - **还原备份**——用更新下发的原文把改动过的文件覆盖回去(可逆,备份不删,随时能再打回来);
132
+ - **还原备份**——用更新下发的原文把改过的文件覆盖回去(可逆,备份不删,随时能再打回来);
158
133
  - **重启控制台**——等价于点菜单里的重启。
159
134
 
160
135
  ### 备份与还原
@@ -168,7 +143,7 @@
168
143
  备份是**每份目标文件旁边的 `<文件>.coopanion-orig`**,第一次改写前拷一次,之后一直留着
169
144
  (再打补丁不会覆盖它——它永远是「更新下发的原文」)。还原 = 拿它覆盖回去,所以:
170
145
 
171
- - 还原后桌宠那一侧要**重启控制台**才回到「没有发图功能」;面板上的「还原备份」故意不自动重启;
146
+ - 还原后 QQ 发图回到「只认扩展目录的相对路径」,要**重启控制台**才生效;面板上的「还原备份」故意不自动重启;
172
147
  - 还原不是终点:下次启动(或再点**重打补丁**)会自动补回来,所以试错很安全。
173
148
 
174
149
  ### 页面陈旧与浏览器缓存
@@ -178,10 +153,10 @@
178
153
  `ws://127.0.0.1:17788/ws/providers/world%3Adesktop-pet/panels/chat` 发 `{"t":"hello"}` 会收到
179
154
  `init`(历史 267 项),桌宠说话也会收到 `item {kind:'say',…}`。**Ctrl+F5 硬刷新一次**就恢复了。
180
155
 
181
- 还有一条是我们这边造成的:扩展面板脚本由服务端按 `/assets/extensions/<包>/<版本>/console.js`
156
+ 还有一条是我们这边造成的:扩展面板脚本由服务端按 `/assets/extensions/<包>/<版本>/webui.js`
182
157
  分配,响应头是 `Cache-Control: public, max-age=31536000, immutable`,而 URL 里的版本号来自
183
158
  `package.json`。所以**改了面板 UI 却没抬版本号时,浏览器会一直用旧脚本**(看不到新按钮)。
184
- 改了 `dist/console.js` 就顺手把 `version` 抬一格,或者让用户硬刷新。
159
+ 改了 `dist/webui.js` 就顺手把 `version` 抬一格,或者让用户硬刷新。
185
160
 
186
161
  **程序内那个控制台窗口更顽固**(Coopanion.exe 自己的窗口,不是外部浏览器):它是长驻的 Chromium
187
162
  渲染进程,磁盘缓存在 `<安装>\data\Cache`,而 `immutable` 让它在新鲜期内**连重启都不回源**。
@@ -192,9 +167,9 @@
192
167
  修法是抬版本 + 重启,主程序会在 Core 就绪时自动重载控制台外壳(
193
168
  `resources\app\app\main.cjs:285` `if (settings) settings.loadURL(consoleUrl());`):
194
169
 
195
- 1. 把 `package.json` 的 `version` 抬一格(例如 `0.1.3` → `0.1.4`);
170
+ 1. 把 `package.json` 的 `version` 抬一格(例如 `0.3.1` → `0.3.2`);
196
171
  2. 重启控制台(状态页「重启控制台」或 `node tools/patch.mjs --restart`);
197
- 3. 程序内窗口自动重载 → 新的 URL `/assets/extensions/cortico-world-image/0.1.4/console.js`
172
+ 3. 程序内窗口自动重载 → 新的 URL `/assets/extensions/cortico-world-image/0.3.1/webui.js`
198
173
  取到新脚本,旧 URL 立刻变 404。
199
174
 
200
175
  不想抬版本也行,但要**先完全退出程序**再删缓存目录:`<安装>\data\Cache`、
@@ -230,7 +205,7 @@ registry 上有版本时直接从 npm 装;想自己改代码、或者 `npm vie
230
205
  或者用命令行,在 `data/extensions` 目录里(`extensions/package.json` 所在处)执行:
231
206
 
232
207
  ```powershell
233
- pnpm add cortico-world-image --ignore-workspace # 想钉住版本就写 cortico-world-image@0.1.4
208
+ pnpm add cortico-world-image --ignore-workspace # 想钉住版本就写 cortico-world-image@0.3.1
234
209
  ```
235
210
 
236
211
  装完重启。升级同理:重跑一遍(控制台的「扩展」页会显示已装版本与最新版本),装完同样要重启。
@@ -256,7 +231,8 @@ pnpm add "D:\path\to\cortico-world-image" --ignore-workspace
256
231
 
257
232
  **装完必须重启 Coopanion**:扩展是 ESM 模块,进程内不会重新加载。重启后在
258
233
  `config.json` 的 `worlds.image.enabled` 上打开(控制台里也有这个开关),四个工具才会挂给桌宠。
259
- 同一轮启动里,扩展还会把「对话页发图」那 8 处补丁检查一遍(其中 2 处是 QQ 扩展的可选补丁,没装就跳过),缺了就补、并自己请控制台重启一次
234
+ 同一轮启动里,扩展还会把 QQ 发图路径那 2 处补丁检查一遍(没装 QQ 扩展、或已切到别的 QQ 扩展就跳过),缺了就补、并自己请控制台重启一次;
235
+ 桌宠包上早先版本留下的旧补丁会在同一轮里被还原成上游原样
260
236
  (详见上一节)。
261
237
 
262
238
  ### 一份能用的 `worlds.image` 长什么样
@@ -341,7 +317,7 @@ pnpm add "D:\path\to\cortico-world-image" --ignore-workspace
341
317
  走完「落盘 → 索引 → 面板取图 → 缩略图显示」整条路。看不到这张图,说明是扩展或面板的问题,
342
318
  不是图片接口的问题。
343
319
 
344
- 开发侧一共有四份检查脚本,外加一份发布前自检。
320
+ 开发侧一共有五份检查脚本,外加一份发布前自检。
345
321
 
346
322
  不碰 DOM 的那份覆盖模块形状、dry-mount 那套判据、工具回执、索引与两个面板的挂载(自带假 DOM):
347
323
 
@@ -361,6 +337,16 @@ node --experimental-transform-types tools/drymount.mts
361
337
  > 参数属性(报 `ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX`),所以必须带
362
338
  > `--experimental-transform-types`(node 22.6+)。带上这个开关就不需要任何额外的 loader。
363
339
 
340
+ 框架 0.2 起每个扩展还要带一份**自描述**(`cortico.describe` → `describe.json`:工具表、事件表,
341
+ 以及环境提示词模板的原文)。这份脚本按同一套判据从定义重新生成一遍,跟包里那份逐字比对:
342
+
343
+ ```powershell
344
+ node --experimental-transform-types tools/describe.mts # 生成 / 刷新 describe.json
345
+ node --experimental-transform-types tools/describe.mts --check # 只核对,不写盘
346
+ ```
347
+
348
+ 改了工具描述、`describe.events` 或 `ENV_PROMPT.md` 之后要重新生成一次,否则发布前自检会拦下来。
349
+
364
350
  第三份会**真的联网画一张图**:拿部署里已有端点的密钥(只读那份 `.env`,不打印密钥),
365
351
  走完「工具回执带图 → 落盘 → 索引 → 面板取图 → 状态页读数」整条路,图存在临时目录里:
366
352
 
@@ -371,18 +357,19 @@ node tools/live.mjs # 可加 LIVE_BASE_URL
371
357
 
372
358
  跑一次视端点而定,通常 20–60 秒(一张 1024×1024)。没有配好密钥的端点就别跑这一份。
373
359
 
374
- 第四份管「对话页发图」那套补丁:默认在临时副本上自测(认锚点、幂等、留备份、还原、esbuild 语法、
375
- 上游改过锚点时必须报失败、自动更新覆写后能补回来但不进重启死循环),另有 `--apply` /
376
- `--backups` / `--restore` / `--restart` 四种真动作,见上面「对话页发图」一节。
360
+ 第四份管 QQ 发图路径那两处补丁:默认在临时副本上自测(认锚点、幂等、留备份、还原、esbuild 语法、
361
+ 上游改过锚点时必须报失败、自动更新覆写后能补回来但不进重启死循环、桌宠包上的旧补丁能被还原),
362
+ 另有 `--apply` / `--backups` / `--restore` / `--restart` 四种真动作,见上面「为什么顺手改了 QQ 扩展」一节。
377
363
 
378
364
  ```powershell
379
365
  node tools/patch.mjs
380
366
  ```
381
367
 
382
- 第五份是**发布前自检**:按宿主 `manifest.ts` 的同一套规则验 `package.json`(`type: module`、
383
- `cortico.api` 是否等于当前框架的 `world` 契约、`consoleClient` 是否真在包里、keywords 里有没有
368
+ 还有一份是**发布前自检**:按宿主 `manifest.ts` 的同一套规则验 `package.json`(`type: module`、
369
+ `cortico.api` 是否等于当前框架的 `world` 契约、`webUiClient` 与 `describe` 是否真在包里、keywords 里有没有
384
370
  `cortico-world`)、按 `files` 展开一遍发布物、扫本机路径与私有端点之类的痕迹、逐个 `node --check`,
385
- 然后自动跑上面的 `check.mjs`(找得到本机 Coopanion 安装时连 `patch.mjs` 自测与 `drymount.mts` 一起跑)。
371
+ 然后自动跑上面的 `check.mjs`(找得到本机 Coopanion 安装时连 `patch.mjs` 自测、`drymount.mts`
372
+ 与 `describe.mts --check` 一起跑)。
386
373
 
387
374
  ```powershell
388
375
  node tools/prepublish.mjs # 或 npm run selfcheck
@@ -396,7 +383,7 @@ node tools/prepublish.mjs --only-static # 只做静态检查
396
383
 
397
384
  ```
398
385
  cortico-world-image/
399
- ├─ package.json # cortico: { kind: world, api: 5, consoleClient: dist/console.js }
386
+ ├─ package.json # cortico: { kind: world, api: 6, webUiClient: dist/webui.js, describe: describe.json }
400
387
  ├─ README.md
401
388
  ├─ PUBLISH.md # 发布流程(维护者用,不打进 npm 包)
402
389
  ├─ ENV_PROMPT.md # 随环境提示词交给桌宠的说明(含 {{image.dir}} / {{image.ready}} 占位符)
@@ -405,16 +392,18 @@ cortico-world-image/
405
392
  │ ├─ definition.js # id/label/defaults/preflight/create
406
393
  │ ├─ config.js # worlds.image 默认值、中英文案、配置组 schema
407
394
  │ ├─ world.js # ImageWorld:工具、控制台面板、流式推送、生命周期
408
- │ ├─ chat-image-patch.js # 对话页发图的 8 处补丁(含 3 处工作区相对路径、2 处可选的 QQ 扩展)、状态文件、重启请求(自愈核心)
395
+ │ ├─ chat-image-patch.js # QQ 发图路径的 2 处可选补丁、旧桌宠补丁还原、状态文件、重启请求(自愈核心)
409
396
  │ ├─ api.js # OpenAI 兼容客户端的取图/落盘/错误归一化
410
397
  │ ├─ gallery.js # 作品仓库:目录 + index.json
411
398
  │ └─ png.js # 零依赖 PNG 编码器 + 自检样图
412
- ├─ dist/console.js # 浏览器侧面板 bundle(export default { panels })
399
+ ├─ describe.json # 自描述(框架 0.2 起必需):工具表、事件表、环境提示词原文
400
+ ├─ dist/webui.js # 浏览器侧面板 bundle(export default { panels })
413
401
  └─ tools/
414
402
  ├─ check.mjs # 离线检查(自带假 DOM)
403
+ ├─ describe.mts # 生成 / 核对 describe.json(--check 只核对)
415
404
  ├─ drymount.mts # 用框架自带判据跑 dry-mount
416
405
  ├─ live.mjs # 联网真画一张(自测整条链路)
417
- ├─ patch.mjs # 对话页发图补丁:自测 / --apply / --backups / --restore / --restart
406
+ ├─ patch.mjs # QQ 发图路径补丁:自测 / --apply / --backups / --restore / --restart
418
407
  └─ prepublish.mjs # 发布前自检:契约 / files / 隐私串 / 语法,顺手跑上面几套
419
408
  ```
420
409
 
@@ -422,10 +411,10 @@ cortico-world-image/
422
411
 
423
412
  - 控制台页面自己有缓存/陈旧问题:换版本或控制台重启后,更新之前就开着的那个标签页可能不再更新,
424
413
  Ctrl+F5 即可(详见「页面陈旧与浏览器缓存」)。
425
- - 桌宠**头顶气泡**仍然只画文字;`pet_show_image` 的图出现在控制台「对话」页(靠上面那套补丁),不在气泡里。
426
- - `pet_show_image` 的相对路径按桌宠工作区解析(`pet-workspace-*` 那三处补丁);万一装配层那处
427
- (`pet-workspace-wire`)没打上、世界里没拿到 `workspaceDir`,行为退回老样子:相对路径报错、
428
- 要求给绝对路径——不会静默拼出一个错的路径。
414
+ - 桌宠**头顶气泡**仍然只画文字;图片出现在控制台「对话」页(`pet_say.images`),不在气泡里。
415
+ - 贴**任意本地文件**要两步(先 `image_show` 换句柄,再 `pet_say`),会比以前的 `pet_show_image` 多一次
416
+ 工具调用、多花约 1k+ token/张(那次调用会把图渲染给模型看)。
417
+ - 桌宠包早于 0.2.0 时没有 `pet_say.images`,贴图这条路走不通(0.3.0 也因此不再支持老桌宠包)。
429
418
  - `image_edit` 先走 `/images/edits`(multipart 上传);端点没这条路由(404/405)时自动退回
430
419
  `/images/generations` 的 `image` 扩展字段(data URI,阿里云百炼那类)。两条路都不通时会回一条
431
420
  带 HTTP 码的错误,不是静默失败。
@@ -437,7 +426,7 @@ cortico-world-image/
437
426
  这个仓库同时就是 npm 包 `cortico-world-image` 的源码。发一版的流程:
438
427
 
439
428
  1. 改完代码、跑完自检(`node tools/prepublish.mjs --app <安装目录>\resources\app` 全绿);
440
- 2. 抬 `package.json` 的 `version`(改了 `dist/console.js` 必须抬,否则浏览器缓存旧脚本);
429
+ 2. 抬 `package.json` 的 `version`(改了 `dist/webui.js` 必须抬,否则浏览器缓存旧脚本);
441
430
  3. 把本仓库整份同步到发布目录(一份纯产物副本,`npm publish` 的根);
442
431
  4. 提交并推进 GitHub;
443
432
  5. 在发布目录里 `npm publish --registry https://registry.npmjs.org/`。
@@ -449,7 +438,7 @@ README 里只写结论,**逐步命令、npm 账号/Token、registry、缓存
449
438
 
450
439
  MIT,见 [LICENSE](LICENSE)。
451
440
 
452
- 这是一个**非官方**扩展:与 Coopanion / cortico 的作者无关。「对话页发图」那部分补丁直接改写
453
- 安装目录里的框架文件与第三方扩展源码(`resources/app` 与 `data/extensions` 下的位置),
441
+ 这是一个**非官方**扩展:与 Coopanion / cortico 的作者无关。QQ 发图路径那两处补丁会改写
442
+ **第三方 QQ 扩展**的源码(`data/extensions/node_modules/cortico-world-qq-better-alont1/src/world.ts`),
454
443
  升级、重装或自动更新前请先看那一节并留好 `.coopanion-orig` 备份;`node tools/patch.mjs --restore`
455
- 就是按这些备份还原。
444
+ 就是按这些备份还原。桌宠包本身不改(早先版本的旧补丁会被还原)。
package/describe.json ADDED
@@ -0,0 +1,132 @@
1
+ {
2
+ "tools": [
3
+ {
4
+ "name": "image_draw",
5
+ "description": "Draw a picture from a text description through the configured image API. The picture is saved to disk, attached to this conversation so you can see it, and shown in the Image Studio console page. The reply carries the absolute file path; hand that path to a channel tool such as qq_send_image to actually deliver the picture to someone.",
6
+ "parameters": {
7
+ "type": "object",
8
+ "properties": {
9
+ "prompt": {
10
+ "type": "string",
11
+ "description": "What to draw: subject, style, composition, lighting. Any language."
12
+ },
13
+ "note": {
14
+ "type": "string",
15
+ "description": "Short label for this picture in the studio list."
16
+ },
17
+ "size": {
18
+ "type": "string",
19
+ "description": "Override the configured size, e.g. 1024x1024 or 1024x1536."
20
+ },
21
+ "quality": {
22
+ "type": "string",
23
+ "description": "Override the configured quality, e.g. low / medium / high."
24
+ }
25
+ },
26
+ "required": [
27
+ "prompt"
28
+ ]
29
+ },
30
+ "tags": [
31
+ "act"
32
+ ],
33
+ "summary": {
34
+ "zh": "按一段文字描述画一张新图,存到磁盘、贴进对话,并显示在作品页。",
35
+ "en": "Draw a new picture from a text description, save it to disk and show it in the conversation."
36
+ }
37
+ },
38
+ {
39
+ "name": "image_edit",
40
+ "description": "Redraw an existing local picture with the image API, following a text instruction. `image` takes an absolute file path, or a picture id from image_gallery. Works with endpoints that implement /images/edits (multipart upload) and with those that take a base64 `image` field on /images/generations instead (e.g. Aliyun Bailian).",
41
+ "parameters": {
42
+ "type": "object",
43
+ "properties": {
44
+ "prompt": {
45
+ "type": "string",
46
+ "description": "What to change or how to restyle it."
47
+ },
48
+ "image": {
49
+ "type": "string",
50
+ "description": "Absolute path of the source picture, or a picture id from image_gallery."
51
+ },
52
+ "note": {
53
+ "type": "string",
54
+ "description": "Short label for the result in the studio list."
55
+ },
56
+ "size": {
57
+ "type": "string",
58
+ "description": "Override the configured size."
59
+ },
60
+ "quality": {
61
+ "type": "string",
62
+ "description": "Override the configured quality."
63
+ }
64
+ },
65
+ "required": [
66
+ "prompt",
67
+ "image"
68
+ ]
69
+ },
70
+ "tags": [
71
+ "act"
72
+ ],
73
+ "summary": {
74
+ "zh": "按一句指令改一张已有的本地图,结果同样落盘并贴进对话。",
75
+ "en": "Redraw an existing local picture following a text instruction."
76
+ }
77
+ },
78
+ {
79
+ "name": "image_gallery",
80
+ "description": "List pictures drawn so far, newest first, with their ids and absolute file paths. Ids can be handed to image_show.",
81
+ "parameters": {
82
+ "type": "object",
83
+ "properties": {
84
+ "limit": {
85
+ "type": "integer",
86
+ "description": "How many to list, 1-50. Defaults to 12."
87
+ }
88
+ },
89
+ "required": []
90
+ },
91
+ "tags": [
92
+ "read"
93
+ ],
94
+ "summary": {
95
+ "zh": "列出已经画好的图,最新的在前,带编号与绝对路径。",
96
+ "en": "List pictures drawn so far, newest first, with their ids and absolute paths."
97
+ }
98
+ },
99
+ {
100
+ "name": "image_show",
101
+ "description": "Put an existing picture back in front of the user: it is re-attached to this conversation and highlighted in the Image Studio console page. Use it to look at or talk about an older picture again.",
102
+ "parameters": {
103
+ "type": "object",
104
+ "properties": {
105
+ "id": {
106
+ "type": "string",
107
+ "description": "Picture id from image_gallery, or its absolute path."
108
+ }
109
+ },
110
+ "required": [
111
+ "id"
112
+ ]
113
+ },
114
+ "tags": [
115
+ "speak"
116
+ ],
117
+ "summary": {
118
+ "zh": "把一张已有的图重新贴回对话,并在作品页高亮。",
119
+ "en": "Put an existing picture back in front of the user."
120
+ }
121
+ }
122
+ ],
123
+ "events": [],
124
+ "prompts": [
125
+ {
126
+ "key": "worlds.image.envPrompt",
127
+ "title": "环境说明(图片工坊)",
128
+ "role": "envPrompt",
129
+ "content": "你可以作画。这一节说明工具箱和规矩。\n\n## 四个工具\n\n- `image_draw`:按一段文字描述画一张新图。图会存到磁盘、附加进本次对话(你自己看得到)、并显示在控制台「图片工坊 → 作品」页。\n- `image_edit`:拿一张已有的图按指令改(换风格、改细节)。`image` 给绝对路径,或 `image_gallery` 里的编号。服务端支不支持改图由接口决定,报错就照实告诉用户。\n- `image_gallery`:列出画过的图,最新的在前,带编号和绝对路径。编号可以交给 `image_show` 或 `image_edit`。\n- `image_show`:把一张旧图重新放回眼前(重新附加进对话,并在作品页高亮)。\n\n图片保存目录:`{{image.dir | 尚未确定;先看控制台「图片工坊 → 状态」。}}`\n\n当前接口状态:`{{image.ready | 未知。不确定就别调用,先告诉用户去控制台「图片工坊 → 状态」看看。}}`\n\n## 规矩\n\n1. **别每轮都画。** 用户明确想看/想让你画,或者确实需要一张图来解释、来送人,才调用。闲聊、讨论、解释概念都不需要配图。\n2. **一次画一张。** 想要不同方向就画一张、看一眼、再决定要不要重画或改。不要一次调好几个 `image_draw` 碰运气。\n3. **描述要具体。** 主体、画风、构图、光线、色调、背景,一句到位;别只给两三个词,也别把整段聊天记录塞进去。\n4. **说人话,不念路径。** 回执里的绝对路径是给工具用的,不是给用户看的。画完就用一句自然的话告诉用户画了什么,像 `pet_say` 那样说话。不要把路径、编号、`blobs`、JSON 念出来,也不要贴 Markdown 图片语法。\n5. **发到别处要另走一步。** 图默认只在你自己的界面里。\n - 想让主人在控制台的「对话」页看到它,画完之后调桌宠的 `pet_say`,`images` 填回执里 `[blob …]` 行的句柄(就是 `[blob ` 后面那一串),`script` 给空串就只贴图不出声;想配一句话就写进 `script`(那句话会在气泡里说出来)。图只进「对话」页,不进气泡。\n - 要发到 QQ 会话:把回执里 `[blob …]` 行的句柄交给 QQ 世界的发图工具。官方 QQ 扩展用 `qq_draft`(`image` 填句柄,`to` 填 `group:<群号>` 或 `private:<QQ号>`,再用 `qq_confirm` 确认发送);旧版 QQ 扩展(`qqbot`)用 `qq_send_image`,`image` 填回执里的绝对路径。\n6. **失败就停。** 如果回执说没配密钥、接口报错、超时,不要换个说法反复重试。把情况告诉用户,让他去控制台「图片工坊 → 状态」里填密钥或改接口地址。\n7. **别凭空说画好了。** 只有工具回执真的带着图回来,才算画好了。\n"
130
+ }
131
+ ]
132
+ }