foliko 1.1.1 → 1.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/.agent/data/weixin-media/2026-04-08/img_1775618677512.jpg +0 -0
- package/.agent/data/weixin-media/2026-04-08/img_1775619073340.jpg +0 -0
- package/.agent/data/weixin-media/2026-04-08/img_1775619097536.jpg +0 -0
- package/.agent/data/weixin-media/2026-04-08/img_1775619209388.jpg +0 -0
- package/.agent/plugins/poster-plugin/package.json +2 -1
- package/.agent/plugins/poster-plugin/src/canvas.js +70 -7
- package/.agent/plugins/poster-plugin/src/components/barcode.js +120 -0
- package/.agent/plugins/poster-plugin/src/components/bubble.js +153 -0
- package/.agent/plugins/poster-plugin/src/components/button.js +124 -0
- package/.agent/plugins/poster-plugin/src/components/cta.js +26 -24
- package/.agent/plugins/poster-plugin/src/components/featureGrid.js +22 -17
- package/.agent/plugins/poster-plugin/src/components/frame.js +230 -0
- package/.agent/plugins/poster-plugin/src/components/highlightText.js +144 -0
- package/.agent/plugins/poster-plugin/src/components/icon.js +94 -0
- package/.agent/plugins/poster-plugin/src/components/index.js +19 -0
- package/.agent/plugins/poster-plugin/src/components/listItem.js +6 -5
- package/.agent/plugins/poster-plugin/src/components/qrcode.js +74 -0
- package/.agent/plugins/poster-plugin/src/components/ribbon.js +193 -0
- package/.agent/plugins/poster-plugin/src/components/seal.js +146 -0
- package/.agent/plugins/poster-plugin/src/components/table.js +17 -9
- package/.agent/plugins/poster-plugin/src/components/tagCloud.js +24 -17
- package/.agent/plugins/poster-plugin/src/components/timeline.js +24 -12
- package/.agent/plugins/poster-plugin/src/composer.js +392 -150
- package/.agent/plugins/poster-plugin/src/elements/background.js +36 -4
- package/.agent/plugins/poster-plugin/src/elements/image.js +4 -47
- package/.agent/plugins/poster-plugin/src/elements/index.js +2 -0
- package/.agent/plugins/poster-plugin/src/elements/richText.js +230 -0
- package/.agent/plugins/poster-plugin/src/elements/svg.js +35 -19
- package/.agent/plugins/poster-plugin/src/index.js +430 -7
- package/.agent/plugins/poster-plugin/src/utils/imageLoader.js +84 -0
- package/.agent/plugins/poster-plugin/test-background.svg +1 -0
- package/.agent/plugins/poster-plugin/test-full-poster.svg +2 -0
- package/.agent/plugins/poster-plugin/test-image.png +0 -0
- package/.agent/sessions/cli_default.json +1089 -145
- package/.agent/sessions/weixin_o9cq80zgZqKPA2-s59PN43GdDy1w@im.wechat.json +8902 -0
- package/.claude/settings.local.json +6 -1
- package/output/beef-love-poster.png +0 -0
- package/output/international-news-daily.png +0 -0
- package/package.json +2 -1
- package/plugins/extension-executor-plugin.js +33 -32
- package/plugins/file-system-plugin.js +4 -19
- package/plugins/subagent-plugin.js +37 -14
- package/plugins/weixin-plugin.js +167 -47
- package/poster-test-2.png +0 -0
- package/skills/poster-guide/SKILL.md +497 -5
- package/src/core/agent-chat.js +141 -8
- package/src/core/agent.js +6 -3
- package/calc_tokens_weixin.js +0 -81
- package/foliko-creative-3.png +0 -0
- package/foliko-creative-4.png +0 -0
- package/foliko-creative-5.png +0 -0
- package/story-cover-book-v2.png +0 -0
- package/story-cover-japanese-1.png +0 -0
- package/story-cover-japanese-2.png +0 -0
- package/story-cover-japanese-3.png +0 -0
- package/story-cover-moran.png +0 -0
- package/undefined.png +0 -0
|
@@ -637,13 +637,263 @@ ext_call({
|
|
|
637
637
|
| `add_poster_list_item` | 列表项 |
|
|
638
638
|
| `add_poster_notification` | 通知 |
|
|
639
639
|
|
|
640
|
+
### 设计组件(新增)
|
|
641
|
+
|
|
642
|
+
| 工具 | 说明 |
|
|
643
|
+
| --------------------------- | --------- |
|
|
644
|
+
| `add_poster_button` | 按钮 |
|
|
645
|
+
| `add_poster_icon` | 图标 |
|
|
646
|
+
| `add_poster_qrcode` | 二维码 |
|
|
647
|
+
| `add_poster_frame` | 装饰边框 |
|
|
648
|
+
| `add_poster_bubble` | 对话气泡 |
|
|
649
|
+
| `add_poster_ribbon` | 丝带/飘带 |
|
|
650
|
+
| `add_poster_seal` | 印章 |
|
|
651
|
+
| `add_poster_highlight_text` | 高亮文字 |
|
|
652
|
+
| `add_poster_barcode` | 条形码 |
|
|
653
|
+
|
|
640
654
|
### 模板
|
|
641
655
|
|
|
642
|
-
| 工具 | 说明
|
|
643
|
-
| ------------------------ |
|
|
644
|
-
| `generate_poster` | 一键生成
|
|
645
|
-
| `list_poster_templates` | 列出模板
|
|
646
|
-
| `list_poster_components` | 列出组件
|
|
656
|
+
| 工具 | 说明 |
|
|
657
|
+
| ------------------------ | ------------ |
|
|
658
|
+
| `generate_poster` | 一键生成 |
|
|
659
|
+
| `list_poster_templates` | 列出模板 |
|
|
660
|
+
| `list_poster_components` | 列出组件 |
|
|
661
|
+
| `list_poster_presets` | 列出预设尺寸 |
|
|
662
|
+
|
|
663
|
+
---
|
|
664
|
+
|
|
665
|
+
## 🆕 新增设计组件详解
|
|
666
|
+
|
|
667
|
+
### 按钮 (button)
|
|
668
|
+
|
|
669
|
+
支持图标、渐变、阴影的现代化按钮。
|
|
670
|
+
|
|
671
|
+
```javascript
|
|
672
|
+
ext_call({
|
|
673
|
+
plugin: 'poster',
|
|
674
|
+
tool: 'add_poster_button',
|
|
675
|
+
args: {
|
|
676
|
+
x: 960,
|
|
677
|
+
y: 800,
|
|
678
|
+
width: 280,
|
|
679
|
+
height: 70,
|
|
680
|
+
text: '立即购买',
|
|
681
|
+
fontSize: 28,
|
|
682
|
+
color: '#ffffff',
|
|
683
|
+
backgroundColor: '#3b82f6',
|
|
684
|
+
borderColor: '#2563eb',
|
|
685
|
+
borderWidth: 2,
|
|
686
|
+
radius: 35,
|
|
687
|
+
shadow: { color: '#3b82f6', blur: 20, offsetX: 0, offsetY: 8 },
|
|
688
|
+
gradient: { colors: ['#3b82f6', '#8b5cf6'] },
|
|
689
|
+
icon: '🛒',
|
|
690
|
+
iconPosition: 'left',
|
|
691
|
+
},
|
|
692
|
+
});
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
### 图标 (icon)
|
|
696
|
+
|
|
697
|
+
支持 emoji 和图片的图标组件。
|
|
698
|
+
|
|
699
|
+
```javascript
|
|
700
|
+
ext_call({
|
|
701
|
+
plugin: 'poster',
|
|
702
|
+
tool: 'add_poster_icon',
|
|
703
|
+
args: {
|
|
704
|
+
x: 100,
|
|
705
|
+
y: 100,
|
|
706
|
+
size: 80,
|
|
707
|
+
icon: '⭐',
|
|
708
|
+
color: '#fbbf24',
|
|
709
|
+
backgroundColor: '#1e293b',
|
|
710
|
+
borderColor: '#fbbf24',
|
|
711
|
+
borderWidth: 2,
|
|
712
|
+
radius: 16,
|
|
713
|
+
shadow: { color: '#000000', blur: 10, offsetX: 2, offsetY: 4 },
|
|
714
|
+
},
|
|
715
|
+
});
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
### 二维码 (qrcode)
|
|
719
|
+
|
|
720
|
+
本地生成二维码,支持自定义颜色和 Logo。
|
|
721
|
+
|
|
722
|
+
```javascript
|
|
723
|
+
ext_call({
|
|
724
|
+
plugin: 'poster',
|
|
725
|
+
tool: 'add_poster_qrcode',
|
|
726
|
+
args: {
|
|
727
|
+
x: 1600,
|
|
728
|
+
y: 700,
|
|
729
|
+
size: 200,
|
|
730
|
+
content: 'https://example.com',
|
|
731
|
+
color: '#0f172a',
|
|
732
|
+
backgroundColor: '#ffffff',
|
|
733
|
+
logo: 'https://example.com/logo.png',
|
|
734
|
+
logoSize: 40,
|
|
735
|
+
},
|
|
736
|
+
});
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
### 装饰边框 (frame)
|
|
740
|
+
|
|
741
|
+
多种边框样式:simple、double、dashed、dotted、corner、vintage、modern、floral。
|
|
742
|
+
|
|
743
|
+
```javascript
|
|
744
|
+
ext_call({
|
|
745
|
+
plugin: 'poster',
|
|
746
|
+
tool: 'add_poster_frame',
|
|
747
|
+
args: {
|
|
748
|
+
x: 100,
|
|
749
|
+
y: 100,
|
|
750
|
+
width: 500,
|
|
751
|
+
height: 400,
|
|
752
|
+
style: 'vintage', // 边框样式
|
|
753
|
+
color: '#c9a227',
|
|
754
|
+
borderWidth: 3,
|
|
755
|
+
radius: 8,
|
|
756
|
+
padding: 20,
|
|
757
|
+
},
|
|
758
|
+
});
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
**style 可选值:**
|
|
762
|
+
|
|
763
|
+
- `simple` - 简单矩形
|
|
764
|
+
- `double` - 双线边框
|
|
765
|
+
- `dashed` - 虚线边框
|
|
766
|
+
- `dotted` - 点线边框
|
|
767
|
+
- `corner` - 四角装饰
|
|
768
|
+
- `vintage` - 复古边框(双线+角装饰)
|
|
769
|
+
- `modern` - 现代边框(粗细交替)
|
|
770
|
+
- `floral` - 花纹边框
|
|
771
|
+
|
|
772
|
+
### 对话气泡 (bubble)
|
|
773
|
+
|
|
774
|
+
带尾巴的对话气泡组件。
|
|
775
|
+
|
|
776
|
+
```javascript
|
|
777
|
+
ext_call({
|
|
778
|
+
plugin: 'poster',
|
|
779
|
+
tool: 'add_poster_bubble',
|
|
780
|
+
args: {
|
|
781
|
+
x: 200,
|
|
782
|
+
y: 500,
|
|
783
|
+
width: 400,
|
|
784
|
+
height: 120,
|
|
785
|
+
text: '你好!有什么可以帮助你的吗?',
|
|
786
|
+
fontSize: 24,
|
|
787
|
+
color: '#1e293b',
|
|
788
|
+
backgroundColor: '#ffffff',
|
|
789
|
+
borderColor: '#e2e8f0',
|
|
790
|
+
borderWidth: 1,
|
|
791
|
+
radius: 20,
|
|
792
|
+
tailDirection: 'bottom', // 尾巴方向: bottom, top, left, right
|
|
793
|
+
tailPosition: 'left', // 尾巴位置: left, center, right
|
|
794
|
+
shadow: { color: '#000000', blur: 10, offsetX: 3, offsetY: 3 },
|
|
795
|
+
},
|
|
796
|
+
});
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
### 丝带/飘带 (ribbon)
|
|
800
|
+
|
|
801
|
+
三种样式:fold(折叠)、diagonal(对角)、corner(角落)。
|
|
802
|
+
|
|
803
|
+
```javascript
|
|
804
|
+
ext_call({
|
|
805
|
+
plugin: 'poster',
|
|
806
|
+
tool: 'add_poster_ribbon',
|
|
807
|
+
args: {
|
|
808
|
+
x: 100,
|
|
809
|
+
y: 50,
|
|
810
|
+
width: 200,
|
|
811
|
+
text: '限时特惠',
|
|
812
|
+
fontSize: 24,
|
|
813
|
+
color: '#ffffff',
|
|
814
|
+
backgroundColor: '#e74c3c',
|
|
815
|
+
borderColor: '#c0392b',
|
|
816
|
+
borderWidth: 2,
|
|
817
|
+
style: 'fold', // fold, diagonal, corner
|
|
818
|
+
position: 'top',
|
|
819
|
+
shadow: { color: '#000000', blur: 8, offsetX: 3, offsetY: 3 },
|
|
820
|
+
},
|
|
821
|
+
});
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
### 印章 (seal)
|
|
825
|
+
|
|
826
|
+
印章效果组件,支持多种形状。
|
|
827
|
+
|
|
828
|
+
```javascript
|
|
829
|
+
ext_call({
|
|
830
|
+
plugin: 'poster',
|
|
831
|
+
tool: 'add_poster_seal',
|
|
832
|
+
args: {
|
|
833
|
+
x: 1700,
|
|
834
|
+
y: 900,
|
|
835
|
+
size: 120,
|
|
836
|
+
text: '正品',
|
|
837
|
+
fontSize: 20,
|
|
838
|
+
color: '#e74c3c',
|
|
839
|
+
style: 'circle', // circle, square, star, hexagon
|
|
840
|
+
borderWidth: 4,
|
|
841
|
+
},
|
|
842
|
+
});
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
### 高亮文字 (highlightText)
|
|
846
|
+
|
|
847
|
+
多种高亮效果:marker、underline、background、stroke、neon。
|
|
848
|
+
|
|
849
|
+
```javascript
|
|
850
|
+
ext_call({
|
|
851
|
+
plugin: 'poster',
|
|
852
|
+
tool: 'add_poster_highlight_text',
|
|
853
|
+
args: {
|
|
854
|
+
x: 300,
|
|
855
|
+
y: 400,
|
|
856
|
+
text: '重要通知',
|
|
857
|
+
fontSize: 60,
|
|
858
|
+
fontFamily: 'sans-serif',
|
|
859
|
+
color: '#1e293b',
|
|
860
|
+
highlightColor: '#fbbf24',
|
|
861
|
+
highlightStyle: 'marker', // marker, underline, background, stroke, neon
|
|
862
|
+
highlightWidth: 20,
|
|
863
|
+
strokeWidth: 3,
|
|
864
|
+
shadow: { color: '#fbbf24', blur: 15, offsetX: 2, offsetY: 2 },
|
|
865
|
+
},
|
|
866
|
+
});
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
**highlightStyle 可选值:**
|
|
870
|
+
|
|
871
|
+
- `marker` - 荧光笔效果(倾斜半透明矩形)
|
|
872
|
+
- `underline` - 下划线
|
|
873
|
+
- `background` - 背景色块
|
|
874
|
+
- `stroke` - 文字描边
|
|
875
|
+
- `neon` - 霓虹发光效果
|
|
876
|
+
|
|
877
|
+
### 条形码 (barcode)
|
|
878
|
+
|
|
879
|
+
生成条形码,支持简单条形码和 API 条形码。
|
|
880
|
+
|
|
881
|
+
```javascript
|
|
882
|
+
ext_call({
|
|
883
|
+
plugin: 'poster',
|
|
884
|
+
tool: 'add_poster_barcode',
|
|
885
|
+
args: {
|
|
886
|
+
x: 1500,
|
|
887
|
+
y: 900,
|
|
888
|
+
width: 300,
|
|
889
|
+
height: 100,
|
|
890
|
+
content: '123456789012',
|
|
891
|
+
showText: true,
|
|
892
|
+
textColor: '#1e293b',
|
|
893
|
+
fontSize: 16,
|
|
894
|
+
},
|
|
895
|
+
});
|
|
896
|
+
```
|
|
647
897
|
|
|
648
898
|
---
|
|
649
899
|
|
|
@@ -926,6 +1176,248 @@ ext_call({
|
|
|
926
1176
|
});
|
|
927
1177
|
```
|
|
928
1178
|
|
|
1179
|
+
### 示例 4:产品宣传海报(使用新增组件)
|
|
1180
|
+
|
|
1181
|
+
展示 button、icon、qrcode、frame、bubble、ribbon、seal、highlightText、barcode 等新组件。
|
|
1182
|
+
|
|
1183
|
+
```javascript
|
|
1184
|
+
// 1. 创建画布
|
|
1185
|
+
ext_call({
|
|
1186
|
+
plugin: 'poster',
|
|
1187
|
+
tool: 'create_poster_canvas',
|
|
1188
|
+
args: { width: 1200, height: 1600, background: '#f8fafc' },
|
|
1189
|
+
});
|
|
1190
|
+
|
|
1191
|
+
// 2. 装饰边框
|
|
1192
|
+
ext_call({
|
|
1193
|
+
plugin: 'poster',
|
|
1194
|
+
tool: 'add_poster_frame',
|
|
1195
|
+
args: {
|
|
1196
|
+
x: 40,
|
|
1197
|
+
y: 40,
|
|
1198
|
+
width: 1120,
|
|
1199
|
+
height: 1520,
|
|
1200
|
+
style: 'vintage',
|
|
1201
|
+
color: '#c9a227',
|
|
1202
|
+
borderWidth: 4,
|
|
1203
|
+
padding: 30,
|
|
1204
|
+
},
|
|
1205
|
+
});
|
|
1206
|
+
|
|
1207
|
+
// 3. 丝带标签
|
|
1208
|
+
ext_call({
|
|
1209
|
+
plugin: 'poster',
|
|
1210
|
+
tool: 'add_poster_ribbon',
|
|
1211
|
+
args: {
|
|
1212
|
+
x: 800,
|
|
1213
|
+
y: 80,
|
|
1214
|
+
width: 200,
|
|
1215
|
+
text: '新品上市',
|
|
1216
|
+
fontSize: 22,
|
|
1217
|
+
backgroundColor: '#e74c3c',
|
|
1218
|
+
style: 'fold',
|
|
1219
|
+
},
|
|
1220
|
+
});
|
|
1221
|
+
|
|
1222
|
+
// 4. 主标题(高亮文字)
|
|
1223
|
+
ext_call({
|
|
1224
|
+
plugin: 'poster',
|
|
1225
|
+
tool: 'add_poster_highlight_text',
|
|
1226
|
+
args: {
|
|
1227
|
+
x: 100,
|
|
1228
|
+
y: 200,
|
|
1229
|
+
text: '智能手表 Pro',
|
|
1230
|
+
fontSize: 72,
|
|
1231
|
+
fontFamily: 'sans-serif',
|
|
1232
|
+
color: '#1e293b',
|
|
1233
|
+
highlightColor: '#fbbf24',
|
|
1234
|
+
highlightStyle: 'marker',
|
|
1235
|
+
},
|
|
1236
|
+
});
|
|
1237
|
+
|
|
1238
|
+
// 5. 图标装饰
|
|
1239
|
+
ext_call({
|
|
1240
|
+
plugin: 'poster',
|
|
1241
|
+
tool: 'add_poster_icon',
|
|
1242
|
+
args: { x: 100, y: 280, size: 60, icon: '⌚', backgroundColor: '#1e293b', radius: 12 },
|
|
1243
|
+
});
|
|
1244
|
+
ext_call({
|
|
1245
|
+
plugin: 'poster',
|
|
1246
|
+
tool: 'add_poster_icon',
|
|
1247
|
+
args: { x: 180, y: 280, size: 60, icon: '🔋', backgroundColor: '#10b981', radius: 12 },
|
|
1248
|
+
});
|
|
1249
|
+
ext_call({
|
|
1250
|
+
plugin: 'poster',
|
|
1251
|
+
tool: 'add_poster_icon',
|
|
1252
|
+
args: { x: 260, y: 280, size: 60, icon: '💧', backgroundColor: '#3b82f6', radius: 12 },
|
|
1253
|
+
});
|
|
1254
|
+
|
|
1255
|
+
// 6. 对话气泡
|
|
1256
|
+
ext_call({
|
|
1257
|
+
plugin: 'poster',
|
|
1258
|
+
tool: 'add_poster_bubble',
|
|
1259
|
+
args: {
|
|
1260
|
+
x: 100,
|
|
1261
|
+
y: 400,
|
|
1262
|
+
width: 500,
|
|
1263
|
+
height: 100,
|
|
1264
|
+
text: '限时特惠,仅剩 3 天!',
|
|
1265
|
+
fontSize: 24,
|
|
1266
|
+
backgroundColor: '#fef3c7',
|
|
1267
|
+
borderColor: '#fbbf24',
|
|
1268
|
+
tailDirection: 'bottom',
|
|
1269
|
+
tailPosition: 'left',
|
|
1270
|
+
},
|
|
1271
|
+
});
|
|
1272
|
+
|
|
1273
|
+
// 7. 产品介绍文字
|
|
1274
|
+
ext_call({
|
|
1275
|
+
plugin: 'poster',
|
|
1276
|
+
tool: 'add_poster_text',
|
|
1277
|
+
args: {
|
|
1278
|
+
x: 100,
|
|
1279
|
+
y: 580,
|
|
1280
|
+
text: '全新智能手表,支持心率监测、GPS定位、防水50米。',
|
|
1281
|
+
fontSize: 28,
|
|
1282
|
+
color: '#64748b',
|
|
1283
|
+
},
|
|
1284
|
+
});
|
|
1285
|
+
|
|
1286
|
+
// 8. 印章认证
|
|
1287
|
+
ext_call({
|
|
1288
|
+
plugin: 'poster',
|
|
1289
|
+
tool: 'add_poster_seal',
|
|
1290
|
+
args: {
|
|
1291
|
+
x: 900,
|
|
1292
|
+
y: 500,
|
|
1293
|
+
size: 100,
|
|
1294
|
+
text: '正品',
|
|
1295
|
+
fontSize: 18,
|
|
1296
|
+
color: '#e74c3c',
|
|
1297
|
+
style: 'circle',
|
|
1298
|
+
},
|
|
1299
|
+
});
|
|
1300
|
+
|
|
1301
|
+
// 9. CTA 按钮
|
|
1302
|
+
ext_call({
|
|
1303
|
+
plugin: 'poster',
|
|
1304
|
+
tool: 'add_poster_button',
|
|
1305
|
+
args: {
|
|
1306
|
+
x: 350,
|
|
1307
|
+
y: 700,
|
|
1308
|
+
width: 500,
|
|
1309
|
+
height: 80,
|
|
1310
|
+
text: '立即购买 →',
|
|
1311
|
+
fontSize: 32,
|
|
1312
|
+
backgroundColor: '#1e293b',
|
|
1313
|
+
gradient: { colors: ['#1e293b', '#334155'] },
|
|
1314
|
+
radius: 40,
|
|
1315
|
+
shadow: { color: '#1e293b', blur: 20, offsetY: 8 },
|
|
1316
|
+
},
|
|
1317
|
+
});
|
|
1318
|
+
|
|
1319
|
+
// 10. 统计卡片
|
|
1320
|
+
ext_call({
|
|
1321
|
+
plugin: 'poster',
|
|
1322
|
+
tool: 'add_poster_stat_card',
|
|
1323
|
+
args: {
|
|
1324
|
+
x: 100,
|
|
1325
|
+
y: 850,
|
|
1326
|
+
title: '1999',
|
|
1327
|
+
subtitle: '原价',
|
|
1328
|
+
icon: '💰',
|
|
1329
|
+
color: '#94a3b8',
|
|
1330
|
+
background: '#f1f5f9',
|
|
1331
|
+
borderColor: '#e2e8f0',
|
|
1332
|
+
},
|
|
1333
|
+
});
|
|
1334
|
+
ext_call({
|
|
1335
|
+
plugin: 'poster',
|
|
1336
|
+
tool: 'add_poster_stat_card',
|
|
1337
|
+
args: {
|
|
1338
|
+
x: 350,
|
|
1339
|
+
y: 850,
|
|
1340
|
+
title: '1499',
|
|
1341
|
+
subtitle: '限时价',
|
|
1342
|
+
icon: '🔥',
|
|
1343
|
+
color: '#e74c3c',
|
|
1344
|
+
background: '#fef2f2',
|
|
1345
|
+
borderColor: '#fecaca',
|
|
1346
|
+
},
|
|
1347
|
+
});
|
|
1348
|
+
ext_call({
|
|
1349
|
+
plugin: 'poster',
|
|
1350
|
+
tool: 'add_poster_stat_card',
|
|
1351
|
+
args: {
|
|
1352
|
+
x: 600,
|
|
1353
|
+
y: 850,
|
|
1354
|
+
title: '4.9',
|
|
1355
|
+
subtitle: '评分',
|
|
1356
|
+
icon: '⭐',
|
|
1357
|
+
color: '#f59e0b',
|
|
1358
|
+
background: '#fffbeb',
|
|
1359
|
+
borderColor: '#fde68a',
|
|
1360
|
+
},
|
|
1361
|
+
});
|
|
1362
|
+
|
|
1363
|
+
// 11. 分隔线
|
|
1364
|
+
ext_call({
|
|
1365
|
+
plugin: 'poster',
|
|
1366
|
+
tool: 'add_poster_divider',
|
|
1367
|
+
args: { x: 100, y: 1050, width: 1000, color: '#e2e8f0', style: 'dashed' },
|
|
1368
|
+
});
|
|
1369
|
+
|
|
1370
|
+
// 12. 二维码
|
|
1371
|
+
ext_call({
|
|
1372
|
+
plugin: 'poster',
|
|
1373
|
+
tool: 'add_poster_qrcode',
|
|
1374
|
+
args: {
|
|
1375
|
+
x: 150,
|
|
1376
|
+
y: 1150,
|
|
1377
|
+
size: 180,
|
|
1378
|
+
content: 'https://example.com/shop/watch-pro',
|
|
1379
|
+
color: '#1e293b',
|
|
1380
|
+
backgroundColor: '#ffffff',
|
|
1381
|
+
},
|
|
1382
|
+
});
|
|
1383
|
+
|
|
1384
|
+
// 13. 条形码
|
|
1385
|
+
ext_call({
|
|
1386
|
+
plugin: 'poster',
|
|
1387
|
+
tool: 'add_poster_barcode',
|
|
1388
|
+
args: {
|
|
1389
|
+
x: 700,
|
|
1390
|
+
y: 1200,
|
|
1391
|
+
width: 300,
|
|
1392
|
+
height: 80,
|
|
1393
|
+
content: '6923456789012',
|
|
1394
|
+
showText: true,
|
|
1395
|
+
textColor: '#1e293b',
|
|
1396
|
+
},
|
|
1397
|
+
});
|
|
1398
|
+
|
|
1399
|
+
// 14. 页脚文字
|
|
1400
|
+
ext_call({
|
|
1401
|
+
plugin: 'poster',
|
|
1402
|
+
tool: 'add_poster_text',
|
|
1403
|
+
args: {
|
|
1404
|
+
x: 600,
|
|
1405
|
+
y: 1450,
|
|
1406
|
+
text: '扫描二维码访问官网',
|
|
1407
|
+
fontSize: 20,
|
|
1408
|
+
color: '#94a3b8',
|
|
1409
|
+
align: 'center',
|
|
1410
|
+
},
|
|
1411
|
+
});
|
|
1412
|
+
|
|
1413
|
+
// 15. 导出
|
|
1414
|
+
ext_call({
|
|
1415
|
+
plugin: 'poster',
|
|
1416
|
+
tool: 'export_poster_canvas',
|
|
1417
|
+
args: { filename: 'product-poster', format: 'png', outputDir: 'output' },
|
|
1418
|
+
});
|
|
1419
|
+
```
|
|
1420
|
+
|
|
929
1421
|
---
|
|
930
1422
|
|
|
931
1423
|
## 🔗 参考资源
|
package/src/core/agent-chat.js
CHANGED
|
@@ -1096,12 +1096,18 @@ ${truncatedContent}${truncatedNote}
|
|
|
1096
1096
|
return messages;
|
|
1097
1097
|
}
|
|
1098
1098
|
|
|
1099
|
-
// 2.
|
|
1099
|
+
// 2. 检查并移除不完整的 tool-call 和对应的 tool result
|
|
1100
|
+
const validated = this._validateToolCallsForPrepare(messages);
|
|
1101
|
+
if (validated !== messages) {
|
|
1102
|
+
return validated;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
// 3. 消息数量超过阈值才修剪
|
|
1100
1106
|
if (messages.length <= 50) {
|
|
1101
1107
|
return messages;
|
|
1102
1108
|
}
|
|
1103
1109
|
|
|
1104
|
-
//
|
|
1110
|
+
// 4. 保留配对完整的消息
|
|
1105
1111
|
const pruned = this._prepareMessagesForAI(messages);
|
|
1106
1112
|
return pruned;
|
|
1107
1113
|
} catch (err) {
|
|
@@ -1146,8 +1152,6 @@ ${truncatedContent}${truncatedNote}
|
|
|
1146
1152
|
stepCount: result.stepCount || 1,
|
|
1147
1153
|
};
|
|
1148
1154
|
} finally {
|
|
1149
|
-
// 校验并修复消息中的不完整工具调用
|
|
1150
|
-
this._validateToolCalls(messages);
|
|
1151
1155
|
// 确保保存聊天历史到 session(无论成功还是失败)
|
|
1152
1156
|
messageStore.save();
|
|
1153
1157
|
}
|
|
@@ -1207,12 +1211,18 @@ ${truncatedContent}${truncatedNote}
|
|
|
1207
1211
|
return messages;
|
|
1208
1212
|
}
|
|
1209
1213
|
|
|
1210
|
-
// 2.
|
|
1214
|
+
// 2. 检查并移除不完整的 tool-call 和对应的 tool result
|
|
1215
|
+
const validated = this._validateToolCallsForPrepare(messages);
|
|
1216
|
+
if (validated !== messages) {
|
|
1217
|
+
return validated;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
// 3. 消息数量超过阈值才修剪
|
|
1211
1221
|
if (messages.length <= 50) {
|
|
1212
1222
|
return messages;
|
|
1213
1223
|
}
|
|
1214
1224
|
|
|
1215
|
-
//
|
|
1225
|
+
// 4. 保留配对完整的消息
|
|
1216
1226
|
const pruned = this._prepareMessagesForAI(messages);
|
|
1217
1227
|
return pruned;
|
|
1218
1228
|
} catch (err) {
|
|
@@ -1269,8 +1279,6 @@ ${truncatedContent}${truncatedNote}
|
|
|
1269
1279
|
this.emit('error', { error: err.message });
|
|
1270
1280
|
yield { type: 'error', error: err.message };
|
|
1271
1281
|
} finally {
|
|
1272
|
-
// 校验并修复消息中的不完整工具调用
|
|
1273
|
-
this._validateToolCalls(messages);
|
|
1274
1282
|
// 确保保存聊天历史到 session(无论成功还是失败)
|
|
1275
1283
|
messageStore.save();
|
|
1276
1284
|
}
|
|
@@ -1462,6 +1470,19 @@ ${truncatedContent}${truncatedNote}
|
|
|
1462
1470
|
delete item.toolName;
|
|
1463
1471
|
delete item.input;
|
|
1464
1472
|
fixedCount++;
|
|
1473
|
+
} else {
|
|
1474
|
+
// 检查 JSON 是否能完整解析
|
|
1475
|
+
try {
|
|
1476
|
+
JSON.parse(trimmed);
|
|
1477
|
+
} catch (e) {
|
|
1478
|
+
// JSON 解析失败,说明是不完整的 JSON
|
|
1479
|
+
item.type = 'text';
|
|
1480
|
+
item.text = `(工具调用 ${item.toolName} 参数不完整(${e.message}),已跳过)`;
|
|
1481
|
+
delete item.toolCallId;
|
|
1482
|
+
delete item.toolName;
|
|
1483
|
+
delete item.input;
|
|
1484
|
+
fixedCount++;
|
|
1485
|
+
}
|
|
1465
1486
|
}
|
|
1466
1487
|
}
|
|
1467
1488
|
}
|
|
@@ -1471,6 +1492,118 @@ ${truncatedContent}${truncatedNote}
|
|
|
1471
1492
|
}
|
|
1472
1493
|
}
|
|
1473
1494
|
|
|
1495
|
+
/**
|
|
1496
|
+
* 在 prepareStep 中检查并移除不完整的 tool-call 和对应的 tool result
|
|
1497
|
+
* 返回修改后的消息数组(如果没修改则返回原数组)
|
|
1498
|
+
* @param {Array} messages - 消息列表
|
|
1499
|
+
* @returns {Array} 修改后的消息数组或原数组
|
|
1500
|
+
* @private
|
|
1501
|
+
*/
|
|
1502
|
+
_validateToolCallsForPrepare(messages) {
|
|
1503
|
+
// 收集不完整的 toolCallId
|
|
1504
|
+
const invalidToolCallIds = new Set();
|
|
1505
|
+
|
|
1506
|
+
for (const msg of messages) {
|
|
1507
|
+
if (msg.role !== 'assistant' || !Array.isArray(msg.content)) {
|
|
1508
|
+
continue;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
for (const item of msg.content) {
|
|
1512
|
+
if (item.type !== 'tool-call' || !item.toolCallId) {
|
|
1513
|
+
continue;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
const input = item.input;
|
|
1517
|
+
if (typeof input !== 'string') {
|
|
1518
|
+
continue;
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
// 检查 input 是否是有效的 JSON
|
|
1522
|
+
const trimmed = input.trim();
|
|
1523
|
+
let isInvalid = false;
|
|
1524
|
+
|
|
1525
|
+
if (trimmed === '{' || trimmed === '' || !trimmed.startsWith('{')) {
|
|
1526
|
+
isInvalid = true;
|
|
1527
|
+
} else {
|
|
1528
|
+
try {
|
|
1529
|
+
JSON.parse(trimmed);
|
|
1530
|
+
} catch (e) {
|
|
1531
|
+
isInvalid = true;
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
if (isInvalid) {
|
|
1536
|
+
invalidToolCallIds.add(item.toolCallId);
|
|
1537
|
+
logger.warn(`Found incomplete tool call: ${item.toolName} (${item.toolCallId})`);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
// 如果没有不完整的 tool-call,返回原数组
|
|
1543
|
+
if (invalidToolCallIds.size === 0) {
|
|
1544
|
+
return messages;
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
// 构建新的消息数组,移除不完整的 tool-call 和对应的 tool result
|
|
1548
|
+
const result = [];
|
|
1549
|
+
|
|
1550
|
+
for (const msg of messages) {
|
|
1551
|
+
if (msg.role === 'tool') {
|
|
1552
|
+
// 检查 tool result 是否对应不完整的 tool-call
|
|
1553
|
+
if (Array.isArray(msg.content)) {
|
|
1554
|
+
const newContent = [];
|
|
1555
|
+
for (const item of msg.content) {
|
|
1556
|
+
if (
|
|
1557
|
+
item.type === 'tool-result' &&
|
|
1558
|
+
item.toolCallId &&
|
|
1559
|
+
invalidToolCallIds.has(item.toolCallId)
|
|
1560
|
+
) {
|
|
1561
|
+
// 跳过这个 tool result
|
|
1562
|
+
continue;
|
|
1563
|
+
}
|
|
1564
|
+
newContent.push(item);
|
|
1565
|
+
}
|
|
1566
|
+
// 如果所有 content 都被移除了,跳过这条消息
|
|
1567
|
+
if (newContent.length === 0) {
|
|
1568
|
+
continue;
|
|
1569
|
+
}
|
|
1570
|
+
// 浅拷贝消息对象
|
|
1571
|
+
result.push({ ...msg, content: newContent });
|
|
1572
|
+
} else {
|
|
1573
|
+
result.push(msg);
|
|
1574
|
+
}
|
|
1575
|
+
} else if (msg.role === 'assistant') {
|
|
1576
|
+
// 检查并移除 assistant 消息中的不完整 tool-call
|
|
1577
|
+
if (Array.isArray(msg.content)) {
|
|
1578
|
+
const newContent = [];
|
|
1579
|
+
for (const item of msg.content) {
|
|
1580
|
+
if (
|
|
1581
|
+
item.type === 'tool-call' &&
|
|
1582
|
+
item.toolCallId &&
|
|
1583
|
+
invalidToolCallIds.has(item.toolCallId)
|
|
1584
|
+
) {
|
|
1585
|
+
// 跳过不完整的 tool-call
|
|
1586
|
+
continue;
|
|
1587
|
+
}
|
|
1588
|
+
newContent.push(item);
|
|
1589
|
+
}
|
|
1590
|
+
// 如果所有 content 都被移除了,跳过这条消息
|
|
1591
|
+
if (newContent.length === 0) {
|
|
1592
|
+
continue;
|
|
1593
|
+
}
|
|
1594
|
+
result.push({ ...msg, content: newContent });
|
|
1595
|
+
} else {
|
|
1596
|
+
result.push(msg);
|
|
1597
|
+
}
|
|
1598
|
+
} else {
|
|
1599
|
+
result.push(msg);
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
logger.info(`Removed ${invalidToolCallIds.size} incomplete tool calls and their results`);
|
|
1604
|
+
return result;
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1474
1607
|
/**
|
|
1475
1608
|
* 获取 AI SDK 格式的工具
|
|
1476
1609
|
* AI SDK 6.x 需要对象形式 { toolName: tool }
|