gpteam 0.1.19 → 0.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/help.js +1 -1
- package/lib/image-mcp/image.js +95 -16
- package/lib/image-mcp/server.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ The Image MCP exposes both a synchronous compatibility tool and a local async jo
|
|
|
27
27
|
|
|
28
28
|
Image MCP results are returned as stable JSON text and MCP `structuredContent`. Successful results include final file path, model, action, size, format, quality, byte size, SHA-256, MIME type, image dimensions, duration, retry count, `job_id`, `trace_id`, and optional `idempotency_key`. Error results use stable `error.code`, `error.message`, `error.retryable`, `error.stage`, `error.upstream_status`, and `error.trace_id` fields while keeping compatibility fields such as `category` and `http_status`.
|
|
29
29
|
|
|
30
|
-
The MCP supports normal text-to-image generation and image-to-image/edit inputs. Pass `images` as data URLs, HTTPS URLs, or local file paths. Pass `mask` the same way for masked edits
|
|
30
|
+
The MCP supports normal text-to-image generation and image-to-image/edit inputs. Pass `images` as data URLs, HTTPS URLs, or local file paths. Pass `mask` the same way for masked edits. `input_fidelity` is accepted for compatibility but is not forwarded to the current GPTeam Image 2 bridge because upstream Codex image edits reject it. The MCP requests GPTeam image endpoints with `stream=true` and reads the final image event, so long image jobs get early stream bytes instead of sitting idle until the final JSON body. File writes create missing directories, avoid overwriting existing files by adding `-v2`, `-v3`, etc., and validate PNG/JPEG/WebP before returning success. `overwrite: true` is available for explicit replacement. `return_revised_prompt` controls whether the upstream revised prompt is included in the result.
|
|
31
31
|
|
|
32
32
|
Claude Code is written to `~/.claude/settings.json` under the `env` section, using the GPTeam `/anthropic` base URL. OpenClaw writes `models.providers.gpteam` and also selects `gpteam/<model>` under `agents.defaults.model`, so the chosen model is active without an extra manual step.
|
|
33
33
|
|
package/lib/help.js
CHANGED
package/lib/image-mcp/image.js
CHANGED
|
@@ -29,6 +29,7 @@ export function buildImageGenerationPayload(input = {}, options = {}) {
|
|
|
29
29
|
model: String(input.model || DEFAULT_IMAGE_MODEL),
|
|
30
30
|
prompt: String(input.prompt || '').trim(),
|
|
31
31
|
response_format: 'b64_json',
|
|
32
|
+
stream: true,
|
|
32
33
|
size: String(input.size || '1024x1024'),
|
|
33
34
|
quality: String(input.quality || 'high'),
|
|
34
35
|
output_format: normalizeImageFormat(input.format || input.output_format || DEFAULT_IMAGE_FORMAT)
|
|
@@ -40,7 +41,6 @@ export function buildImageGenerationPayload(input = {}, options = {}) {
|
|
|
40
41
|
if (mask) payload.mask = { image_url: mask };
|
|
41
42
|
copyOptionalImageToolOption(payload, input, 'background');
|
|
42
43
|
copyOptionalImageToolOption(payload, input, 'moderation');
|
|
43
|
-
copyOptionalImageToolOption(payload, input, 'input_fidelity');
|
|
44
44
|
return payload;
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -331,21 +331,8 @@ async function fetchImageOnce(fetchImpl, credentials, payload, options) {
|
|
|
331
331
|
signal: requestSignal.signal
|
|
332
332
|
});
|
|
333
333
|
if (!response.ok) throw await imageErrorFromHTTPResponse(response, credentials.apiKey);
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
const b64 = first && typeof first.b64_json === 'string' ? first.b64_json : '';
|
|
337
|
-
if (!b64) {
|
|
338
|
-
throw new ImageMCPError('GPTeam 图片接口没有返回 b64_json 图片数据。', {
|
|
339
|
-
code: 'image_data_missing',
|
|
340
|
-
category: 'response_invalid',
|
|
341
|
-
stage: 'local',
|
|
342
|
-
retryable: false
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
return {
|
|
346
|
-
b64,
|
|
347
|
-
revisedPrompt: first && typeof first.revised_prompt === 'string' ? first.revised_prompt : ''
|
|
348
|
-
};
|
|
334
|
+
if (isEventStreamResponse(response)) return await readImageSSE(response);
|
|
335
|
+
return await readImageJSON(response);
|
|
349
336
|
} catch (error) {
|
|
350
337
|
if (error instanceof ImageMCPError) throw error;
|
|
351
338
|
throw imageErrorFromFetch(error, {
|
|
@@ -358,6 +345,98 @@ async function fetchImageOnce(fetchImpl, credentials, payload, options) {
|
|
|
358
345
|
}
|
|
359
346
|
}
|
|
360
347
|
|
|
348
|
+
async function readImageJSON(response) {
|
|
349
|
+
const data = await response.json();
|
|
350
|
+
const first = Array.isArray(data && data.data) ? data.data[0] : null;
|
|
351
|
+
return imageResultFromItem(first);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async function readImageSSE(response) {
|
|
355
|
+
const text = await response.text();
|
|
356
|
+
let lastError = null;
|
|
357
|
+
for (const event of parseSSEDataEvents(text)) {
|
|
358
|
+
if (!event || event === '[DONE]') continue;
|
|
359
|
+
let payload;
|
|
360
|
+
try {
|
|
361
|
+
payload = JSON.parse(event);
|
|
362
|
+
} catch {
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
if (payload && payload.error) {
|
|
366
|
+
lastError = imageErrorFromSSEPayload(payload);
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
const item = imageResultFromStreamPayload(payload);
|
|
370
|
+
if (item) return item;
|
|
371
|
+
}
|
|
372
|
+
if (lastError) throw lastError;
|
|
373
|
+
throw missingImageDataError();
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function parseSSEDataEvents(text) {
|
|
377
|
+
const events = [];
|
|
378
|
+
let current = [];
|
|
379
|
+
for (const rawLine of String(text || '').split(/\r?\n/)) {
|
|
380
|
+
if (rawLine === '') {
|
|
381
|
+
if (current.length > 0) events.push(current.join('\n'));
|
|
382
|
+
current = [];
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
if (rawLine.startsWith('data:')) current.push(rawLine.slice(5).trimStart());
|
|
386
|
+
}
|
|
387
|
+
if (current.length > 0) events.push(current.join('\n'));
|
|
388
|
+
return events;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function imageResultFromStreamPayload(payload) {
|
|
392
|
+
const type = String(payload && payload.type || '');
|
|
393
|
+
if (!type.endsWith('.completed')) return null;
|
|
394
|
+
return imageResultFromItem(payload);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function imageResultFromItem(first) {
|
|
398
|
+
const b64 = first && typeof first.b64_json === 'string' ? first.b64_json : imageDataURLToB64(first && first.url);
|
|
399
|
+
if (!b64) throw missingImageDataError();
|
|
400
|
+
return {
|
|
401
|
+
b64,
|
|
402
|
+
revisedPrompt: first && typeof first.revised_prompt === 'string' ? first.revised_prompt : ''
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function imageDataURLToB64(value) {
|
|
407
|
+
const text = String(value || '').trim();
|
|
408
|
+
const match = text.match(/^data:image\/[a-z0-9.+-]+;base64,(.+)$/i);
|
|
409
|
+
return match ? match[1] : '';
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function missingImageDataError() {
|
|
413
|
+
return new ImageMCPError('GPTeam 图片接口没有返回 b64_json 图片数据。', {
|
|
414
|
+
code: 'image_data_missing',
|
|
415
|
+
category: 'response_invalid',
|
|
416
|
+
stage: 'local',
|
|
417
|
+
retryable: false
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function imageErrorFromSSEPayload(payload) {
|
|
422
|
+
const error = payload && payload.error;
|
|
423
|
+
const message = typeof error === 'string' ? error : String(error && error.message || 'GPTeam 图片流返回错误。');
|
|
424
|
+
const code = typeof error === 'object' && error ? String(error.code || payload.code || 'upstream_stream_error') : String(payload.code || 'upstream_stream_error');
|
|
425
|
+
return new ImageMCPError(message, {
|
|
426
|
+
code,
|
|
427
|
+
category: 'upstream',
|
|
428
|
+
stage: 'upstream',
|
|
429
|
+
retryable: code === 'rate_limit_exceeded' || code === 'server_error'
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function isEventStreamResponse(response) {
|
|
434
|
+
const contentType = response && response.headers && typeof response.headers.get === 'function'
|
|
435
|
+
? String(response.headers.get('content-type') || '')
|
|
436
|
+
: '';
|
|
437
|
+
return /text\/event-stream/i.test(contentType);
|
|
438
|
+
}
|
|
439
|
+
|
|
361
440
|
function buildSuccessResult(input) {
|
|
362
441
|
const result = {
|
|
363
442
|
ok: true,
|
package/lib/image-mcp/server.js
CHANGED
|
@@ -59,7 +59,7 @@ const imageInputProperties = {
|
|
|
59
59
|
},
|
|
60
60
|
input_fidelity: {
|
|
61
61
|
type: 'string',
|
|
62
|
-
description: '
|
|
62
|
+
description: 'Accepted for compatibility. The GPTeam Image 2 bridge currently ignores this option because the upstream Codex image tool rejects it on edits.',
|
|
63
63
|
enum: ['low', 'high']
|
|
64
64
|
},
|
|
65
65
|
return_revised_prompt: {
|