opencode-img 0.1.0 → 0.2.0
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 +64 -16
- package/dist/config.d.ts +4 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -15
- package/dist/config.js.map +1 -1
- package/dist/gemini-provider.d.ts +17 -0
- package/dist/gemini-provider.d.ts.map +1 -0
- package/dist/gemini-provider.js +130 -0
- package/dist/gemini-provider.js.map +1 -0
- package/dist/grok-provider.d.ts +17 -0
- package/dist/grok-provider.d.ts.map +1 -0
- package/dist/grok-provider.js +94 -0
- package/dist/grok-provider.js.map +1 -0
- package/dist/http.d.ts +23 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +55 -0
- package/dist/http.js.map +1 -0
- package/dist/image.d.ts +11 -2
- package/dist/image.d.ts.map +1 -1
- package/dist/image.js +42 -15
- package/dist/image.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -6
- package/dist/index.js.map +1 -1
- package/dist/openai-provider.d.ts +12 -7
- package/dist/openai-provider.d.ts.map +1 -1
- package/dist/openai-provider.js +34 -48
- package/dist/openai-provider.js.map +1 -1
- package/dist/provider.d.ts +9 -3
- package/dist/provider.d.ts.map +1 -1
- package/dist/providers.d.ts +32 -0
- package/dist/providers.d.ts.map +1 -0
- package/dist/providers.js +60 -0
- package/dist/providers.js.map +1 -0
- package/dist/tool.d.ts +9 -5
- package/dist/tool.d.ts.map +1 -1
- package/dist/tool.js +27 -31
- package/dist/tool.js.map +1 -1
- package/package.json +12 -9
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# opencode-img
|
|
2
2
|
|
|
3
|
-
An OpenCode V2 plugin that generates and edits bitmap images through
|
|
3
|
+
An OpenCode V2 plugin that generates and edits bitmap images through OpenAI, Gemini, and xAI image APIs. One tool, `gpt_imagegen`, takes a prompt and an output path, calls the API with your key, and writes one image to disk.
|
|
4
4
|
|
|
5
5
|
[](https://opencode.ai/v2/docs/build/plugins)
|
|
6
6
|
[](https://www.npmjs.com/package/opencode-img)
|
|
@@ -9,13 +9,13 @@ An OpenCode V2 plugin that generates and edits bitmap images through the OpenAI
|
|
|
9
9
|
|
|
10
10
|
Source: [github.com/mattsafaii/opencode-img](https://github.com/mattsafaii/opencode-img)
|
|
11
11
|
|
|
12
|
-
**Status:**
|
|
12
|
+
**Status:** early release.
|
|
13
13
|
|
|
14
14
|
## What it does
|
|
15
15
|
|
|
16
|
-
- **Generates and edits.** A prompt makes a new image; local `referenceImages` edit an existing one
|
|
16
|
+
- **Generates and edits.** A prompt makes a new image; local `referenceImages` edit an existing one.
|
|
17
|
+
- **Three providers.** `openai` (default), `gemini` (Nano Banana), and `grok` (Grok Imagine), selectable per call.
|
|
17
18
|
- **Never overwrites.** A taken output path gets the next version instead.
|
|
18
|
-
- **Your key, two ways.** The OpenCode connection from `/connect`, or `OPENAI_API_KEY`.
|
|
19
19
|
- **No SDK.** Native `fetch`, `FormData`, and `Blob` only.
|
|
20
20
|
|
|
21
21
|
## Install
|
|
@@ -35,16 +35,55 @@ Or add it to `opencode.json`:
|
|
|
35
35
|
}
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
##
|
|
38
|
+
## Plugin options
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
Set call defaults in `opencode.json` with the object form:
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
```jsonc
|
|
43
|
+
{
|
|
44
|
+
"$schema": "https://opencode.ai/config.json",
|
|
45
|
+
"plugins": [
|
|
46
|
+
{
|
|
47
|
+
"package": "opencode-img",
|
|
48
|
+
"options": {
|
|
49
|
+
"provider": "gemini",
|
|
50
|
+
"models": {
|
|
51
|
+
"openai": "gpt-image-1.5",
|
|
52
|
+
"gemini": "gemini-3.1-flash-image",
|
|
53
|
+
"grok": "grok-imagine-image-2.0"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- `provider` — used when a call does not name one. Defaults to `openai`.
|
|
62
|
+
- `models` — per-provider default model. Each provider falls back to its built-in default.
|
|
63
|
+
|
|
64
|
+
A call can still pass `provider` or `model` to override either.
|
|
65
|
+
|
|
66
|
+
## Providers
|
|
67
|
+
|
|
68
|
+
| `provider` | Default model | Key |
|
|
69
|
+
| --- | --- | --- |
|
|
70
|
+
| `openai` (default) | `gpt-image-1.5` | `OPENAI_API_KEY`, or the OpenAI connection from `/connect` |
|
|
71
|
+
| `gemini` | `gemini-3.1-flash-image` (Nano Banana 2) | `GEMINI_API_KEY` (or `GOOGLE_API_KEY`, `GOOGLE_GENERATIVE_AI_API_KEY`), or the Google connection from `/connect` |
|
|
72
|
+
| `grok` | `grok-imagine-image-2.0` (Grok Imagine) | `XAI_API_KEY`, or the xAI connection from `/connect` |
|
|
73
|
+
|
|
74
|
+
Pass `provider` to pick one.
|
|
75
|
+
|
|
76
|
+
## Add your API key
|
|
77
|
+
|
|
78
|
+
Set the environment variable for the provider you use, or connect it in OpenCode. When both are present, the environment variable wins.
|
|
79
|
+
|
|
80
|
+
- **OpenAI:** `OPENAI_API_KEY`, or `/connect` → OpenAI.
|
|
81
|
+
- **Gemini:** `GEMINI_API_KEY`, or `/connect` → Google.
|
|
82
|
+
- **Grok:** `XAI_API_KEY`, or `/connect` → xAI.
|
|
44
83
|
|
|
45
|
-
|
|
84
|
+
Use the environment variable for headless setups, or when your key lives in the OpenCode Console (BYOK), which plugins cannot read.
|
|
46
85
|
|
|
47
|
-
A key
|
|
86
|
+
A missing key fails before any network request, and the error names the provider's variables and integration.
|
|
48
87
|
|
|
49
88
|
## Use
|
|
50
89
|
|
|
@@ -60,14 +99,17 @@ Generate a 1024x1024 image of a red square on a white background and save it to
|
|
|
60
99
|
| --- | --- | --- |
|
|
61
100
|
| `prompt` | yes | What to generate, or the edit to make. |
|
|
62
101
|
| `outputPath` | yes | Where to save the image. Relative paths resolve against the session directory. |
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
102
|
+
| `provider` | no | `openai` (default), `gemini`, or `grok`. |
|
|
103
|
+
| `model` | no | Image model. Defaults to the provider's default. |
|
|
104
|
+
| `quality` | no | `auto`, `low`, `medium`, `high`, `standard`, `hd`, `xhigh`, or `max` (OpenAI); `auto`, `low`, or `medium` (Grok). |
|
|
105
|
+
| `size` | no | `WIDTHxHEIGHT` (for example `1024x1024`) or `auto` (OpenAI only). |
|
|
106
|
+
| `outputFormat` | no | `png`, `jpeg`, or `webp` (OpenAI); `png` or `jpeg` (Gemini). Inferred from the output path extension when omitted. |
|
|
107
|
+
| `referenceImages` | no | Local image paths. When present, the tool edits them. |
|
|
68
108
|
|
|
69
109
|
## Worked examples
|
|
70
110
|
|
|
111
|
+
Each image below is a real output of this plugin.
|
|
112
|
+
|
|
71
113
|
### Edit an existing image
|
|
72
114
|
|
|
73
115
|
Pass local paths in `referenceImages` and the tool edits them.
|
|
@@ -76,12 +118,16 @@ Pass local paths in `referenceImages` and the tool edits them.
|
|
|
76
118
|
Take assets/example-generate.png, change the coffee cup to a teacup, and add a spoon on the saucer. Save it to assets/example-edit.png.
|
|
77
119
|
```
|
|
78
120
|
|
|
121
|
+

|
|
122
|
+
|
|
79
123
|
### Generate from a prompt
|
|
80
124
|
|
|
81
125
|
```text
|
|
82
126
|
Generate a flat vector illustration of a steaming coffee cup on a saucer, two-tone, centered on a plain white background, and save it to assets/example-generate.png.
|
|
83
127
|
```
|
|
84
128
|
|
|
129
|
+

|
|
130
|
+
|
|
85
131
|
### Run the same path twice
|
|
86
132
|
|
|
87
133
|
The first file is left untouched; the new image lands at `example-generate-1.png`.
|
|
@@ -90,6 +136,8 @@ The first file is left untouched; the new image lands at `example-generate-1.png
|
|
|
90
136
|
Now do the same path, but a slice of cake on a plate instead.
|
|
91
137
|
```
|
|
92
138
|
|
|
139
|
+

|
|
140
|
+
|
|
93
141
|
## Output
|
|
94
142
|
|
|
95
143
|
- One image per request.
|
|
@@ -100,7 +148,7 @@ Now do the same path, but a slice of cake on a plate instead.
|
|
|
100
148
|
|
|
101
149
|
## Disclaimer
|
|
102
150
|
|
|
103
|
-
opencode-img is an independent project. It is not made by, affiliated with, or endorsed by
|
|
151
|
+
opencode-img is an independent project. It is not made by, affiliated with, or endorsed by any provider it supports or the OpenCode team. Calls to a provider's API are subject to that provider's terms and usage policies.
|
|
104
152
|
|
|
105
153
|
## Development
|
|
106
154
|
|
package/dist/config.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/** The OpenAI image model used when the caller does not name one. */
|
|
2
|
-
export declare const DEFAULT_MODEL = "gpt-image-1.5";
|
|
3
1
|
/** Quality values accepted by the OpenAI Images API. */
|
|
4
2
|
export declare const QUALITY_VALUES: readonly ["auto", "standard", "hd", "low", "medium", "high", "xhigh", "max"];
|
|
5
3
|
/** Output formats accepted by the GPT image models. */
|
|
@@ -26,18 +24,19 @@ export declare function isGptImageModel(model: string): boolean;
|
|
|
26
24
|
*/
|
|
27
25
|
export declare function outputFormatForExtension(filePath: string): OutputFormat | undefined;
|
|
28
26
|
/**
|
|
29
|
-
* Validate the optional generation settings and apply the
|
|
27
|
+
* Validate the optional generation settings and apply the provider's default
|
|
28
|
+
* model.
|
|
30
29
|
*
|
|
31
30
|
* Size is checked as a shape (`auto` or `WIDTHxHEIGHT`) rather than a fixed
|
|
32
31
|
* list so newer models that accept arbitrary resolutions keep working.
|
|
33
32
|
*/
|
|
34
|
-
export declare function resolveSettings(input: GenerationSettingsInput): GenerationSettings;
|
|
33
|
+
export declare function resolveSettings(input: GenerationSettingsInput, defaultModel: string): GenerationSettings;
|
|
35
34
|
/**
|
|
36
35
|
* Keep the encoded format and the output filename in agreement.
|
|
37
36
|
*
|
|
38
37
|
* An explicit `outputFormat` must match a known image extension on the path.
|
|
39
38
|
* When the format is omitted, a known extension selects it so the bytes never
|
|
40
|
-
* contradict the name.
|
|
39
|
+
* contradict the name.
|
|
41
40
|
*/
|
|
42
41
|
export declare function reconcileOutputFormat(settings: GenerationSettings, targetPath: string): GenerationSettings;
|
|
43
42
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,wDAAwD;AACxD,eAAO,MAAM,cAAc,8EASjB,CAAA;AAEV,uDAAuD;AACvD,eAAO,MAAM,oBAAoB,kCAAmC,CAAA;AAEpE,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AACrD,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEhE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,YAAY,CAAC,EAAE,YAAY,CAAA;CAC5B;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,qFAAqF;AACrF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAYnF;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,uBAAuB,EAC9B,YAAY,EAAE,MAAM,GACnB,kBAAkB,CAiCpB;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,kBAAkB,EAC5B,UAAU,EAAE,MAAM,GACjB,kBAAkB,CAcpB"}
|
package/dist/config.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { extname } from "node:path";
|
|
2
2
|
import { invalidArgument, requireNonEmptyString } from "./errors.js";
|
|
3
|
-
/** The OpenAI image model used when the caller does not name one. */
|
|
4
|
-
export const DEFAULT_MODEL = "gpt-image-1.5";
|
|
5
3
|
/** Quality values accepted by the OpenAI Images API. */
|
|
6
4
|
export const QUALITY_VALUES = [
|
|
7
5
|
"auto",
|
|
@@ -37,13 +35,14 @@ export function outputFormatForExtension(filePath) {
|
|
|
37
35
|
}
|
|
38
36
|
}
|
|
39
37
|
/**
|
|
40
|
-
* Validate the optional generation settings and apply the
|
|
38
|
+
* Validate the optional generation settings and apply the provider's default
|
|
39
|
+
* model.
|
|
41
40
|
*
|
|
42
41
|
* Size is checked as a shape (`auto` or `WIDTHxHEIGHT`) rather than a fixed
|
|
43
42
|
* list so newer models that accept arbitrary resolutions keep working.
|
|
44
43
|
*/
|
|
45
|
-
export function resolveSettings(input) {
|
|
46
|
-
const model = input.model === undefined ?
|
|
44
|
+
export function resolveSettings(input, defaultModel) {
|
|
45
|
+
const model = input.model === undefined ? defaultModel : requireNonEmptyString(input.model, "model");
|
|
47
46
|
const settings = { model };
|
|
48
47
|
if (input.quality !== undefined) {
|
|
49
48
|
const quality = requireNonEmptyString(input.quality, "quality");
|
|
@@ -64,9 +63,6 @@ export function resolveSettings(input) {
|
|
|
64
63
|
if (!OUTPUT_FORMAT_VALUES.includes(outputFormat)) {
|
|
65
64
|
throw invalidArgument(`\`outputFormat\` must be one of ${OUTPUT_FORMAT_VALUES.join(", ")}.`);
|
|
66
65
|
}
|
|
67
|
-
if (!isGptImageModel(model)) {
|
|
68
|
-
throw invalidArgument("`outputFormat` is only supported by the GPT image models; DALL·E models always return PNG.");
|
|
69
|
-
}
|
|
70
66
|
settings.outputFormat = outputFormat;
|
|
71
67
|
}
|
|
72
68
|
return settings;
|
|
@@ -76,7 +72,7 @@ export function resolveSettings(input) {
|
|
|
76
72
|
*
|
|
77
73
|
* An explicit `outputFormat` must match a known image extension on the path.
|
|
78
74
|
* When the format is omitted, a known extension selects it so the bytes never
|
|
79
|
-
* contradict the name.
|
|
75
|
+
* contradict the name.
|
|
80
76
|
*/
|
|
81
77
|
export function reconcileOutputFormat(settings, targetPath) {
|
|
82
78
|
const extensionFormat = outputFormatForExtension(targetPath);
|
|
@@ -88,12 +84,6 @@ export function reconcileOutputFormat(settings, targetPath) {
|
|
|
88
84
|
}
|
|
89
85
|
return settings;
|
|
90
86
|
}
|
|
91
|
-
if (!isGptImageModel(settings.model)) {
|
|
92
|
-
if (extensionFormat !== "png") {
|
|
93
|
-
throw invalidArgument(`DALL·E models always return PNG, so \`outputPath\` cannot end in \`.${extensionFormat}\`.`);
|
|
94
|
-
}
|
|
95
|
-
return settings;
|
|
96
|
-
}
|
|
97
87
|
return { ...settings, outputFormat: extensionFormat };
|
|
98
88
|
}
|
|
99
89
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAEpE,
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type GenerationSettings, type GenerationSettingsInput } from "./config.ts";
|
|
2
|
+
import type { ImageProvider, ImageProviderOptions } from "./provider.ts";
|
|
3
|
+
export declare const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
|
|
4
|
+
/** Nano Banana 2, the generalist Gemini image model. */
|
|
5
|
+
export declare const GEMINI_DEFAULT_MODEL = "gemini-3.1-flash-image";
|
|
6
|
+
/** Resolve the settings Gemini accepts. Quality, size, and webp do not apply. */
|
|
7
|
+
export declare function resolveGeminiSettings(input: GenerationSettingsInput, targetPath: string): GenerationSettings;
|
|
8
|
+
/**
|
|
9
|
+
* Create the Gemini (Nano Banana) adapter for the internal {@link ImageProvider}
|
|
10
|
+
* seam.
|
|
11
|
+
*
|
|
12
|
+
* Generation and editing use one endpoint. The prompt and every reference image
|
|
13
|
+
* travel together as content parts, and the generated image comes back in the
|
|
14
|
+
* response steps.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createGeminiImageProvider(options: ImageProviderOptions): ImageProvider;
|
|
17
|
+
//# sourceMappingURL=gemini-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini-provider.d.ts","sourceRoot":"","sources":["../src/gemini-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAE7B,MAAM,aAAa,CAAA;AAIpB,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAA6B,MAAM,eAAe,CAAA;AAEnG,eAAO,MAAM,gBAAgB,qDAAqD,CAAA;AAElF,wDAAwD;AACxD,eAAO,MAAM,oBAAoB,2BAA2B,CAAA;AAK5D,iFAAiF;AACjF,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,uBAAuB,EAC9B,UAAU,EAAE,MAAM,GACjB,kBAAkB,CAoBpB;AAqDD;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAgEtF"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { reconcileOutputFormat, resolveSettings, } from "./config.js";
|
|
2
|
+
import { ImageToolError, invalidArgument } from "./errors.js";
|
|
3
|
+
import { sendJsonRequest } from "./http.js";
|
|
4
|
+
import { decodeBase64Image, mimeTypeForOutputFormat } from "./image.js";
|
|
5
|
+
export const DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
|
|
6
|
+
/** Nano Banana 2, the generalist Gemini image model. */
|
|
7
|
+
export const GEMINI_DEFAULT_MODEL = "gemini-3.1-flash-image";
|
|
8
|
+
/** Output formats the Gemini image models can produce. */
|
|
9
|
+
const SUPPORTED_OUTPUT_FORMATS = ["png", "jpeg"];
|
|
10
|
+
/** Resolve the settings Gemini accepts. Quality, size, and webp do not apply. */
|
|
11
|
+
export function resolveGeminiSettings(input, targetPath) {
|
|
12
|
+
const settings = resolveSettings(input, GEMINI_DEFAULT_MODEL);
|
|
13
|
+
if (settings.quality !== undefined) {
|
|
14
|
+
throw invalidArgument("`quality` is not supported by the gemini provider.");
|
|
15
|
+
}
|
|
16
|
+
if (settings.size !== undefined) {
|
|
17
|
+
throw invalidArgument("`size` is not supported by the gemini provider; Gemini uses aspect ratios.");
|
|
18
|
+
}
|
|
19
|
+
const reconciled = reconcileOutputFormat(settings, targetPath);
|
|
20
|
+
if (reconciled.outputFormat !== undefined &&
|
|
21
|
+
!SUPPORTED_OUTPUT_FORMATS.includes(reconciled.outputFormat)) {
|
|
22
|
+
throw invalidArgument(`The gemini provider cannot produce ${reconciled.outputFormat}; use png or jpeg.`);
|
|
23
|
+
}
|
|
24
|
+
return reconciled;
|
|
25
|
+
}
|
|
26
|
+
function asRecord(value) {
|
|
27
|
+
return typeof value === "object" && value !== null
|
|
28
|
+
? value
|
|
29
|
+
: undefined;
|
|
30
|
+
}
|
|
31
|
+
function imageBlockFrom(value) {
|
|
32
|
+
const block = asRecord(value);
|
|
33
|
+
if (block === undefined)
|
|
34
|
+
return undefined;
|
|
35
|
+
if (block.type !== undefined && block.type !== "image")
|
|
36
|
+
return undefined;
|
|
37
|
+
const data = block.data;
|
|
38
|
+
if (typeof data !== "string" || data === "")
|
|
39
|
+
return undefined;
|
|
40
|
+
const mimeType = typeof block.mime_type === "string"
|
|
41
|
+
? block.mime_type
|
|
42
|
+
: typeof block.mimeType === "string"
|
|
43
|
+
? block.mimeType
|
|
44
|
+
: "image/png";
|
|
45
|
+
return { data, mimeType };
|
|
46
|
+
}
|
|
47
|
+
/** Find the last image block in an Interactions response. */
|
|
48
|
+
function findImage(payload) {
|
|
49
|
+
const root = asRecord(payload);
|
|
50
|
+
if (root === undefined)
|
|
51
|
+
return undefined;
|
|
52
|
+
const direct = imageBlockFrom(root.output_image ?? root.outputImage);
|
|
53
|
+
if (direct !== undefined)
|
|
54
|
+
return direct;
|
|
55
|
+
const steps = root.steps;
|
|
56
|
+
if (!Array.isArray(steps))
|
|
57
|
+
return undefined;
|
|
58
|
+
let found;
|
|
59
|
+
for (const step of steps) {
|
|
60
|
+
const content = asRecord(step)?.content;
|
|
61
|
+
if (!Array.isArray(content))
|
|
62
|
+
continue;
|
|
63
|
+
for (const block of content) {
|
|
64
|
+
const image = imageBlockFrom(block);
|
|
65
|
+
if (image !== undefined)
|
|
66
|
+
found = image;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return found;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Create the Gemini (Nano Banana) adapter for the internal {@link ImageProvider}
|
|
73
|
+
* seam.
|
|
74
|
+
*
|
|
75
|
+
* Generation and editing use one endpoint. The prompt and every reference image
|
|
76
|
+
* travel together as content parts, and the generated image comes back in the
|
|
77
|
+
* response steps.
|
|
78
|
+
*/
|
|
79
|
+
export function createGeminiImageProvider(options) {
|
|
80
|
+
const apiKey = options.apiKey;
|
|
81
|
+
const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
82
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
83
|
+
function buildBody(request) {
|
|
84
|
+
const input = [{ type: "text", text: request.prompt }];
|
|
85
|
+
for (const reference of request.references ?? []) {
|
|
86
|
+
input.push({
|
|
87
|
+
type: "image",
|
|
88
|
+
mime_type: reference.mimeType,
|
|
89
|
+
data: Buffer.from(reference.data).toString("base64"),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const body = { model: request.settings.model, input };
|
|
93
|
+
if (request.settings.outputFormat !== undefined) {
|
|
94
|
+
body.response_format = {
|
|
95
|
+
type: "image",
|
|
96
|
+
mime_type: mimeTypeForOutputFormat(request.settings.outputFormat),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return body;
|
|
100
|
+
}
|
|
101
|
+
async function send(request, operation, signal) {
|
|
102
|
+
const payload = await sendJsonRequest({
|
|
103
|
+
provider: "Gemini",
|
|
104
|
+
operation,
|
|
105
|
+
fetchImpl,
|
|
106
|
+
url: `${baseUrl}/interactions`,
|
|
107
|
+
init: {
|
|
108
|
+
method: "POST",
|
|
109
|
+
headers: {
|
|
110
|
+
"x-goog-api-key": apiKey,
|
|
111
|
+
"Content-Type": "application/json",
|
|
112
|
+
},
|
|
113
|
+
body: JSON.stringify(buildBody(request)),
|
|
114
|
+
},
|
|
115
|
+
signal,
|
|
116
|
+
});
|
|
117
|
+
const image = findImage(payload);
|
|
118
|
+
if (image === undefined) {
|
|
119
|
+
throw new ImageToolError("malformed_response", "decode image response", "The image response could not be read: the response contained no image.");
|
|
120
|
+
}
|
|
121
|
+
return { data: decodeBase64Image(image.data), mimeType: image.mimeType };
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
generate(request, signal) {
|
|
125
|
+
const hasReferences = (request.references?.length ?? 0) > 0;
|
|
126
|
+
return send(request, hasReferences ? "edit image" : "generate image", signal);
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=gemini-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini-provider.js","sourceRoot":"","sources":["../src/gemini-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type GenerationSettings, type GenerationSettingsInput } from "./config.ts";
|
|
2
|
+
import type { ImageProvider, ImageProviderOptions } from "./provider.ts";
|
|
3
|
+
export declare const DEFAULT_BASE_URL = "https://api.x.ai/v1";
|
|
4
|
+
/** Grok Imagine Image 2.0, the current xAI image model. */
|
|
5
|
+
export declare const GROK_DEFAULT_MODEL = "grok-imagine-image-2.0";
|
|
6
|
+
/** Resolve the settings xAI accepts. Size and output format do not apply. */
|
|
7
|
+
export declare function resolveGrokSettings(input: GenerationSettingsInput, _targetPath: string): GenerationSettings;
|
|
8
|
+
/**
|
|
9
|
+
* Create the Grok Imagine (xAI) adapter for the internal {@link ImageProvider}
|
|
10
|
+
* seam.
|
|
11
|
+
*
|
|
12
|
+
* Generation posts JSON to `/images/generations`. Editing posts JSON to
|
|
13
|
+
* `/images/edits` with the source images as data URIs; the endpoint does not
|
|
14
|
+
* accept multipart form data.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createGrokImageProvider(options: ImageProviderOptions): ImageProvider;
|
|
17
|
+
//# sourceMappingURL=grok-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grok-provider.d.ts","sourceRoot":"","sources":["../src/grok-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAE7B,MAAM,aAAa,CAAA;AAIpB,OAAO,KAAK,EACV,aAAa,EACb,oBAAoB,EAIrB,MAAM,eAAe,CAAA;AAEtB,eAAO,MAAM,gBAAgB,wBAAwB,CAAA;AAErD,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,2BAA2B,CAAA;AAK1D,6EAA6E;AAC7E,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,uBAAuB,EAC9B,WAAW,EAAE,MAAM,GAClB,kBAAkB,CAepB;AAMD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAiEpF"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { resolveSettings, } from "./config.js";
|
|
2
|
+
import { invalidArgument } from "./errors.js";
|
|
3
|
+
import { sendJsonRequest } from "./http.js";
|
|
4
|
+
import { decodeImageResponse, mimeTypeForImageBytes } from "./image.js";
|
|
5
|
+
export const DEFAULT_BASE_URL = "https://api.x.ai/v1";
|
|
6
|
+
/** Grok Imagine Image 2.0, the current xAI image model. */
|
|
7
|
+
export const GROK_DEFAULT_MODEL = "grok-imagine-image-2.0";
|
|
8
|
+
/** Quality values the xAI image model accepts. */
|
|
9
|
+
const GROK_QUALITY_VALUES = ["auto", "low", "medium"];
|
|
10
|
+
/** Resolve the settings xAI accepts. Size and output format do not apply. */
|
|
11
|
+
export function resolveGrokSettings(input, _targetPath) {
|
|
12
|
+
const settings = resolveSettings(input, GROK_DEFAULT_MODEL);
|
|
13
|
+
if (settings.size !== undefined) {
|
|
14
|
+
throw invalidArgument("`size` is not supported by the grok provider; xAI uses aspect ratios.");
|
|
15
|
+
}
|
|
16
|
+
if (settings.outputFormat !== undefined) {
|
|
17
|
+
throw invalidArgument("`outputFormat` is not supported by the grok provider.");
|
|
18
|
+
}
|
|
19
|
+
if (settings.quality !== undefined && !GROK_QUALITY_VALUES.includes(settings.quality)) {
|
|
20
|
+
throw invalidArgument(`The grok provider accepts quality ${GROK_QUALITY_VALUES.join(", ")}.`);
|
|
21
|
+
}
|
|
22
|
+
return settings;
|
|
23
|
+
}
|
|
24
|
+
function referenceDataUri(reference) {
|
|
25
|
+
return `data:${reference.mimeType};base64,${Buffer.from(reference.data).toString("base64")}`;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Create the Grok Imagine (xAI) adapter for the internal {@link ImageProvider}
|
|
29
|
+
* seam.
|
|
30
|
+
*
|
|
31
|
+
* Generation posts JSON to `/images/generations`. Editing posts JSON to
|
|
32
|
+
* `/images/edits` with the source images as data URIs; the endpoint does not
|
|
33
|
+
* accept multipart form data.
|
|
34
|
+
*/
|
|
35
|
+
export function createGrokImageProvider(options) {
|
|
36
|
+
const apiKey = options.apiKey;
|
|
37
|
+
const baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
38
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
39
|
+
function buildBody(request) {
|
|
40
|
+
const body = {
|
|
41
|
+
model: request.settings.model,
|
|
42
|
+
prompt: request.prompt,
|
|
43
|
+
n: 1,
|
|
44
|
+
response_format: "b64_json",
|
|
45
|
+
};
|
|
46
|
+
if (request.settings.quality !== undefined)
|
|
47
|
+
body.quality = request.settings.quality;
|
|
48
|
+
const references = request.references ?? [];
|
|
49
|
+
if (references.length === 1) {
|
|
50
|
+
body.image = { url: referenceDataUri(references[0]), type: "image_url" };
|
|
51
|
+
}
|
|
52
|
+
else if (references.length > 1) {
|
|
53
|
+
body.image = references.map((reference) => ({
|
|
54
|
+
url: referenceDataUri(reference),
|
|
55
|
+
type: "image_url",
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
return body;
|
|
59
|
+
}
|
|
60
|
+
async function send(request, operation, signal) {
|
|
61
|
+
const hasReferences = (request.references?.length ?? 0) > 0;
|
|
62
|
+
const url = hasReferences ? `${baseUrl}/images/edits` : `${baseUrl}/images/generations`;
|
|
63
|
+
const payload = await sendJsonRequest({
|
|
64
|
+
provider: "xAI",
|
|
65
|
+
operation,
|
|
66
|
+
fetchImpl,
|
|
67
|
+
url,
|
|
68
|
+
init: {
|
|
69
|
+
method: "POST",
|
|
70
|
+
headers: {
|
|
71
|
+
Authorization: `Bearer ${apiKey}`,
|
|
72
|
+
"Content-Type": "application/json",
|
|
73
|
+
},
|
|
74
|
+
body: JSON.stringify(buildBody(request)),
|
|
75
|
+
},
|
|
76
|
+
signal,
|
|
77
|
+
});
|
|
78
|
+
const decoded = decodeImageResponse(payload);
|
|
79
|
+
const result = {
|
|
80
|
+
data: decoded.data,
|
|
81
|
+
mimeType: mimeTypeForImageBytes(decoded.data),
|
|
82
|
+
};
|
|
83
|
+
if (decoded.revisedPrompt !== undefined)
|
|
84
|
+
result.revisedPrompt = decoded.revisedPrompt;
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
generate(request, signal) {
|
|
89
|
+
const hasReferences = (request.references?.length ?? 0) > 0;
|
|
90
|
+
return send(request, hasReferences ? "edit image" : "generate image", signal);
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=grok-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grok-provider.js","sourceRoot":"","sources":["../src/grok-provider.ts"],"names":[],"mappings":"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"}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ImageToolOperation } from "./errors.ts";
|
|
2
|
+
/** True when a fetch failure is an abort rather than a real error. */
|
|
3
|
+
export declare function isAbort(error: unknown, signal: AbortSignal | undefined): boolean;
|
|
4
|
+
/** A short, safe detail string pulled from an error response body. */
|
|
5
|
+
export declare function safeErrorDetail(body: string): string;
|
|
6
|
+
export interface JsonRequest {
|
|
7
|
+
/** Provider name used in error messages, for example `OpenAI`. */
|
|
8
|
+
provider: string;
|
|
9
|
+
operation: ImageToolOperation;
|
|
10
|
+
fetchImpl: typeof globalThis.fetch;
|
|
11
|
+
url: string;
|
|
12
|
+
init: RequestInit;
|
|
13
|
+
signal: AbortSignal | undefined;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Send a JSON request and return the parsed body.
|
|
17
|
+
*
|
|
18
|
+
* Shared by every adapter so the failure semantics stay identical: an abort is a
|
|
19
|
+
* cancellation, a non-OK status is an API error carrying the status and a
|
|
20
|
+
* bounded detail, and an unreadable body is a malformed response.
|
|
21
|
+
*/
|
|
22
|
+
export declare function sendJsonRequest(request: JsonRequest): Promise<unknown>;
|
|
23
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAIrE,sEAAsE;AACtE,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,OAAO,CAEhF;AAED,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAapD;AAED,MAAM,WAAW,WAAW;IAC1B,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,kBAAkB,CAAA;IAC7B,SAAS,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;IAClC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,WAAW,CAAA;IACjB,MAAM,EAAE,WAAW,GAAG,SAAS,CAAA;CAChC;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAqC5E"}
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ImageToolError } from "./errors.js";
|
|
2
|
+
const MAX_ERROR_DETAIL = 500;
|
|
3
|
+
/** True when a fetch failure is an abort rather than a real error. */
|
|
4
|
+
export function isAbort(error, signal) {
|
|
5
|
+
return (error instanceof Error && error.name === "AbortError") || signal?.aborted === true;
|
|
6
|
+
}
|
|
7
|
+
/** A short, safe detail string pulled from an error response body. */
|
|
8
|
+
export function safeErrorDetail(body) {
|
|
9
|
+
const trimmed = body.trim();
|
|
10
|
+
if (trimmed === "")
|
|
11
|
+
return "no response body";
|
|
12
|
+
try {
|
|
13
|
+
const parsed = JSON.parse(trimmed);
|
|
14
|
+
const message = parsed.error?.message;
|
|
15
|
+
if (typeof message === "string" && message !== "") {
|
|
16
|
+
return message.slice(0, MAX_ERROR_DETAIL);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// Not JSON; fall through to the raw text.
|
|
21
|
+
}
|
|
22
|
+
return trimmed.slice(0, MAX_ERROR_DETAIL);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Send a JSON request and return the parsed body.
|
|
26
|
+
*
|
|
27
|
+
* Shared by every adapter so the failure semantics stay identical: an abort is a
|
|
28
|
+
* cancellation, a non-OK status is an API error carrying the status and a
|
|
29
|
+
* bounded detail, and an unreadable body is a malformed response.
|
|
30
|
+
*/
|
|
31
|
+
export async function sendJsonRequest(request) {
|
|
32
|
+
let response;
|
|
33
|
+
try {
|
|
34
|
+
response = await request.fetchImpl(request.url, { ...request.init, signal: request.signal });
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (isAbort(error, request.signal)) {
|
|
38
|
+
throw new ImageToolError("cancelled", request.operation, "The image request was cancelled.", {
|
|
39
|
+
cause: error,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
throw new ImageToolError("api_error", request.operation, `Could not reach ${request.provider} while trying to ${request.operation}: ${error.message}.`, { cause: error });
|
|
43
|
+
}
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
const detail = safeErrorDetail(await response.text().catch(() => ""));
|
|
46
|
+
throw new ImageToolError("api_error", request.operation, `${request.provider} returned HTTP ${response.status} while trying to ${request.operation}: ${detail}`);
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
return await response.json();
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
throw new ImageToolError("malformed_response", request.operation, `${request.provider} returned a response body that was not valid JSON.`, { cause: error });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAA2B,MAAM,aAAa,CAAA;AAErE,MAAM,gBAAgB,GAAG,GAAG,CAAA;AAE5B,sEAAsE;AACtE,MAAM,UAAU,OAAO,CAAC,KAAc,EAAE,MAA+B;IACrE,OAAO,CAAC,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,CAAC,IAAI,MAAM,EAAE,OAAO,KAAK,IAAI,CAAA;AAC5F,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;IAC3B,IAAI,OAAO,KAAK,EAAE;QAAE,OAAO,kBAAkB,CAAA;IAC7C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAsC,CAAA;QACvE,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,EAAE,OAAO,CAAA;QACrC,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;YAClD,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAA;QAC3C,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,0CAA0C;IAC5C,CAAC;IACD,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAA;AAC3C,CAAC;AAYD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAoB;IACxD,IAAI,QAAkB,CAAA;IACtB,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IAC9F,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACnC,MAAM,IAAI,cAAc,CAAC,WAAW,EAAE,OAAO,CAAC,SAAS,EAAE,kCAAkC,EAAE;gBAC3F,KAAK,EAAE,KAAK;aACb,CAAC,CAAA;QACJ,CAAC;QACD,MAAM,IAAI,cAAc,CACtB,WAAW,EACX,OAAO,CAAC,SAAS,EACjB,mBAAmB,OAAO,CAAC,QAAQ,oBAAoB,OAAO,CAAC,SAAS,KAAM,KAAe,CAAC,OAAO,GAAG,EACxG,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAA;IACH,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;QACrE,MAAM,IAAI,cAAc,CACtB,WAAW,EACX,OAAO,CAAC,SAAS,EACjB,GAAG,OAAO,CAAC,QAAQ,kBAAkB,QAAQ,CAAC,MAAM,oBAAoB,OAAO,CAAC,SAAS,KAAK,MAAM,EAAE,CACvG,CAAA;IACH,CAAC;IAED,IAAI,CAAC;QACH,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;IAC9B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,cAAc,CACtB,oBAAoB,EACpB,OAAO,CAAC,SAAS,EACjB,GAAG,OAAO,CAAC,QAAQ,oDAAoD,EACvE,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAA;IACH,CAAC;AACH,CAAC"}
|