imgx-cli 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/README.md +24 -10
- package/dist/cli.bundle.js +12 -3
- package/dist/mcp.bundle.js +34 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.1 (2026-02-27)
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Default output directory changed from process cwd to `~/Pictures/imgx` — fixes images being saved to AppData when used via MCP (Claude Desktop, etc.)
|
|
8
|
+
|
|
9
|
+
## 0.6.0 (2026-02-27)
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **OpenAI provider** — `gpt-image-1` model with generate and edit support
|
|
14
|
+
- Native `fetch` implementation (no `openai` npm dependency)
|
|
15
|
+
- Aspect ratio mapping to OpenAI size strings, resolution mapping to quality parameter
|
|
16
|
+
- Manual multipart/form-data construction for Node 18 compatibility
|
|
17
|
+
- `OPENAI_API_KEY` environment variable support
|
|
18
|
+
- `--provider` flag for `imgx config set api-key` — manage API keys per provider
|
|
19
|
+
- `imgx config set api-key <key> --provider openai`
|
|
20
|
+
- `imgx config list` now shows all configured provider keys
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- CLI and MCP server now initialize both Gemini and OpenAI providers at startup
|
|
25
|
+
- `imgx providers` and error messages updated for multi-provider context
|
|
26
|
+
- Help text updated with OpenAI provider info and env var
|
|
27
|
+
|
|
3
28
|
## 0.5.2 (2026-02-26)
|
|
4
29
|
|
|
5
30
|
### Fixed
|
package/README.md
CHANGED
|
@@ -48,16 +48,25 @@ Requires Node.js 18+.
|
|
|
48
48
|
|
|
49
49
|
## Setup
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
Set up at least one provider:
|
|
52
|
+
|
|
53
|
+
**Gemini** — get a key from [Google AI Studio](https://aistudio.google.com/apikey) (free tier available):
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
imgx config set api-key YOUR_GEMINI_API_KEY --provider gemini
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**OpenAI** — get a key from [OpenAI Platform](https://platform.openai.com/api-keys):
|
|
52
60
|
|
|
53
61
|
```bash
|
|
54
|
-
imgx config set api-key
|
|
62
|
+
imgx config set api-key YOUR_OPENAI_API_KEY --provider openai
|
|
55
63
|
```
|
|
56
64
|
|
|
57
|
-
|
|
65
|
+
Keys are stored in `~/.config/imgx/config.json` (Linux/macOS) or `%APPDATA%\imgx\config.json` (Windows). Alternatively, set environment variables:
|
|
58
66
|
|
|
59
67
|
```bash
|
|
60
68
|
export GEMINI_API_KEY="your-api-key"
|
|
69
|
+
export OPENAI_API_KEY="your-api-key"
|
|
61
70
|
```
|
|
62
71
|
|
|
63
72
|
Environment variables take precedence over the config file.
|
|
@@ -106,7 +115,8 @@ imgx edit --last -p "Crop to 16:9" -o final.png
|
|
|
106
115
|
### Configuration
|
|
107
116
|
|
|
108
117
|
```bash
|
|
109
|
-
imgx config set api-key <key>
|
|
118
|
+
imgx config set api-key <key> --provider gemini # Save Gemini API key
|
|
119
|
+
imgx config set api-key <key> --provider openai # Save OpenAI API key
|
|
110
120
|
imgx config set model <name> # Set default model
|
|
111
121
|
imgx config set output-dir <dir> # Set default output directory
|
|
112
122
|
imgx config set aspect-ratio 16:9 # Set default aspect ratio
|
|
@@ -162,7 +172,8 @@ Environment variables override config file settings.
|
|
|
162
172
|
|
|
163
173
|
| Variable | Description |
|
|
164
174
|
|----------|-------------|
|
|
165
|
-
| `GEMINI_API_KEY` | Gemini API key
|
|
175
|
+
| `GEMINI_API_KEY` | Gemini API key |
|
|
176
|
+
| `OPENAI_API_KEY` | OpenAI API key |
|
|
166
177
|
| `IMGX_PROVIDER` | Default provider |
|
|
167
178
|
| `IMGX_MODEL` | Default model |
|
|
168
179
|
| `IMGX_OUTPUT_DIR` | Default output directory |
|
|
@@ -206,7 +217,7 @@ Add to your tool's MCP config. The `env` section is optional if you have already
|
|
|
206
217
|
"imgx": {
|
|
207
218
|
"command": "npx",
|
|
208
219
|
"args": ["--package=imgx-cli", "-y", "imgx-mcp"],
|
|
209
|
-
"env": { "GEMINI_API_KEY": "your-
|
|
220
|
+
"env": { "GEMINI_API_KEY": "your-key", "OPENAI_API_KEY": "your-key" }
|
|
210
221
|
}
|
|
211
222
|
}
|
|
212
223
|
}
|
|
@@ -224,7 +235,7 @@ Or install as a [Claude Code plugin](#install) for automatic MCP registration.
|
|
|
224
235
|
"imgx": {
|
|
225
236
|
"command": "npx",
|
|
226
237
|
"args": ["--package=imgx-cli", "-y", "imgx-mcp"],
|
|
227
|
-
"env": { "GEMINI_API_KEY": "your-
|
|
238
|
+
"env": { "GEMINI_API_KEY": "your-key", "OPENAI_API_KEY": "your-key" }
|
|
228
239
|
}
|
|
229
240
|
}
|
|
230
241
|
}
|
|
@@ -240,7 +251,7 @@ macOS / Linux:
|
|
|
240
251
|
"imgx": {
|
|
241
252
|
"command": "npx",
|
|
242
253
|
"args": ["--package=imgx-cli", "-y", "imgx-mcp"],
|
|
243
|
-
"env": { "GEMINI_API_KEY": "your-
|
|
254
|
+
"env": { "GEMINI_API_KEY": "your-key", "OPENAI_API_KEY": "your-key" }
|
|
244
255
|
}
|
|
245
256
|
}
|
|
246
257
|
}
|
|
@@ -254,7 +265,7 @@ Windows:
|
|
|
254
265
|
"imgx": {
|
|
255
266
|
"command": "cmd",
|
|
256
267
|
"args": ["/c", "npx", "--package=imgx-cli", "-y", "imgx-mcp"],
|
|
257
|
-
"env": { "GEMINI_API_KEY": "your-
|
|
268
|
+
"env": { "GEMINI_API_KEY": "your-key", "OPENAI_API_KEY": "your-key" }
|
|
258
269
|
}
|
|
259
270
|
}
|
|
260
271
|
}
|
|
@@ -270,11 +281,13 @@ Config file location: `%APPDATA%\Claude\claude_desktop_config.json` (Windows) or
|
|
|
270
281
|
[mcp_servers.imgx]
|
|
271
282
|
command = "npx"
|
|
272
283
|
args = ["--package=imgx-cli", "-y", "imgx-mcp"]
|
|
273
|
-
env = { GEMINI_API_KEY = "your-
|
|
284
|
+
env = { GEMINI_API_KEY = "your-key", OPENAI_API_KEY = "your-key" }
|
|
274
285
|
```
|
|
275
286
|
|
|
276
287
|
The same `npx` pattern works with Cursor, Windsurf, Continue.dev, Cline, Zed, and other MCP-compatible tools. On Windows, use `cmd /c npx` instead of `npx` directly.
|
|
277
288
|
|
|
289
|
+
Only include the API keys for providers you want to use. At least one is required.
|
|
290
|
+
|
|
278
291
|
## Architecture
|
|
279
292
|
|
|
280
293
|
imgx separates **model-independent** and **model-dependent** concerns:
|
|
@@ -308,6 +321,7 @@ Each provider declares its supported capabilities. The CLI dynamically enables o
|
|
|
308
321
|
| Provider | Models | Capabilities |
|
|
309
322
|
|----------|--------|-------------|
|
|
310
323
|
| Gemini | `gemini-3-pro-image-preview`, `gemini-2.5-flash-image` | All 7 capabilities |
|
|
324
|
+
| OpenAI | `gpt-image-1` | Generate, edit, aspect ratio, multi-output |
|
|
311
325
|
|
|
312
326
|
## Development
|
|
313
327
|
|
package/dist/cli.bundle.js
CHANGED
|
@@ -39210,8 +39210,9 @@ function getApiKeyFromEnv() {
|
|
|
39210
39210
|
|
|
39211
39211
|
// build/core/storage.js
|
|
39212
39212
|
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
|
|
39213
|
-
import { dirname, resolve as resolve2 } from "node:path";
|
|
39213
|
+
import { dirname, join as join2, resolve as resolve2 } from "node:path";
|
|
39214
39214
|
import { randomUUID } from "node:crypto";
|
|
39215
|
+
import { homedir as homedir2 } from "node:os";
|
|
39215
39216
|
var MIME_TO_EXT = {
|
|
39216
39217
|
"image/png": ".png",
|
|
39217
39218
|
"image/jpeg": ".jpg",
|
|
@@ -39224,9 +39225,17 @@ function readImageAsBase64(filePath) {
|
|
|
39224
39225
|
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png";
|
|
39225
39226
|
return { data: buffer.toString("base64"), mimeType };
|
|
39226
39227
|
}
|
|
39228
|
+
function fallbackOutputDir(outputDir) {
|
|
39229
|
+
if (outputDir)
|
|
39230
|
+
return outputDir;
|
|
39231
|
+
const configured = resolveDefault("outputDir");
|
|
39232
|
+
if (configured)
|
|
39233
|
+
return configured;
|
|
39234
|
+
return join2(homedir2(), "Pictures", "imgx");
|
|
39235
|
+
}
|
|
39227
39236
|
function saveImage(image, outputPath, outputDir) {
|
|
39228
39237
|
const ext = MIME_TO_EXT[image.mimeType] || ".png";
|
|
39229
|
-
const filePath = outputPath ? resolve2(outputPath) : resolve2(outputDir
|
|
39238
|
+
const filePath = outputPath ? resolve2(outputPath) : resolve2(fallbackOutputDir(outputDir), `imgx-${randomUUID().slice(0, 8)}${ext}`);
|
|
39230
39239
|
mkdirSync2(dirname(filePath), { recursive: true });
|
|
39231
39240
|
writeFileSync2(filePath, image.data);
|
|
39232
39241
|
return filePath;
|
|
@@ -39760,7 +39769,7 @@ function showAll() {
|
|
|
39760
39769
|
}
|
|
39761
39770
|
|
|
39762
39771
|
// build/cli/index.js
|
|
39763
|
-
var VERSION2 = "0.6.
|
|
39772
|
+
var VERSION2 = "0.6.1";
|
|
39764
39773
|
var HELP = `imgx v${VERSION2} \u2014 AI image generation and editing CLI
|
|
39765
39774
|
|
|
39766
39775
|
Commands:
|
package/dist/mcp.bundle.js
CHANGED
|
@@ -52131,6 +52131,28 @@ function resolveApiKey(providerName) {
|
|
|
52131
52131
|
const config2 = loadConfig();
|
|
52132
52132
|
return config2.providers?.[providerName]?.apiKey;
|
|
52133
52133
|
}
|
|
52134
|
+
function loadProjectConfig() {
|
|
52135
|
+
try {
|
|
52136
|
+
const raw = readFileSync(resolve(".imgxrc"), "utf-8");
|
|
52137
|
+
return JSON.parse(raw);
|
|
52138
|
+
} catch {
|
|
52139
|
+
return {};
|
|
52140
|
+
}
|
|
52141
|
+
}
|
|
52142
|
+
function resolveDefault(key) {
|
|
52143
|
+
const envMap = {
|
|
52144
|
+
provider: process.env.IMGX_PROVIDER,
|
|
52145
|
+
model: process.env.IMGX_MODEL,
|
|
52146
|
+
outputDir: process.env.IMGX_OUTPUT_DIR
|
|
52147
|
+
};
|
|
52148
|
+
if (envMap[key])
|
|
52149
|
+
return envMap[key];
|
|
52150
|
+
const project = loadProjectConfig();
|
|
52151
|
+
if (project.defaults?.[key])
|
|
52152
|
+
return project.defaults[key];
|
|
52153
|
+
const config2 = loadConfig();
|
|
52154
|
+
return config2.defaults?.[key];
|
|
52155
|
+
}
|
|
52134
52156
|
function saveLastOutput(filePaths) {
|
|
52135
52157
|
const dir = configDir();
|
|
52136
52158
|
mkdirSync(dir, { recursive: true });
|
|
@@ -69226,8 +69248,9 @@ function getApiKeyFromEnv() {
|
|
|
69226
69248
|
|
|
69227
69249
|
// build/core/storage.js
|
|
69228
69250
|
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
|
|
69229
|
-
import { dirname, resolve as resolve2 } from "node:path";
|
|
69251
|
+
import { dirname, join as join2, resolve as resolve2 } from "node:path";
|
|
69230
69252
|
import { randomUUID } from "node:crypto";
|
|
69253
|
+
import { homedir as homedir2 } from "node:os";
|
|
69231
69254
|
var MIME_TO_EXT = {
|
|
69232
69255
|
"image/png": ".png",
|
|
69233
69256
|
"image/jpeg": ".jpg",
|
|
@@ -69240,9 +69263,17 @@ function readImageAsBase64(filePath) {
|
|
|
69240
69263
|
const mimeType = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png";
|
|
69241
69264
|
return { data: buffer.toString("base64"), mimeType };
|
|
69242
69265
|
}
|
|
69266
|
+
function fallbackOutputDir(outputDir) {
|
|
69267
|
+
if (outputDir)
|
|
69268
|
+
return outputDir;
|
|
69269
|
+
const configured = resolveDefault("outputDir");
|
|
69270
|
+
if (configured)
|
|
69271
|
+
return configured;
|
|
69272
|
+
return join2(homedir2(), "Pictures", "imgx");
|
|
69273
|
+
}
|
|
69243
69274
|
function saveImage(image, outputPath, outputDir) {
|
|
69244
69275
|
const ext = MIME_TO_EXT[image.mimeType] || ".png";
|
|
69245
|
-
const filePath = outputPath ? resolve2(outputPath) : resolve2(outputDir
|
|
69276
|
+
const filePath = outputPath ? resolve2(outputPath) : resolve2(fallbackOutputDir(outputDir), `imgx-${randomUUID().slice(0, 8)}${ext}`);
|
|
69246
69277
|
mkdirSync2(dirname(filePath), { recursive: true });
|
|
69247
69278
|
writeFileSync2(filePath, image.data);
|
|
69248
69279
|
return filePath;
|
|
@@ -69563,7 +69594,7 @@ function initOpenAI() {
|
|
|
69563
69594
|
// build/mcp/server.js
|
|
69564
69595
|
var server = new McpServer({
|
|
69565
69596
|
name: "imgx",
|
|
69566
|
-
version: "0.6.
|
|
69597
|
+
version: "0.6.1"
|
|
69567
69598
|
});
|
|
69568
69599
|
initGemini();
|
|
69569
69600
|
initOpenAI();
|