cortico-world-image 0.2.0 → 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
5
  这是一个 `cortico` 的 **World 扩展**(`kind: world`,`api: 6`),只补一个 `image` 世界,
6
6
  不依赖任何第三方 npm 包(纯 ESM JS + Node 内置模块)。
7
7
 
8
8
  > **性质说明**:这是给 Coopanion 写的**非官方**扩展,走的是框架公开的扩展接口
9
- > (`kind: world` + `webUiClient` 面板 + 必需的 `describe.json` 自描述)。唯一伸手动别人的地方是「对话页发图」那一节的
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
- 所以升级换了写法之后照样能打上。
71
+ | `pet-show-image-describe` | 桌宠 `src/definition.ts` | 把 `pet_show_image` 补进 `describe.tools`(框架 0.2 会核对,缺了记「工具不在 describe.tools 里」) |
88
72
 
89
- ### 框架 0.1.23 之后为什么少了 4 处补丁
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
+ 注入的工具、它的相对路径、工作区接线、自描述四处都不再需要,一并删掉。
90
78
 
91
- 0.1.23 起「把图发到对话页」是框架原生能力,不再是这个扩展要补的洞:
92
-
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。
98
-
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
  ### 页面陈旧与浏览器缓存
@@ -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.2.0/webui.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` 长什么样
@@ -381,9 +357,9 @@ node tools/live.mjs # 可加 LIVE_BASE_URL
381
357
 
382
358
  跑一次视端点而定,通常 20–60 秒(一张 1024×1024)。没有配好密钥的端点就别跑这一份。
383
359
 
384
- 第四份管「对话页发图」那套补丁:默认在临时副本上自测(认锚点、幂等、留备份、还原、esbuild 语法、
385
- 上游改过锚点时必须报失败、自动更新覆写后能补回来但不进重启死循环),另有 `--apply` /
386
- `--backups` / `--restore` / `--restart` 四种真动作,见上面「对话页发图」一节。
360
+ 第四份管 QQ 发图路径那两处补丁:默认在临时副本上自测(认锚点、幂等、留备份、还原、esbuild 语法、
361
+ 上游改过锚点时必须报失败、自动更新覆写后能补回来但不进重启死循环、桌宠包上的旧补丁能被还原),
362
+ 另有 `--apply` / `--backups` / `--restore` / `--restart` 四种真动作,见上面「为什么顺手改了 QQ 扩展」一节。
387
363
 
388
364
  ```powershell
389
365
  node tools/patch.mjs
@@ -416,7 +392,7 @@ cortico-world-image/
416
392
  │ ├─ definition.js # id/label/defaults/preflight/create
417
393
  │ ├─ config.js # worlds.image 默认值、中英文案、配置组 schema
418
394
  │ ├─ world.js # ImageWorld:工具、控制台面板、流式推送、生命周期
419
- │ ├─ chat-image-patch.js # 对话页发图的 8 处补丁(含 3 处工作区相对路径、2 处可选的 QQ 扩展)、状态文件、重启请求(自愈核心)
395
+ │ ├─ chat-image-patch.js # QQ 发图路径的 2 处可选补丁、旧桌宠补丁还原、状态文件、重启请求(自愈核心)
420
396
  │ ├─ api.js # OpenAI 兼容客户端的取图/落盘/错误归一化
421
397
  │ ├─ gallery.js # 作品仓库:目录 + index.json
422
398
  │ └─ png.js # 零依赖 PNG 编码器 + 自检样图
@@ -427,7 +403,7 @@ cortico-world-image/
427
403
  ├─ describe.mts # 生成 / 核对 describe.json(--check 只核对)
428
404
  ├─ drymount.mts # 用框架自带判据跑 dry-mount
429
405
  ├─ live.mjs # 联网真画一张(自测整条链路)
430
- ├─ patch.mjs # 对话页发图补丁:自测 / --apply / --backups / --restore / --restart
406
+ ├─ patch.mjs # QQ 发图路径补丁:自测 / --apply / --backups / --restore / --restart
431
407
  └─ prepublish.mjs # 发布前自检:契约 / files / 隐私串 / 语法,顺手跑上面几套
