evomeme 0.1.7-beta.5 → 0.1.8
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/dist/canvas-public/app.js +149 -99
- package/dist/index.js +201 -96
- package/package.json +1 -1
- package/skills/evomeme-canvas/SKILL.md +52 -0
- package/skills/evomeme-canvas/agents/openai.yaml +7 -0
|
@@ -18,7 +18,8 @@ const state = {
|
|
|
18
18
|
videoDialogs: {}, // id -> video dialog state
|
|
19
19
|
fileInputDialogId: null,
|
|
20
20
|
mediaInputDialogId: null,
|
|
21
|
-
activeOperations: new Set(),
|
|
21
|
+
activeOperations: new Set(),
|
|
22
|
+
failedOperationIds: new Set(),
|
|
22
23
|
deleting: false,
|
|
23
24
|
refreshing: false,
|
|
24
25
|
externalSyncPending: false,
|
|
@@ -1161,8 +1162,11 @@ async function api(path, options) {
|
|
|
1161
1162
|
}
|
|
1162
1163
|
|
|
1163
1164
|
/** 画布只显示 layout 里的元素;磁盘上的孤儿文件不会自动冒出来 */
|
|
1164
|
-
function mergeLayout(_images, layoutItems) {
|
|
1165
|
-
return (layoutItems || []).filter((item) =>
|
|
1165
|
+
function mergeLayout(_images, layoutItems) {
|
|
1166
|
+
return (layoutItems || []).filter((item) =>
|
|
1167
|
+
item?.type !== 'sequence' &&
|
|
1168
|
+
!(isPending(item) && state.failedOperationIds.has(item.operationId || item.id))
|
|
1169
|
+
).map((item) => {
|
|
1166
1170
|
if (isFrameEditor(item)) {
|
|
1167
1171
|
return {
|
|
1168
1172
|
...item,
|
|
@@ -1710,15 +1714,14 @@ async function submitGenDialog(dialogId) {
|
|
|
1710
1714
|
ds.busy = true
|
|
1711
1715
|
state.error = ''
|
|
1712
1716
|
// 对话框立刻变成生成中图片节点
|
|
1713
|
-
state.layoutItems = state.layoutItems.map((it) => (it.id === dialogId ? pendingItem : it))
|
|
1714
|
-
setSelection([pendingId])
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
const result = await api('/api/generate', {
|
|
1717
|
+
state.layoutItems = state.layoutItems.map((it) => (it.id === dialogId ? pendingItem : it))
|
|
1718
|
+
setSelection([pendingId])
|
|
1719
|
+
const activeOperation = beginActiveOperation('image-generation')
|
|
1720
|
+
try {
|
|
1721
|
+
await saveLayout(state.layoutItems)
|
|
1722
|
+
renderCanvas()
|
|
1723
|
+
renderChrome()
|
|
1724
|
+
const result = await api('/api/generate', {
|
|
1722
1725
|
method: 'POST',
|
|
1723
1726
|
headers: { 'Content-Type': 'application/json' },
|
|
1724
1727
|
body: JSON.stringify({
|
|
@@ -1737,18 +1740,37 @@ async function submitGenDialog(dialogId) {
|
|
|
1737
1740
|
setSelection([result.id])
|
|
1738
1741
|
renderCanvas()
|
|
1739
1742
|
renderChrome()
|
|
1740
|
-
} catch (err) {
|
|
1741
|
-
const detail = String(err.message || err)
|
|
1742
|
-
state.error = detail
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1743
|
+
} catch (err) {
|
|
1744
|
+
const detail = String(err.message || err)
|
|
1745
|
+
state.error = detail
|
|
1746
|
+
ds.busy = false
|
|
1747
|
+
ds.prompt = text
|
|
1748
|
+
ds.messages.push({
|
|
1749
|
+
id: `e-${Date.now()}`,
|
|
1750
|
+
role: 'assistant',
|
|
1751
|
+
error: detail,
|
|
1752
|
+
})
|
|
1753
|
+
await discardFailedPendingOperation(pendingId)
|
|
1754
|
+
// Validation can fail before the server creates a job. In that case the
|
|
1755
|
+
// persisted pending node cannot be reconciled server-side, so replace it
|
|
1756
|
+
// explicitly with the original generator dialog.
|
|
1757
|
+
state.layoutItems = state.layoutItems.filter(
|
|
1758
|
+
(it) => it.id !== pendingId && it.id !== dialogId,
|
|
1759
|
+
)
|
|
1760
|
+
state.layoutItems.push({
|
|
1761
|
+
...dialog,
|
|
1762
|
+
id: dialogId,
|
|
1763
|
+
type: 'gen_dialog',
|
|
1764
|
+
name: dialog.name || '图片生成',
|
|
1765
|
+
width: dialog.width || GEN_DIALOG_W,
|
|
1766
|
+
height: null,
|
|
1767
|
+
})
|
|
1768
|
+
await saveLayout(state.layoutItems).catch((saveError) => {
|
|
1769
|
+
console.warn('保存图片生成失败回滚布局失败', saveError)
|
|
1770
|
+
})
|
|
1771
|
+
setSelection([dialogId])
|
|
1772
|
+
renderCanvas()
|
|
1773
|
+
renderChrome()
|
|
1752
1774
|
} finally {
|
|
1753
1775
|
endActiveOperation(activeOperation)
|
|
1754
1776
|
}
|
|
@@ -2005,15 +2027,14 @@ async function submitVideoGenDialog(dialogId) {
|
|
|
2005
2027
|
references,
|
|
2006
2028
|
}
|
|
2007
2029
|
: it,
|
|
2008
|
-
)
|
|
2009
|
-
setSelection([pendingId])
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
const seedRaw = String(ds.seed ?? '').trim()
|
|
2030
|
+
)
|
|
2031
|
+
setSelection([pendingId])
|
|
2032
|
+
const activeOperation = beginActiveOperation('video-generation')
|
|
2033
|
+
try {
|
|
2034
|
+
await saveLayout(state.layoutItems)
|
|
2035
|
+
renderCanvas()
|
|
2036
|
+
renderChrome()
|
|
2037
|
+
const seedRaw = String(ds.seed ?? '').trim()
|
|
2017
2038
|
const result = await api('/api/generate-video', {
|
|
2018
2039
|
method: 'POST',
|
|
2019
2040
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -2039,33 +2060,23 @@ async function submitVideoGenDialog(dialogId) {
|
|
|
2039
2060
|
state.error = detail
|
|
2040
2061
|
ds.busy = false
|
|
2041
2062
|
ds.prompt = text
|
|
2042
|
-
ds.messages.push({
|
|
2063
|
+
ds.messages.push({
|
|
2043
2064
|
id: `e-${Date.now()}`,
|
|
2044
2065
|
role: 'assistant',
|
|
2045
2066
|
error: detail,
|
|
2046
|
-
})
|
|
2047
|
-
await
|
|
2048
|
-
state.layoutItems.
|
|
2049
|
-
id
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
id: dialogId,
|
|
2060
|
-
type: 'video_dialog',
|
|
2061
|
-
name: '视频生成',
|
|
2062
|
-
x: it.x,
|
|
2063
|
-
y: it.y,
|
|
2064
|
-
width: VIDEO_DIALOG_W,
|
|
2065
|
-
height: null,
|
|
2066
|
-
}
|
|
2067
|
-
: it,
|
|
2068
|
-
)
|
|
2067
|
+
})
|
|
2068
|
+
await discardFailedPendingOperation(pendingId)
|
|
2069
|
+
state.layoutItems = state.layoutItems.filter(
|
|
2070
|
+
(it) => it.id !== pendingId && it.id !== dialogId,
|
|
2071
|
+
)
|
|
2072
|
+
state.layoutItems.push({
|
|
2073
|
+
...dialog,
|
|
2074
|
+
id: dialogId,
|
|
2075
|
+
type: 'video_dialog',
|
|
2076
|
+
name: dialog.name || '视频生成',
|
|
2077
|
+
width: VIDEO_DIALOG_W,
|
|
2078
|
+
height: null,
|
|
2079
|
+
})
|
|
2069
2080
|
await saveLayout(state.layoutItems)
|
|
2070
2081
|
setSelection([dialogId])
|
|
2071
2082
|
renderCanvas()
|
|
@@ -2510,10 +2521,10 @@ async function saveFrameEditorAsSheet(editorId) {
|
|
|
2510
2521
|
if (result.id) setSelection([result.id])
|
|
2511
2522
|
renderCanvas()
|
|
2512
2523
|
renderChrome()
|
|
2513
|
-
} catch (err) {
|
|
2514
|
-
state.error = String(err.message || err)
|
|
2515
|
-
await
|
|
2516
|
-
state.layoutItems = [
|
|
2524
|
+
} catch (err) {
|
|
2525
|
+
state.error = String(err.message || err)
|
|
2526
|
+
await discardFailedPendingOperation(operationId)
|
|
2527
|
+
state.layoutItems = [
|
|
2517
2528
|
...state.layoutItems.filter(
|
|
2518
2529
|
(candidate) => candidate.id !== operationId && candidate.id !== editorId,
|
|
2519
2530
|
),
|
|
@@ -3541,7 +3552,8 @@ function resetSessionState() {
|
|
|
3541
3552
|
state.videoDialogs = {}
|
|
3542
3553
|
state.fileInputDialogId = null
|
|
3543
3554
|
state.mediaInputDialogId = null
|
|
3544
|
-
state.activeOperations.clear()
|
|
3555
|
+
state.activeOperations.clear()
|
|
3556
|
+
state.failedOperationIds.clear()
|
|
3545
3557
|
state.deleting = false
|
|
3546
3558
|
state.error = ''
|
|
3547
3559
|
state.camera = { x: 0, y: 0, scale: 1 }
|
|
@@ -4701,8 +4713,9 @@ async function confirmVideoCycle() {
|
|
|
4701
4713
|
els.videoCycleStatus.textContent = '正在扫描周期…'
|
|
4702
4714
|
renderChrome()
|
|
4703
4715
|
|
|
4704
|
-
|
|
4705
|
-
|
|
4716
|
+
let operationId = ''
|
|
4717
|
+
try {
|
|
4718
|
+
operationId = await createProcessingPlaceholder(node, {
|
|
4706
4719
|
name: '循环提取中',
|
|
4707
4720
|
media: 'video',
|
|
4708
4721
|
references: [videoId],
|
|
@@ -4726,8 +4739,9 @@ async function confirmVideoCycle() {
|
|
|
4726
4739
|
closeVideoCycleModal(true)
|
|
4727
4740
|
renderCanvas()
|
|
4728
4741
|
renderChrome()
|
|
4729
|
-
} catch (err) {
|
|
4730
|
-
|
|
4742
|
+
} catch (err) {
|
|
4743
|
+
await discardFailedPendingOperation(operationId)
|
|
4744
|
+
if (err?.name === 'AbortError') {
|
|
4731
4745
|
els.videoCycleModal.hidden = true
|
|
4732
4746
|
state.videoCycleId = null
|
|
4733
4747
|
els.videoCycleStatus.hidden = true
|
|
@@ -4750,7 +4764,7 @@ async function confirmVideoCycle() {
|
|
|
4750
4764
|
}
|
|
4751
4765
|
}
|
|
4752
4766
|
|
|
4753
|
-
async function createProcessingPlaceholder(source, { name, media = 'image', references = [] } = {}) {
|
|
4767
|
+
async function createProcessingPlaceholder(source, { name, media = 'image', references = [] } = {}) {
|
|
4754
4768
|
const id = createCanvasClientId('pending')
|
|
4755
4769
|
const width = source?.width || (media === 'video' ? 280 : DEFAULT_W)
|
|
4756
4770
|
const item = {
|
|
@@ -4766,12 +4780,42 @@ async function createProcessingPlaceholder(source, { name, media = 'image', refe
|
|
|
4766
4780
|
id: referenceId,
|
|
4767
4781
|
role: 'source',
|
|
4768
4782
|
})),
|
|
4769
|
-
}
|
|
4770
|
-
state.layoutItems.push(item)
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4783
|
+
}
|
|
4784
|
+
state.layoutItems.push(item)
|
|
4785
|
+
try {
|
|
4786
|
+
await saveLayout(state.layoutItems)
|
|
4787
|
+
} catch (error) {
|
|
4788
|
+
state.layoutItems = state.layoutItems.filter((candidate) => candidate.id !== id)
|
|
4789
|
+
throw error
|
|
4790
|
+
}
|
|
4791
|
+
renderCanvas()
|
|
4792
|
+
return id
|
|
4793
|
+
}
|
|
4794
|
+
|
|
4795
|
+
async function discardFailedPendingOperation(operationId) {
|
|
4796
|
+
if (!operationId) return
|
|
4797
|
+
state.failedOperationIds.add(operationId)
|
|
4798
|
+
state.layoutItems = state.layoutItems.filter(
|
|
4799
|
+
(item) => !(isPending(item) && (item.id === operationId || item.operationId === operationId)),
|
|
4800
|
+
)
|
|
4801
|
+
state.selectedIds.delete(operationId)
|
|
4802
|
+
renderCanvas()
|
|
4803
|
+
renderChrome()
|
|
4804
|
+
|
|
4805
|
+
try {
|
|
4806
|
+
await api('/api/delete', {
|
|
4807
|
+
method: 'POST',
|
|
4808
|
+
headers: { 'Content-Type': 'application/json' },
|
|
4809
|
+
body: JSON.stringify({ layout_ids: [operationId], image_ids: [] }),
|
|
4810
|
+
})
|
|
4811
|
+
await reload().catch(() => undefined)
|
|
4812
|
+
} catch (error) {
|
|
4813
|
+
console.warn('后端清理失败的载入节点失败', error)
|
|
4814
|
+
await saveLayout(state.layoutItems).catch((error) => {
|
|
4815
|
+
console.warn('保存失败节点清理结果失败', error)
|
|
4816
|
+
})
|
|
4817
|
+
}
|
|
4818
|
+
}
|
|
4775
4819
|
|
|
4776
4820
|
const INTERACTIVE_EDITOR_TRANSLATION = {
|
|
4777
4821
|
name: 'zh-CN-canvas',
|
|
@@ -5248,10 +5292,10 @@ async function confirmInteractiveEdit() {
|
|
|
5248
5292
|
if (result.id) setSelection([result.id])
|
|
5249
5293
|
renderCanvas()
|
|
5250
5294
|
renderChrome()
|
|
5251
|
-
} catch (error) {
|
|
5252
|
-
state.error = String(error.message || error)
|
|
5253
|
-
await
|
|
5254
|
-
renderChrome()
|
|
5295
|
+
} catch (error) {
|
|
5296
|
+
state.error = String(error.message || error)
|
|
5297
|
+
await discardFailedPendingOperation(operationId)
|
|
5298
|
+
renderChrome()
|
|
5255
5299
|
} finally {
|
|
5256
5300
|
endActiveOperation(activeOperation)
|
|
5257
5301
|
renderChrome()
|
|
@@ -5339,10 +5383,10 @@ async function confirmLayerDecomposition() {
|
|
|
5339
5383
|
setSelection(images.map((image) => image.id).filter(Boolean))
|
|
5340
5384
|
renderCanvas()
|
|
5341
5385
|
renderChrome()
|
|
5342
|
-
} catch (err) {
|
|
5343
|
-
state.error = String(err.message || err)
|
|
5344
|
-
await
|
|
5345
|
-
renderChrome()
|
|
5386
|
+
} catch (err) {
|
|
5387
|
+
state.error = String(err.message || err)
|
|
5388
|
+
await discardFailedPendingOperation(operationId)
|
|
5389
|
+
renderChrome()
|
|
5346
5390
|
} finally {
|
|
5347
5391
|
endActiveOperation(activeOperation)
|
|
5348
5392
|
setLayerDecompositionControlsDisabled(false)
|
|
@@ -5405,8 +5449,9 @@ async function confirmFrameExtract() {
|
|
|
5405
5449
|
els.frameExtractStatus.textContent = `正在抽取 ${count} 帧并拼接 PNG…`
|
|
5406
5450
|
renderChrome()
|
|
5407
5451
|
|
|
5408
|
-
|
|
5409
|
-
|
|
5452
|
+
let operationId = ''
|
|
5453
|
+
try {
|
|
5454
|
+
operationId = await createProcessingPlaceholder(node, {
|
|
5410
5455
|
name: '视频序列帧图生成中',
|
|
5411
5456
|
references: [videoId],
|
|
5412
5457
|
})
|
|
@@ -5423,9 +5468,10 @@ async function confirmFrameExtract() {
|
|
|
5423
5468
|
state.frameExtractVideoId = null
|
|
5424
5469
|
renderCanvas()
|
|
5425
5470
|
renderChrome()
|
|
5426
|
-
} catch (err) {
|
|
5427
|
-
state.error = String(err.message || err)
|
|
5428
|
-
|
|
5471
|
+
} catch (err) {
|
|
5472
|
+
state.error = String(err.message || err)
|
|
5473
|
+
await discardFailedPendingOperation(operationId)
|
|
5474
|
+
els.frameExtractStatus.hidden = false
|
|
5429
5475
|
els.frameExtractStatus.textContent = state.error
|
|
5430
5476
|
renderChrome()
|
|
5431
5477
|
} finally {
|
|
@@ -5780,8 +5826,9 @@ async function runSmartSplit(imageId, anchor = 'center') {
|
|
|
5780
5826
|
els.smartSplitStatus.hidden = false
|
|
5781
5827
|
els.smartSplitStatus.textContent = '正在识别主体并统一尺寸…'
|
|
5782
5828
|
renderChrome()
|
|
5783
|
-
|
|
5784
|
-
|
|
5829
|
+
let operationId = ''
|
|
5830
|
+
try {
|
|
5831
|
+
operationId = await createProcessingPlaceholder(node, {
|
|
5785
5832
|
name: '智能切分中',
|
|
5786
5833
|
references: [imageId],
|
|
5787
5834
|
})
|
|
@@ -5800,9 +5847,10 @@ async function runSmartSplit(imageId, anchor = 'center') {
|
|
|
5800
5847
|
state.smartSplitImageId = null
|
|
5801
5848
|
renderCanvas()
|
|
5802
5849
|
renderChrome()
|
|
5803
|
-
} catch (err) {
|
|
5804
|
-
state.error = String(err.message || err)
|
|
5805
|
-
|
|
5850
|
+
} catch (err) {
|
|
5851
|
+
state.error = String(err.message || err)
|
|
5852
|
+
await discardFailedPendingOperation(operationId)
|
|
5853
|
+
els.smartSplitStatus.hidden = false
|
|
5806
5854
|
els.smartSplitStatus.textContent = state.error
|
|
5807
5855
|
renderChrome()
|
|
5808
5856
|
} finally {
|
|
@@ -5902,8 +5950,9 @@ async function confirmManualSplit() {
|
|
|
5902
5950
|
els.manualSplitCols.disabled = true
|
|
5903
5951
|
renderChrome()
|
|
5904
5952
|
|
|
5905
|
-
|
|
5906
|
-
|
|
5953
|
+
let operationId = ''
|
|
5954
|
+
try {
|
|
5955
|
+
operationId = await createProcessingPlaceholder(node, {
|
|
5907
5956
|
name: '手动切分中',
|
|
5908
5957
|
references: [imageId],
|
|
5909
5958
|
})
|
|
@@ -5927,9 +5976,10 @@ async function confirmManualSplit() {
|
|
|
5927
5976
|
setSelection(images.map((image) => image.id))
|
|
5928
5977
|
renderCanvas()
|
|
5929
5978
|
renderChrome()
|
|
5930
|
-
} catch (err) {
|
|
5931
|
-
state.error = String(err.message || err)
|
|
5932
|
-
|
|
5979
|
+
} catch (err) {
|
|
5980
|
+
state.error = String(err.message || err)
|
|
5981
|
+
await discardFailedPendingOperation(operationId)
|
|
5982
|
+
els.manualSplitStatus.hidden = false
|
|
5933
5983
|
els.manualSplitStatus.textContent = state.error
|
|
5934
5984
|
renderChrome()
|
|
5935
5985
|
} finally {
|
|
@@ -6039,9 +6089,9 @@ async function runCutout(imageId) {
|
|
|
6039
6089
|
})
|
|
6040
6090
|
await reload()
|
|
6041
6091
|
setSelection([result.id])
|
|
6042
|
-
} catch (err) {
|
|
6043
|
-
state.error = String(err.message || err)
|
|
6044
|
-
await
|
|
6092
|
+
} catch (err) {
|
|
6093
|
+
state.error = String(err.message || err)
|
|
6094
|
+
await discardFailedPendingOperation(pendingId)
|
|
6045
6095
|
} finally {
|
|
6046
6096
|
endActiveOperation(activeOperation)
|
|
6047
6097
|
renderCanvas()
|
package/dist/index.js
CHANGED
|
@@ -18267,7 +18267,7 @@ var StdioClientTransport = class {
|
|
|
18267
18267
|
};
|
|
18268
18268
|
|
|
18269
18269
|
// src/version.ts
|
|
18270
|
-
var VERSION = "0.1.
|
|
18270
|
+
var VERSION = "0.1.8";
|
|
18271
18271
|
|
|
18272
18272
|
// src/publishChannel.generated.ts
|
|
18273
18273
|
var PUBLISH_NPM_NAME = "evomeme";
|
|
@@ -19200,7 +19200,7 @@ var GAME_IMAGE_SIZES = [
|
|
|
19200
19200
|
"2496x1664",
|
|
19201
19201
|
"3024x1296"
|
|
19202
19202
|
];
|
|
19203
|
-
var GAME_IMAGE_MODELS = ["asset-pro", "asset-lite"];
|
|
19203
|
+
var GAME_IMAGE_MODELS = ["asset-pro", "asset-lite", "gpt-image-2"];
|
|
19204
19204
|
var IMAGE_JOB_STATUSES = [
|
|
19205
19205
|
"QUEUED",
|
|
19206
19206
|
"RUNNING",
|
|
@@ -19215,7 +19215,8 @@ var IMAGE_JOB_KINDS = [
|
|
|
19215
19215
|
];
|
|
19216
19216
|
var GAME_IMAGE_MODEL_BACKEND = {
|
|
19217
19217
|
"asset-pro": "doubao-seedream-5-0-pro-260628",
|
|
19218
|
-
"asset-lite": "doubao-seedream-5-0-260128"
|
|
19218
|
+
"asset-lite": "doubao-seedream-5-0-260128",
|
|
19219
|
+
"gpt-image-2": "gpt-image-2"
|
|
19219
19220
|
};
|
|
19220
19221
|
function resolveGameImageModel(model) {
|
|
19221
19222
|
return GAME_IMAGE_MODEL_BACKEND[model];
|
|
@@ -19264,7 +19265,7 @@ async function getImageJobStatus(token, jobId, options = {}) {
|
|
|
19264
19265
|
path: `/v1/tools/image/jobs/${encodeURIComponent(jobId)}`,
|
|
19265
19266
|
token
|
|
19266
19267
|
});
|
|
19267
|
-
if (typeof data.job_id !== "string" || data.job_id.length === 0 || !IMAGE_JOB_KINDS.includes(data.kind ?? "") || !IMAGE_JOB_STATUSES.includes(data.status ?? "") || typeof data.asset_url !== "string" || data.asset_url.length === 0 || typeof data.pending !== "boolean" || typeof data.attempts !== "number" || typeof data.max_attempts !== "number" || data.error_code !== void 0 && data.error_code !== null && typeof data.error_code !== "string" || data.error_stage !== void 0 && data.error_stage !== null && typeof data.error_stage !== "string" || typeof data.created_at !== "string" || typeof data.updated_at !== "string" || data.completed_at !== void 0 && data.completed_at !== null && typeof data.completed_at !== "string" || data.layers !== void 0 && !Array.isArray(data.layers)) {
|
|
19268
|
+
if (typeof data.job_id !== "string" || data.job_id.length === 0 || !IMAGE_JOB_KINDS.includes(data.kind ?? "") || !IMAGE_JOB_STATUSES.includes(data.status ?? "") || typeof data.asset_url !== "string" || data.asset_url.length === 0 || typeof data.pending !== "boolean" || typeof data.attempts !== "number" || typeof data.max_attempts !== "number" || data.error_code !== void 0 && data.error_code !== null && typeof data.error_code !== "string" || data.error_stage !== void 0 && data.error_stage !== null && typeof data.error_stage !== "string" || data.error_message !== void 0 && data.error_message !== null && typeof data.error_message !== "string" || typeof data.created_at !== "string" || typeof data.updated_at !== "string" || data.completed_at !== void 0 && data.completed_at !== null && typeof data.completed_at !== "string" || data.layers !== void 0 && !Array.isArray(data.layers)) {
|
|
19268
19269
|
throw incompatibleResponseError(JSON.stringify(data));
|
|
19269
19270
|
}
|
|
19270
19271
|
const layers = (data.layers ?? []).map((layer) => {
|
|
@@ -19290,6 +19291,7 @@ async function getImageJobStatus(token, jobId, options = {}) {
|
|
|
19290
19291
|
maxAttempts: data.max_attempts,
|
|
19291
19292
|
errorCode: data.error_code ?? void 0,
|
|
19292
19293
|
errorStage: data.error_stage ?? void 0,
|
|
19294
|
+
...data.error_message == null ? {} : { errorMessage: data.error_message },
|
|
19293
19295
|
createdAt: data.created_at,
|
|
19294
19296
|
updatedAt: data.updated_at,
|
|
19295
19297
|
completedAt: data.completed_at ?? void 0,
|
|
@@ -20575,6 +20577,7 @@ async function waitForImageJob(jobId, initialAssetUrl, deps = defaultImageServic
|
|
|
20575
20577
|
await hooks.onStatus?.({ ...status, assetUrl: lastAssetUrl });
|
|
20576
20578
|
if (status.status === "SUCCEEDED") return { ...status, assetUrl: lastAssetUrl };
|
|
20577
20579
|
if (status.status === "FAILED") {
|
|
20580
|
+
if (status.errorMessage) throw new Error(status.errorMessage);
|
|
20578
20581
|
throw new Error(
|
|
20579
20582
|
[
|
|
20580
20583
|
"图片任务失败。",
|
|
@@ -22469,6 +22472,8 @@ var MAX_JSON_BODY_BYTES = 80 * 1024 * 1024;
|
|
|
22469
22472
|
var INTERACTIVE_EDIT_MIN_PIXELS = 921600;
|
|
22470
22473
|
var INTERACTIVE_EDIT_MAX_PIXELS = 4624220;
|
|
22471
22474
|
var TRANSPARENT_BACKGROUND_REFERENCE_ERROR = "透明背景图功能要求有且只有一个PNG格式参考图输入";
|
|
22475
|
+
var CANVAS_QUOTA_ERROR = "当前账号的生成额度已用完,请充值或补充额度后重试";
|
|
22476
|
+
var CANVAS_NETWORK_ERROR = "网络连接失败,请重试";
|
|
22472
22477
|
var CANVAS_VENDOR_ASSETS = {
|
|
22473
22478
|
"/vendor/painterro.min.js": {
|
|
22474
22479
|
packageName: "painterro",
|
|
@@ -22512,7 +22517,7 @@ async function startCanvasServer(options = {}) {
|
|
|
22512
22517
|
} catch (error2) {
|
|
22513
22518
|
const status = statusFromError(error2);
|
|
22514
22519
|
if (!response.headersSent) {
|
|
22515
|
-
sendJson(response, status, { detail:
|
|
22520
|
+
sendJson(response, status, { detail: canvasGenerationErrorMessage(error2) });
|
|
22516
22521
|
} else {
|
|
22517
22522
|
response.end();
|
|
22518
22523
|
}
|
|
@@ -22977,8 +22982,9 @@ async function generateFromCanvas(workspaceDir, body, imageServiceDeps) {
|
|
|
22977
22982
|
tos_url: generated.assetUrl
|
|
22978
22983
|
};
|
|
22979
22984
|
} catch (error2) {
|
|
22980
|
-
|
|
22981
|
-
|
|
22985
|
+
const normalized = normalizeCanvasGenerationError(error2);
|
|
22986
|
+
await failCanvasJob(canvasJob.workspaceDir, canvasJob.operationId, normalized).catch(() => void 0);
|
|
22987
|
+
throw normalized;
|
|
22982
22988
|
}
|
|
22983
22989
|
}
|
|
22984
22990
|
async function generateVideoFromCanvas(workspaceDir, body, videoGenerationDeps) {
|
|
@@ -23124,8 +23130,9 @@ async function generateVideoFromCanvas(workspaceDir, body, videoGenerationDeps)
|
|
|
23124
23130
|
reference_ids: parentAssetIds
|
|
23125
23131
|
};
|
|
23126
23132
|
} catch (error2) {
|
|
23127
|
-
|
|
23128
|
-
|
|
23133
|
+
const normalized = normalizeCanvasGenerationError(error2);
|
|
23134
|
+
await failCanvasJob(canvasJob.workspaceDir, canvasJob.operationId, normalized).catch(() => void 0);
|
|
23135
|
+
throw normalized;
|
|
23129
23136
|
}
|
|
23130
23137
|
}
|
|
23131
23138
|
async function cutoutFromCanvas(workspaceDir, body) {
|
|
@@ -23184,8 +23191,9 @@ async function cutoutFromCanvas(workspaceDir, body) {
|
|
|
23184
23191
|
tos_url: result.assetUrl
|
|
23185
23192
|
};
|
|
23186
23193
|
} catch (error2) {
|
|
23187
|
-
|
|
23188
|
-
|
|
23194
|
+
const normalized = normalizeCanvasGenerationError(error2);
|
|
23195
|
+
await failCanvasJob(canvasJob.workspaceDir, canvasJob.operationId, normalized).catch(() => void 0);
|
|
23196
|
+
throw normalized;
|
|
23189
23197
|
}
|
|
23190
23198
|
}
|
|
23191
23199
|
async function ensureReferenceAssetUrl(workspaceDir, assetId) {
|
|
@@ -24080,6 +24088,68 @@ function statusFromError(error2) {
|
|
|
24080
24088
|
function errorMessage(error2) {
|
|
24081
24089
|
return error2 instanceof Error ? error2.message : String(error2);
|
|
24082
24090
|
}
|
|
24091
|
+
function canvasGenerationErrorMessage(error2) {
|
|
24092
|
+
const message = incompatibleResponseDetail(error2) ?? errorMessage(error2);
|
|
24093
|
+
const normalized = message.toLowerCase();
|
|
24094
|
+
if (/insufficient[_ -]?quota|quota (?:has been )?(?:exceeded|exhausted)|insufficient (?:balance|credit|funds)|credit balance (?:is )?too low/i.test(message) || /(?:额度(?:不足|已?用完|耗尽)|余额(?:不足|已?用完))/.test(message)) {
|
|
24095
|
+
return CANVAS_QUOTA_ERROR;
|
|
24096
|
+
}
|
|
24097
|
+
const code = String(error2?.code || "").toUpperCase();
|
|
24098
|
+
const name = error2 instanceof Error ? error2.name : "";
|
|
24099
|
+
if (name === "NetworkError" || ["ECONNABORTED", "ECONNREFUSED", "ECONNRESET", "EHOSTUNREACH", "ENETUNREACH", "ETIMEDOUT"].includes(code) || /fetch failed|network(?: request)? (?:error|failed)|socket hang up|connection (?:error|failed|refused|reset|timed out)|request timed out|connecttimeout|网络异常|连接失败/.test(normalized)) {
|
|
24100
|
+
return CANVAS_NETWORK_ERROR;
|
|
24101
|
+
}
|
|
24102
|
+
return message;
|
|
24103
|
+
}
|
|
24104
|
+
function incompatibleResponseDetail(error2) {
|
|
24105
|
+
if (!(error2 instanceof Error) || error2.name !== "IncompatibleResponseError") return void 0;
|
|
24106
|
+
const rawBody = error2.rawBody;
|
|
24107
|
+
if (typeof rawBody !== "string" || !rawBody.trim()) {
|
|
24108
|
+
return "服务端返回了无法解析的响应";
|
|
24109
|
+
}
|
|
24110
|
+
const raw = rawBody.trim();
|
|
24111
|
+
try {
|
|
24112
|
+
const parsed = JSON.parse(raw);
|
|
24113
|
+
const extracted = responseErrorMessage(parsed);
|
|
24114
|
+
if (extracted) return providerMessageFromText(extracted);
|
|
24115
|
+
} catch {
|
|
24116
|
+
}
|
|
24117
|
+
return providerMessageFromText(raw);
|
|
24118
|
+
}
|
|
24119
|
+
function responseErrorMessage(value) {
|
|
24120
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
24121
|
+
const record2 = value;
|
|
24122
|
+
if (typeof record2.error_message === "string" && record2.error_message.trim()) {
|
|
24123
|
+
return record2.error_message.trim();
|
|
24124
|
+
}
|
|
24125
|
+
if (record2.error && typeof record2.error === "object" && !Array.isArray(record2.error)) {
|
|
24126
|
+
const nested = responseErrorMessage(record2.error);
|
|
24127
|
+
if (nested) return nested;
|
|
24128
|
+
}
|
|
24129
|
+
if (record2.data && typeof record2.data === "object" && !Array.isArray(record2.data)) {
|
|
24130
|
+
const nested = responseErrorMessage(record2.data);
|
|
24131
|
+
if (nested) return nested;
|
|
24132
|
+
}
|
|
24133
|
+
if (typeof record2.detail === "string" && record2.detail.trim()) return record2.detail.trim();
|
|
24134
|
+
if (typeof record2.message === "string" && record2.message.trim() && record2.message !== "ok") {
|
|
24135
|
+
return record2.message.trim();
|
|
24136
|
+
}
|
|
24137
|
+
return void 0;
|
|
24138
|
+
}
|
|
24139
|
+
function providerMessageFromText(message) {
|
|
24140
|
+
const pythonPayloadMessage = message.match(
|
|
24141
|
+
/['"]message['"]\s*:\s*(['"])([\s\S]*?)\1\s*,\s*['"](?:type|param|code)['"]\s*:/
|
|
24142
|
+
);
|
|
24143
|
+
return pythonPayloadMessage?.[2]?.trim() || message;
|
|
24144
|
+
}
|
|
24145
|
+
function normalizeCanvasGenerationError(error2) {
|
|
24146
|
+
const message = canvasGenerationErrorMessage(error2);
|
|
24147
|
+
if (error2 instanceof Error && message === error2.message) return error2;
|
|
24148
|
+
const normalized = new Error(message);
|
|
24149
|
+
const status = error2?.status;
|
|
24150
|
+
if (typeof status === "number") Object.assign(normalized, { status });
|
|
24151
|
+
return normalized;
|
|
24152
|
+
}
|
|
24083
24153
|
|
|
24084
24154
|
// src/cli/canvas/command.ts
|
|
24085
24155
|
async function runCanvas(parsed) {
|
|
@@ -25432,7 +25502,7 @@ var generateGameImageTool = {
|
|
|
25432
25502
|
image_model: {
|
|
25433
25503
|
type: "string",
|
|
25434
25504
|
enum: [...GAME_IMAGE_MODELS],
|
|
25435
|
-
description: "生图模型,默认 asset-lite;可选 asset-pro。"
|
|
25505
|
+
description: "生图模型,默认 asset-lite;可选 asset-pro 或 gpt-image-2。"
|
|
25436
25506
|
},
|
|
25437
25507
|
workspace_dir: {
|
|
25438
25508
|
type: "string",
|
|
@@ -25747,90 +25817,6 @@ function canvasHooks2(workspaceDir, operationId, deps) {
|
|
|
25747
25817
|
};
|
|
25748
25818
|
}
|
|
25749
25819
|
|
|
25750
|
-
// src/mcp/server.ts
|
|
25751
|
-
function listMcpTools() {
|
|
25752
|
-
return [
|
|
25753
|
-
checkEvomemeLoginTool,
|
|
25754
|
-
loginEvomemeTool,
|
|
25755
|
-
submitGameCodeTool,
|
|
25756
|
-
uploadGameAssetTool,
|
|
25757
|
-
generateGameImageTool,
|
|
25758
|
-
cutoutGameImageTool
|
|
25759
|
-
// Video generation is temporarily offline for this release. Keep the
|
|
25760
|
-
// implementation in place so it can be restored in a later version.
|
|
25761
|
-
// generateGameVideoTool,
|
|
25762
|
-
// ...listMeshTools(),
|
|
25763
|
-
];
|
|
25764
|
-
}
|
|
25765
|
-
async function dispatchToolCall(name, rawArgs = {}) {
|
|
25766
|
-
if (name === CHECK_EVOMEME_LOGIN_TOOL_NAME) {
|
|
25767
|
-
return handleCheckEvomemeLogin();
|
|
25768
|
-
}
|
|
25769
|
-
if (name === LOGIN_EVOMEME_TOOL_NAME) {
|
|
25770
|
-
return handleLoginEvomeme();
|
|
25771
|
-
}
|
|
25772
|
-
if (name === SUBMIT_GAME_CODE_TOOL_NAME) {
|
|
25773
|
-
return handleSubmitGameCode(parseSubmitArgs(rawArgs));
|
|
25774
|
-
}
|
|
25775
|
-
if (name === UPLOAD_GAME_ASSET_TOOL_NAME) {
|
|
25776
|
-
return handleUploadGameAsset(parseUploadArgs(rawArgs));
|
|
25777
|
-
}
|
|
25778
|
-
if (name === GENERATE_GAME_IMAGE_TOOL_NAME) {
|
|
25779
|
-
return handleGenerateGameImage(parseGenerateImageArgs(rawArgs));
|
|
25780
|
-
}
|
|
25781
|
-
if (name === CUTOUT_GAME_IMAGE_TOOL_NAME) {
|
|
25782
|
-
return handleCutoutGameImage(parseCutoutImageArgs(rawArgs));
|
|
25783
|
-
}
|
|
25784
|
-
return toolError(new Error(`未知工具:${name}`));
|
|
25785
|
-
}
|
|
25786
|
-
function createMcpServer(options = {}) {
|
|
25787
|
-
const getUpdateNotice = options.getUpdateNotice ?? (async () => void 0);
|
|
25788
|
-
const server = new Server(
|
|
25789
|
-
{ name: "evomeme", version: VERSION },
|
|
25790
|
-
{ capabilities: { tools: { listChanged: true } } }
|
|
25791
|
-
);
|
|
25792
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: listMcpTools() }));
|
|
25793
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
25794
|
-
const args = request.params.arguments ?? {};
|
|
25795
|
-
const [result, updateNotice] = await Promise.all([
|
|
25796
|
-
dispatchToolCall(request.params.name, args),
|
|
25797
|
-
getUpdateNotice()
|
|
25798
|
-
]);
|
|
25799
|
-
return appendToolNotice(result, updateNotice);
|
|
25800
|
-
});
|
|
25801
|
-
return server;
|
|
25802
|
-
}
|
|
25803
|
-
function parseSubmitArgs(args) {
|
|
25804
|
-
return {
|
|
25805
|
-
workspace_dir: typeof args.workspace_dir === "string" ? args.workspace_dir : void 0,
|
|
25806
|
-
name: typeof args.name === "string" ? args.name : void 0,
|
|
25807
|
-
cover_url: typeof args.cover_url === "string" ? args.cover_url : void 0
|
|
25808
|
-
};
|
|
25809
|
-
}
|
|
25810
|
-
function parseUploadArgs(args) {
|
|
25811
|
-
return {
|
|
25812
|
-
local_path: typeof args.local_path === "string" ? args.local_path : "",
|
|
25813
|
-
folder: typeof args.folder === "string" ? args.folder : void 0,
|
|
25814
|
-
extension: typeof args.extension === "string" ? args.extension : void 0
|
|
25815
|
-
};
|
|
25816
|
-
}
|
|
25817
|
-
function parseGenerateImageArgs(args) {
|
|
25818
|
-
return {
|
|
25819
|
-
prompt: typeof args.prompt === "string" ? args.prompt : "",
|
|
25820
|
-
image_type: typeof args.image_type === "string" ? args.image_type : void 0,
|
|
25821
|
-
size: typeof args.size === "string" ? args.size : void 0,
|
|
25822
|
-
reference_image_url: typeof args.reference_image_url === "string" ? args.reference_image_url : Array.isArray(args.reference_image_url) ? args.reference_image_url.filter((value) => typeof value === "string") : void 0,
|
|
25823
|
-
image_model: typeof args.image_model === "string" ? args.image_model : void 0,
|
|
25824
|
-
workspace_dir: typeof args.workspace_dir === "string" ? args.workspace_dir : void 0
|
|
25825
|
-
};
|
|
25826
|
-
}
|
|
25827
|
-
function parseCutoutImageArgs(args) {
|
|
25828
|
-
return {
|
|
25829
|
-
image_url: typeof args.image_url === "string" ? args.image_url : "",
|
|
25830
|
-
workspace_dir: typeof args.workspace_dir === "string" ? args.workspace_dir : void 0
|
|
25831
|
-
};
|
|
25832
|
-
}
|
|
25833
|
-
|
|
25834
25820
|
// src/mcp/tools/generateGameVideo.ts
|
|
25835
25821
|
var GENERATE_GAME_VIDEO_TOOL_NAME = "evomeme_generate_game_video";
|
|
25836
25822
|
var GENERATE_GAME_VIDEO_DESCRIPTION = [
|
|
@@ -25892,6 +25878,21 @@ var defaultDeps9 = {
|
|
|
25892
25878
|
fail: failCanvasJob
|
|
25893
25879
|
}
|
|
25894
25880
|
};
|
|
25881
|
+
async function handleGenerateGameVideo(args, deps = defaultDeps9) {
|
|
25882
|
+
if (deps.backgroundTaskRunner) {
|
|
25883
|
+
const workspaceDir = args.workspace_dir?.trim() || deps.cwd();
|
|
25884
|
+
try {
|
|
25885
|
+
return await deps.backgroundTaskRunner(
|
|
25886
|
+
"generate_game_video",
|
|
25887
|
+
{ ...args, workspace_dir: workspaceDir },
|
|
25888
|
+
workspaceDir
|
|
25889
|
+
);
|
|
25890
|
+
} catch (error2) {
|
|
25891
|
+
return toolError(error2);
|
|
25892
|
+
}
|
|
25893
|
+
}
|
|
25894
|
+
return executeGenerateGameVideo(args, deps);
|
|
25895
|
+
}
|
|
25895
25896
|
async function executeGenerateGameVideo(args, deps = defaultDeps9) {
|
|
25896
25897
|
let canvasJob;
|
|
25897
25898
|
try {
|
|
@@ -26038,6 +26039,110 @@ function buildVideoInput(args) {
|
|
|
26038
26039
|
return input;
|
|
26039
26040
|
}
|
|
26040
26041
|
|
|
26042
|
+
// src/mcp/server.ts
|
|
26043
|
+
function listMcpTools() {
|
|
26044
|
+
return [
|
|
26045
|
+
checkEvomemeLoginTool,
|
|
26046
|
+
loginEvomemeTool,
|
|
26047
|
+
submitGameCodeTool,
|
|
26048
|
+
uploadGameAssetTool,
|
|
26049
|
+
generateGameImageTool,
|
|
26050
|
+
cutoutGameImageTool,
|
|
26051
|
+
generateGameVideoTool
|
|
26052
|
+
// ...listMeshTools(),
|
|
26053
|
+
];
|
|
26054
|
+
}
|
|
26055
|
+
async function dispatchToolCall(name, rawArgs = {}) {
|
|
26056
|
+
if (name === CHECK_EVOMEME_LOGIN_TOOL_NAME) {
|
|
26057
|
+
return handleCheckEvomemeLogin();
|
|
26058
|
+
}
|
|
26059
|
+
if (name === LOGIN_EVOMEME_TOOL_NAME) {
|
|
26060
|
+
return handleLoginEvomeme();
|
|
26061
|
+
}
|
|
26062
|
+
if (name === SUBMIT_GAME_CODE_TOOL_NAME) {
|
|
26063
|
+
return handleSubmitGameCode(parseSubmitArgs(rawArgs));
|
|
26064
|
+
}
|
|
26065
|
+
if (name === UPLOAD_GAME_ASSET_TOOL_NAME) {
|
|
26066
|
+
return handleUploadGameAsset(parseUploadArgs(rawArgs));
|
|
26067
|
+
}
|
|
26068
|
+
if (name === GENERATE_GAME_IMAGE_TOOL_NAME) {
|
|
26069
|
+
return handleGenerateGameImage(parseGenerateImageArgs(rawArgs));
|
|
26070
|
+
}
|
|
26071
|
+
if (name === CUTOUT_GAME_IMAGE_TOOL_NAME) {
|
|
26072
|
+
return handleCutoutGameImage(parseCutoutImageArgs(rawArgs));
|
|
26073
|
+
}
|
|
26074
|
+
if (name === GENERATE_GAME_VIDEO_TOOL_NAME) {
|
|
26075
|
+
return handleGenerateGameVideo(parseGenerateVideoArgs(rawArgs));
|
|
26076
|
+
}
|
|
26077
|
+
return toolError(new Error(`未知工具:${name}`));
|
|
26078
|
+
}
|
|
26079
|
+
function createMcpServer(options = {}) {
|
|
26080
|
+
const getUpdateNotice = options.getUpdateNotice ?? (async () => void 0);
|
|
26081
|
+
const server = new Server(
|
|
26082
|
+
{ name: "evomeme", version: VERSION },
|
|
26083
|
+
{ capabilities: { tools: { listChanged: true } } }
|
|
26084
|
+
);
|
|
26085
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: listMcpTools() }));
|
|
26086
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
26087
|
+
const args = request.params.arguments ?? {};
|
|
26088
|
+
const [result, updateNotice] = await Promise.all([
|
|
26089
|
+
dispatchToolCall(request.params.name, args),
|
|
26090
|
+
getUpdateNotice()
|
|
26091
|
+
]);
|
|
26092
|
+
return appendToolNotice(result, updateNotice);
|
|
26093
|
+
});
|
|
26094
|
+
return server;
|
|
26095
|
+
}
|
|
26096
|
+
function parseSubmitArgs(args) {
|
|
26097
|
+
return {
|
|
26098
|
+
workspace_dir: typeof args.workspace_dir === "string" ? args.workspace_dir : void 0,
|
|
26099
|
+
name: typeof args.name === "string" ? args.name : void 0,
|
|
26100
|
+
cover_url: typeof args.cover_url === "string" ? args.cover_url : void 0
|
|
26101
|
+
};
|
|
26102
|
+
}
|
|
26103
|
+
function parseUploadArgs(args) {
|
|
26104
|
+
return {
|
|
26105
|
+
local_path: typeof args.local_path === "string" ? args.local_path : "",
|
|
26106
|
+
folder: typeof args.folder === "string" ? args.folder : void 0,
|
|
26107
|
+
extension: typeof args.extension === "string" ? args.extension : void 0
|
|
26108
|
+
};
|
|
26109
|
+
}
|
|
26110
|
+
function parseGenerateImageArgs(args) {
|
|
26111
|
+
return {
|
|
26112
|
+
prompt: typeof args.prompt === "string" ? args.prompt : "",
|
|
26113
|
+
image_type: typeof args.image_type === "string" ? args.image_type : void 0,
|
|
26114
|
+
size: typeof args.size === "string" ? args.size : void 0,
|
|
26115
|
+
reference_image_url: typeof args.reference_image_url === "string" ? args.reference_image_url : Array.isArray(args.reference_image_url) ? args.reference_image_url.filter((value) => typeof value === "string") : void 0,
|
|
26116
|
+
image_model: typeof args.image_model === "string" ? args.image_model : void 0,
|
|
26117
|
+
workspace_dir: typeof args.workspace_dir === "string" ? args.workspace_dir : void 0
|
|
26118
|
+
};
|
|
26119
|
+
}
|
|
26120
|
+
function parseCutoutImageArgs(args) {
|
|
26121
|
+
return {
|
|
26122
|
+
image_url: typeof args.image_url === "string" ? args.image_url : "",
|
|
26123
|
+
workspace_dir: typeof args.workspace_dir === "string" ? args.workspace_dir : void 0
|
|
26124
|
+
};
|
|
26125
|
+
}
|
|
26126
|
+
function parseGenerateVideoArgs(args) {
|
|
26127
|
+
const references = Array.isArray(args.references) ? args.references.filter((value) => Boolean(value && typeof value === "object")).map((value) => ({
|
|
26128
|
+
type: typeof value.type === "string" ? value.type : void 0,
|
|
26129
|
+
url: typeof value.url === "string" ? value.url : void 0,
|
|
26130
|
+
role: typeof value.role === "string" ? value.role : void 0
|
|
26131
|
+
})) : void 0;
|
|
26132
|
+
return {
|
|
26133
|
+
prompt: typeof args.prompt === "string" ? args.prompt : "",
|
|
26134
|
+
ratio: typeof args.ratio === "string" ? args.ratio : void 0,
|
|
26135
|
+
duration: typeof args.duration === "number" ? args.duration : void 0,
|
|
26136
|
+
resolution: typeof args.resolution === "string" ? args.resolution : void 0,
|
|
26137
|
+
generate_audio: typeof args.generate_audio === "boolean" ? args.generate_audio : void 0,
|
|
26138
|
+
watermark: typeof args.watermark === "boolean" ? args.watermark : void 0,
|
|
26139
|
+
seed: typeof args.seed === "number" ? args.seed : void 0,
|
|
26140
|
+
video_model: typeof args.video_model === "string" ? args.video_model : void 0,
|
|
26141
|
+
references,
|
|
26142
|
+
workspace_dir: typeof args.workspace_dir === "string" ? args.workspace_dir : void 0
|
|
26143
|
+
};
|
|
26144
|
+
}
|
|
26145
|
+
|
|
26041
26146
|
// src/mcp/index.ts
|
|
26042
26147
|
async function startServer() {
|
|
26043
26148
|
const server = createMcpServer({ getUpdateNotice: getMcpUpdateNotice });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "evomeme",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "evomemeplugin_client - local stdio MCP server. Install: npx -y evomeme@latest install --ide cursor. Init: npx -y evomeme@latest init.",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: evomeme-canvas
|
|
3
|
+
description: 在生成或编辑任何游戏素材前,必须确保当前工作区的 evomeme素材画布在用户可见,并把画布链接告知用户。当任务涉及生图、图片编辑、抠图、图层分离、视频生成或其他 evomeme素材工具时使用。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# evomeme素材画布
|
|
7
|
+
|
|
8
|
+
在调用任何素材生成或编辑工具前,先完成本技能的画布启动流程。画布必须对应当前任务的工作区。
|
|
9
|
+
|
|
10
|
+
## 启动前检查
|
|
11
|
+
|
|
12
|
+
只根据当前对话记录判断一个画布服务是否可复用,不得仅凭端口已占用或 `/api/health` 响应正常推断服务属于当前工作区。
|
|
13
|
+
|
|
14
|
+
- 当前对话中明确记录了由本次对话为同一工作区启动的前台画布终端和地址时,访问 `<画布地址>/api/health`;响应正常则复用,地址未知、健康检查失败或终端已关闭则重新启动。
|
|
15
|
+
- 当前对话中不存在画布启动记录时,即使发现已有 evomeme画布服务也不得复用。该服务可能属于其他工作区;先关闭已有服务,再按照下方流程为当前工作区启动新的前台服务。
|
|
16
|
+
- 当前对话记录中的画布属于其他工作区时,同样先关闭该服务,再为当前工作区重新启动。
|
|
17
|
+
|
|
18
|
+
关闭已有服务时只关闭能够确认是 evomeme画布的进程,不得终止身份不明的端口占用进程。
|
|
19
|
+
|
|
20
|
+
## 前台启动
|
|
21
|
+
|
|
22
|
+
1. 在当前工作区根目录打开一个用户可见的独立交互式终端或命令框。
|
|
23
|
+
2. 在该终端以前台方式执行且只执行:
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
npx -y evomeme@latest canvas
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
3. 保持这个终端打开。命令必须持续占用该终端,用户必须能够在其中按 `Ctrl+C`,或直接关闭终端窗口来停止服务。
|
|
30
|
+
4. 等待终端输出“素材画布已启动”和“画布地址”。启动失败时,先处理或向用户说明错误,不得继续调用素材生成工具。
|
|
31
|
+
|
|
32
|
+
不得将命令作为隐藏、分离或后台进程运行。不得使用 `&`、`nohup`、`start /B`、`Start-Job`、隐藏窗口、服务管理器或其他后台启动方式,也不要添加 `--no-open`。如果当前执行环境不能提供用户可见且可自行中断的终端,请让用户在可见终端中执行上述命令;确认启动输出或健康检查通过后再继续。
|
|
33
|
+
|
|
34
|
+
画布终端保持前台运行不妨碍使用另一个终端或其他工具继续工作;不要为了继续生成而关闭画布终端。
|
|
35
|
+
|
|
36
|
+
## 告知并引导用户
|
|
37
|
+
|
|
38
|
+
取得终端实际输出的地址后,在调用第一个素材工具前立即向用户发送可点击链接,并说明:
|
|
39
|
+
|
|
40
|
+
- 保持画布终端打开;在终端中`Ctrl+C` 或关闭该终端会停止服务。
|
|
41
|
+
- 在画布中可以实时查看当前工作区的素材、生成任务状态和生成结果。
|
|
42
|
+
- 用户可以直接在画布中选择参考图、生成图片或继续编辑;AI 也会继续使用当前工作区的素材工具,结果会同步显示在画布中。
|
|
43
|
+
|
|
44
|
+
使用实际地址,不要猜测端口。可以采用下面的简短表达:
|
|
45
|
+
|
|
46
|
+
```markdown
|
|
47
|
+
素材画布已启动:[打开 evomeme素材画布](<实际地址>)。请保持运行画布的命令行窗口打开;你可以在画布中监控任务,也可以直接选择参考图并使用生图或编辑功能。在命令行中按 Ctrl+C 或关闭该命令行即可停止服务。
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 生成期间
|
|
51
|
+
|
|
52
|
+
确认画布可用并告知用户后,继续执行已经获得授权的素材任务,无需仅为打开画布再次请求确认。后续生成只能复用当前对话明确记录的同一工作区健康画布;如果服务中途停止,在下一次素材工具调用前重新完成启动流程。
|