432
408
  ```
433
409
 
@@ -435,10 +411,10 @@ cortico-world-image/
435
411
 
436
412
  - 控制台页面自己有缓存/陈旧问题:换版本或控制台重启后,更新之前就开着的那个标签页可能不再更新,
437
413
  Ctrl+F5 即可(详见「页面陈旧与浏览器缓存」)。
438
- - 桌宠**头顶气泡**仍然只画文字;`pet_show_image` 的图出现在控制台「对话」页(靠上面那套补丁),不在气泡里。
439
- - `pet_show_image` 的相对路径按桌宠工作区解析(`pet-workspace-*` 那三处补丁);万一装配层那处
440
- (`pet-workspace-wire`)没打上、世界里没拿到 `workspaceDir`,行为退回老样子:相对路径报错、
441
- 要求给绝对路径——不会静默拼出一个错的路径。
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 也因此不再支持老桌宠包)。
442
418
  - `image_edit` 先走 `/images/edits`(multipart 上传);端点没这条路由(404/405)时自动退回
443
419
  `/images/generations` 的 `image` 扩展字段(data URI,阿里云百炼那类)。两条路都不通时会回一条
444
420
  带 HTTP 码的错误,不是静默失败。
@@ -462,7 +438,7 @@ README 里只写结论,**逐步命令、npm 账号/Token、registry、缓存
462
438
 
463
439
  MIT,见 [LICENSE](LICENSE)。
464
440
 
465
- 这是一个**非官方**扩展:与 Coopanion / cortico 的作者无关。「对话页发图」那部分补丁直接改写
466
- 安装目录里的框架文件与第三方扩展源码(`resources/app` 与 `data/extensions` 下的位置),
441
+ 这是一个**非官方**扩展:与 Coopanion / cortico 的作者无关。QQ 发图路径那两处补丁会改写
442
+ **第三方 QQ 扩展**的源码(`data/extensions/node_modules/cortico-world-qq-better-alont1/src/world.ts`),
467
443
  升级、重装或自动更新前请先看那一节并留好 `.coopanion-orig` 备份;`node tools/patch.mjs --restore`
468
- 就是按这些备份还原。
444
+ 就是按这些备份还原。桌宠包本身不改(早先版本的旧补丁会被还原)。
package/describe.json CHANGED
@@ -126,7 +126,7 @@
126
126
  "key": "worlds.image.envPrompt",
127
127
  "title": "环境说明(图片工坊)",
128
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_show_image`:`path` 填 `image_draw` 回执里的绝对路径,`caption` 可选(一句配文)。它只贴图、不出声,也不会引发语音。\n - 要发到 QQ 会话,画完之后再调 `qq_send_image`,`image` 参数填 `image_draw` 回执里那个绝对路径。\n6. **失败就停。** 如果回执说没配密钥、接口报错、超时,不要换个说法反复重试。把情况告诉用户,让他去控制台「图片工坊 → 状态」里填密钥或改接口地址。\n7. **别凭空说画好了。** 只有工具回执真的带着图回来,才算画好了。\n"
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
130
  }
131
131
  ]
132
132
  }
package/dist/webui.js CHANGED
@@ -84,13 +84,14 @@ const ZH = {
84
84
  runtime: '运行时',
85
85
  unknown: '未知',
86
86
  loadBad: '读不到作品列表',
87
- patchTitle: '对话页发图',
88
- patchDesc: '桌宠要在控制台「对话」页发图,得改安装目录里的两处代码(页面渲染、桌宠的事件出口)。程序自动更新会把它们覆写回去,所以每次启动都重打一遍;打不上会显示在这里。',
87
+ patchTitle: 'QQ 发图路径',
88
+ patchDesc: '旧版 QQ 扩展(qqbot)的 qq_send_image / qq_view_image 只按扩展目录解析本地路径;这里把它们改成先按桌宠工作区解析、扩展目录兜底。第三方包升级会把改动覆写掉,所以每次启动都重打一遍;打不上会显示在这里。切到官方 QQ 扩展(qq)后它按 blob 句柄发图,这两处自动跳过、不碰文件。',
89
89
  patchWhere: '安装目录',
90
90
  patchCount: '补丁',
91
91
  patchAt: '上次检查',
92
92
  patchLastRestart: '上次重启请求',
93
- patchAllDone: '7 处都在',
93
+ patchAllDone: '处都在',
94
+ patchNone: '没有要打补丁的 QQ 扩展',
94
95
  patchPending: '刚补上,重启后生效',
95
96
  patchMissing: '有没打上的',
96
97
  patchNoApp: '没找到安装目录',
@@ -100,7 +101,7 @@ const ZH = {
100
101
  patchRepatch: '重打补丁',
101
102
  patchRestore: '还原备份',
102
103
  patchRestoreAsk: '按备份还原补丁?',
103
- patchRestoreBody: '用更新下发的原文(.coopanion-orig)覆盖回这三份文件,桌宠发图会随之失效(重启控制台后生效)。备份不会删,随时能再打回来。',
104
+ patchRestoreBody: '用更新下发的原文(.coopanion-orig)覆盖回 QQ 扩展的 world.ts,QQ 发图改回只认扩展目录里的相对路径(重启控制台后生效)。备份不会删,随时能再打回来。',
104
105
  patchRestoreDone: '已按备份还原',
105
106
  patchRestoreNone: '没动:文件本来就是原文',
106
107
  patchRestoreBad: '还原出错',
@@ -111,7 +112,7 @@ const ZH = {
111
112
  patchBackupSame: '与当前一致',
112
113
  patchRestart: '重启控制台',
113
114
  patchRestartAsk: '现在重启控制台?',
114
- patchRestartBody: '控制台会自己重启(几秒钟),正在跑的对话会断。桌宠那一侧的补丁要重启才装载。',
115
+ patchRestartBody: '控制台会自己重启(几秒钟),正在跑的对话会断。QQ 扩展的补丁要重启才装载。',
115
116
  patchRestartSent: '重启请求已发出,稍等几秒',
116
117
  patchRestartBad: '重启请求没送到',
117
118
  patchRepatched: '又对了一遍',
@@ -180,13 +181,14 @@ const EN = {
180
181
  runtime: 'Runtime',
181
182
  unknown: 'unknown',
182
183
  loadBad: 'Could not read the picture list',
183
- patchTitle: 'Images in Chat',
184
- patchDesc: 'Sending a picture to the console Chat page needs two code patches in the install directory (the page renderer and the pet\'s event outlet). The updater overwrites them, so they are re-applied on every start; anything that fails shows up here.',
184
+ patchTitle: 'QQ Image Paths',
185
+ patchDesc: 'The legacy QQ extension (qqbot) resolves local picture paths against its own folder; these two patches make qq_send_image / qq_view_image try your pet workspace first. The updater overwrites them, so they are re-applied on every start; anything that fails shows up here. With the official QQ extension (qq) pictures travel as blob handles, so both patches are skipped and no file is touched.',
185
186
  patchWhere: 'Install dir',
186
187
  patchCount: 'Patches',
187
188
  patchAt: 'Checked',
188
189
  patchLastRestart: 'Last restart request',
189
- patchAllDone: 'all 7 in place',
190
+ patchAllDone: 'in place',
191
+ patchNone: 'no QQ extension needs patching',
190
192
  patchPending: 'applied, takes effect after restart',
191
193
  patchMissing: 'some are missing',
192
194
  patchNoApp: 'install directory not found',
@@ -196,7 +198,7 @@ const EN = {
196
198
  patchRepatch: 'Re-apply',
197
199
  patchRestore: 'Restore from backup',
198
200
  patchRestoreAsk: 'Restore the patches from backup?',
199
- patchRestoreBody: 'Overwrites the three files with the updater-shipped originals (.coopanion-orig); the pet loses image sending after a console restart. Backups are kept, so you can re-apply any time.',
201
+ patchRestoreBody: 'Overwrites the QQ extension world.ts with the updater-shipped original (.coopanion-orig); QQ images fall back to extension-relative paths after a console restart. Backups are kept, so you can re-apply any time.',
200
202
  patchRestoreDone: 'Restored from backup',
201
203
  patchRestoreNone: 'Nothing changed: files were already pristine',
202
204
  patchRestoreBad: 'Restore failed',
@@ -207,7 +209,7 @@ const EN = {
207
209
  patchBackupSame: 'same as current file',
208
210
  patchRestart: 'Restart console',
209
211
  patchRestartAsk: 'Restart the console now?',
210
- patchRestartBody: 'The console restarts by itself (a few seconds); a running conversation is cut off. The pet-side patch only loads on restart.',
212
+ patchRestartBody: 'The console restarts by itself (a few seconds); a running conversation is cut off. The QQ patch only loads on restart.',
211
213
  patchRestartSent: 'Restart requested, give it a few seconds',
212
214
  patchRestartBad: 'Restart request did not go through',
213
215
  patchRepatched: 'Checked again',
@@ -556,7 +558,7 @@ function statusPanel(ctx) {
556
558
 
557
559
  let currentDir = '';
558
560
 
559
- /** 「对话页发图」补丁:7 处都在?这次补了几处?备份还在吗?要不要重启? */
561
+ /** QQ 发图路径补丁:2 处都在?这次补了几处?备份还在吗?要不要重启? */
560
562
  const renderPatch = (p) => {
561
563
  const total = Number(p?.total ?? 0);
562
564
  const done = Number(p?.done ?? 0);
@@ -573,7 +575,7 @@ function statusPanel(ctx) {
573
575
  ? tx.patchUnchecked
574
576
  : restored.length
575
577
  ? tx.patchRestored
576
- : (pending ? tx.patchPending : `${tx.patchAllDone}`);
578
+ : (pending ? tx.patchPending : (total === 0 ? tx.patchNone : `${done}/${total} ${tx.patchAllDone}`));
577
579
  const tone = p?.error || failed.length || restored.length ? 'off' : 'on';
578
580
  patchHead.replaceChildren(ctx.ui.pill(label, tone));
579
581
  if (patched.length) patchHead.appendChild(ctx.ui.pill(`${tx.patchApplied} ${patched.length}`, 'on'));
@@ -583,7 +585,7 @@ function statusPanel(ctx) {
583
585
  : tx.none;
584
586
  patchKv.replaceChildren(ctx.ui.kv([
585
587
  { k: tx.patchWhere, v: ctx.ui.h('span', 'mono', String(p?.appRoot || '—')) },
586
- { k: tx.patchCount, v: `${done}/${total}` },
588
+ { k: tx.patchCount, v: total === 0 ? (tx.patchNone) : `${done}/${total}` },
587
589
  { k: tx.patchAt, v: p?.at ? ctx.ui.fmt.clock(p.at) : tx.none },
588
590
  { k: tx.patchBackup, v: ctx.ui.h('span', 'mono', backupText) },
589
591
  ...(restored.length
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "cortico-world-image",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
- "description": "【注意:未升级0.2.0以上版本请安装本插件的上一个历史版本】Image Studio(图片工坊)—— 给 Coopanion 桌宠的 cortico World 扩展:用任意 OpenAI 兼容的图片接口(包括阿里)画图/改图、落盘成作品库、把图贴进控制台「对话」页,并自带一套自动重打的自愈补丁。 / A cortico World extension that draws through any OpenAI-compatible image API, keeps pictures on disk and shows them in the console.",
5
+ "description": "【0.3+仅支持0.2.4+的coopanion(支持官方qq插件和alont1 不再给coo本体打补丁)/0.2+支持0.2.0-0.2.3(支持alont1QQ)/再往前的版本装0.1.x的】Image Studio(图片工坊)—— 给 Coopanion 桌宠的 cortico World 扩展:用任意 OpenAI 兼容的图片接口画图/改图、落盘成作品库、把图贴进控制台「对话」页,并自带一套自动重打的自愈补丁。 / A cortico World extension that draws through any OpenAI-compatible image API, keeps pictures on disk and shows them in the console.",
6
6
  "license": "MIT",
7
7
  "author": "lostdegree",
8
8
  "homepage": "https://github.com/Lostdegree/cortico-world-image#readme",