grok-image-cli 0.1.1 → 0.3.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 +23 -22
- package/dist/main.mjs +152 -46
- package/dist/main.mjs.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
CLI for generating and editing images with Grok API, powered by `@ai-sdk/xai`.
|
|
4
4
|
|
|
5
|
+
Supports multiple models: `grok-imagine-image` (default), `grok-imagine-image-pro`, `grok-2-image-1212`.
|
|
6
|
+
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
7
9
|
```bash
|
|
@@ -11,7 +13,7 @@ npm install -g grok-image-cli
|
|
|
11
13
|
### From source
|
|
12
14
|
|
|
13
15
|
```bash
|
|
14
|
-
git clone https://github.com/cyberash/grok-image-cli.git
|
|
16
|
+
git clone https://github.com/cyberash-dev/grok-image-cli.git
|
|
15
17
|
cd grok-image-cli
|
|
16
18
|
npm install
|
|
17
19
|
npm run build
|
|
@@ -20,36 +22,28 @@ npm link
|
|
|
20
22
|
|
|
21
23
|
## Authentication
|
|
22
24
|
|
|
23
|
-
The CLI stores your xAI API key securely in the macOS Keychain
|
|
25
|
+
The CLI stores your xAI API key securely in the OS native credential store (macOS Keychain, Windows Credential Manager, Linux Secret Service) via `cross-keychain`. Alternatively, set the `XAI_API_KEY` environment variable.
|
|
24
26
|
|
|
25
27
|
```bash
|
|
26
|
-
# Save API key
|
|
27
|
-
grok-img auth
|
|
28
|
-
|
|
29
|
-
# Check authentication status
|
|
30
|
-
grok-img auth status
|
|
31
|
-
|
|
32
|
-
# Remove API key from keychain
|
|
33
|
-
grok-img auth logout
|
|
28
|
+
grok-img auth login # Save API key (interactive prompt)
|
|
29
|
+
grok-img auth status # Check authentication status
|
|
30
|
+
grok-img auth logout # Remove API key
|
|
34
31
|
```
|
|
35
32
|
|
|
36
33
|
## Image Generation
|
|
37
34
|
|
|
38
35
|
```bash
|
|
39
|
-
# Generate a single image
|
|
40
36
|
grok-img generate "A futuristic city skyline at night"
|
|
41
|
-
|
|
42
|
-
# Generate multiple images with specific aspect ratio
|
|
43
37
|
grok-img generate "Mountain landscape at sunrise" -n 4 -a 16:9
|
|
44
|
-
|
|
45
|
-
# Specify output directory
|
|
46
38
|
grok-img generate "A serene Japanese garden" -o ./my-images
|
|
39
|
+
grok-img generate "Photorealistic portrait" -m grok-imagine-image-pro
|
|
47
40
|
```
|
|
48
41
|
|
|
49
42
|
### Options
|
|
50
43
|
|
|
51
44
|
| Option | Description | Default |
|
|
52
45
|
|--------|-------------|---------|
|
|
46
|
+
| `-m, --model <model>` | Model (`grok-imagine-image`, `grok-imagine-image-pro`, `grok-2-image-1212`) | `grok-imagine-image` |
|
|
53
47
|
| `-a, --aspect-ratio <ratio>` | Aspect ratio (`1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `3:2`, `2:3`, `2:1`, `1:2`, `19.5:9`, `9:19.5`, `20:9`, `9:20`, `auto`) | `auto` |
|
|
54
48
|
| `-n, --count <number>` | Number of images (1-10) | `1` |
|
|
55
49
|
| `-o, --output <dir>` | Output directory | `./grok-images` |
|
|
@@ -57,14 +51,10 @@ grok-img generate "A serene Japanese garden" -o ./my-images
|
|
|
57
51
|
## Image Editing
|
|
58
52
|
|
|
59
53
|
```bash
|
|
60
|
-
# Edit a local image
|
|
61
54
|
grok-img edit "Make it look like a watercolor painting" -i ./photo.jpg
|
|
62
|
-
|
|
63
|
-
# Edit using a URL
|
|
64
55
|
grok-img edit "Change the sky to sunset colors" -i https://example.com/photo.jpg
|
|
65
|
-
|
|
66
|
-
# Specify aspect ratio and output directory
|
|
67
56
|
grok-img edit "Add a vintage film grain effect" -i ./photo.jpg -a 3:2 -o ./edited
|
|
57
|
+
grok-img edit "Render as pencil sketch" -i ./photo.jpg -m grok-imagine-image-pro
|
|
68
58
|
```
|
|
69
59
|
|
|
70
60
|
### Options
|
|
@@ -72,9 +62,20 @@ grok-img edit "Add a vintage film grain effect" -i ./photo.jpg -a 3:2 -o ./edite
|
|
|
72
62
|
| Option | Description | Default |
|
|
73
63
|
|--------|-------------|---------|
|
|
74
64
|
| `-i, --image <path>` | Source image (local path or URL) | **required** |
|
|
65
|
+
| `-m, --model <model>` | Model (`grok-imagine-image`, `grok-imagine-image-pro`, `grok-2-image-1212`) | `grok-imagine-image` |
|
|
75
66
|
| `-a, --aspect-ratio <ratio>` | Aspect ratio | `auto` |
|
|
76
67
|
| `-o, --output <dir>` | Output directory | `./grok-images` |
|
|
77
68
|
|
|
69
|
+
## Models
|
|
70
|
+
|
|
71
|
+
| Model | Modalities | Rate Limit | Price |
|
|
72
|
+
|-------|------------|------------|-------|
|
|
73
|
+
| `grok-imagine-image` | text, image -> image | 300 RPM | $0.02/image |
|
|
74
|
+
| `grok-imagine-image-pro` | text, image -> image | 30 RPM | $0.07/image |
|
|
75
|
+
| `grok-2-image-1212` | text -> image | 300 RPM | $0.07/image |
|
|
76
|
+
|
|
77
|
+
`grok-2-image-1212` does not support `aspect_ratio: "auto"` or image editing.
|
|
78
|
+
|
|
78
79
|
## Development
|
|
79
80
|
|
|
80
81
|
```bash
|
|
@@ -95,14 +96,14 @@ src/
|
|
|
95
96
|
main.ts # Composition root
|
|
96
97
|
domain/ # Entities & port interfaces (zero deps)
|
|
97
98
|
application/ # Use cases (depends on domain only)
|
|
98
|
-
infrastructure/ # Adapters (@ai-sdk/xai, keychain, fs)
|
|
99
|
+
infrastructure/ # Adapters (@ai-sdk/xai, cross-keychain, fs)
|
|
99
100
|
presentation/ # CLI commands (commander)
|
|
100
101
|
```
|
|
101
102
|
|
|
102
103
|
## Requirements
|
|
103
104
|
|
|
104
105
|
- Node.js >= 20.19.0
|
|
105
|
-
- macOS
|
|
106
|
+
- macOS, Windows, or Linux
|
|
106
107
|
- xAI API key from [console.x.ai](https://console.x.ai)
|
|
107
108
|
|
|
108
109
|
## License
|
package/dist/main.mjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import chalk from "chalk";
|
|
2
3
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
4
|
import { join, resolve } from "node:path";
|
|
4
5
|
import { createXai } from "@ai-sdk/xai";
|
|
5
6
|
import { NoImageGeneratedError, generateImage } from "ai";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
7
|
+
import { deletePassword, getPassword, setPassword } from "cross-keychain";
|
|
8
|
+
import { execFileSync } from "node:child_process";
|
|
8
9
|
import { Command } from "commander";
|
|
9
10
|
import { createInterface } from "node:readline";
|
|
10
|
-
import chalk from "chalk";
|
|
11
11
|
import ora from "ora";
|
|
12
12
|
|
|
13
13
|
//#region src/domain/errors.ts
|
|
@@ -94,7 +94,7 @@ var GetAuthStatusUseCase = class {
|
|
|
94
94
|
return {
|
|
95
95
|
authenticated: true,
|
|
96
96
|
maskedKey: key.length > 8 ? `${key.slice(0, 4)}${"*".repeat(key.length - 8)}${key.slice(-4)}` : "****",
|
|
97
|
-
source: process.env.XAI_API_KEY === key ? "env" : "
|
|
97
|
+
source: process.env.XAI_API_KEY === key ? "env" : "credential-store"
|
|
98
98
|
};
|
|
99
99
|
}
|
|
100
100
|
};
|
|
@@ -121,6 +121,18 @@ var LogoutUseCase = class {
|
|
|
121
121
|
}
|
|
122
122
|
};
|
|
123
123
|
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/infrastructure/adapters/env-key-store.adapter.ts
|
|
126
|
+
var EnvKeyStoreAdapter = class {
|
|
127
|
+
async get() {
|
|
128
|
+
return process.env.XAI_API_KEY ?? null;
|
|
129
|
+
}
|
|
130
|
+
async save(_key) {
|
|
131
|
+
throw new Error("XAI_API_KEY is a read-only environment variable");
|
|
132
|
+
}
|
|
133
|
+
async remove() {}
|
|
134
|
+
};
|
|
135
|
+
|
|
124
136
|
//#endregion
|
|
125
137
|
//#region src/infrastructure/adapters/file-storage.adapter.ts
|
|
126
138
|
const MEDIA_TYPE_EXT = {
|
|
@@ -149,13 +161,13 @@ var FileStorageAdapter = class {
|
|
|
149
161
|
|
|
150
162
|
//#endregion
|
|
151
163
|
//#region src/infrastructure/adapters/grok-api.adapter.ts
|
|
152
|
-
const
|
|
164
|
+
const DEFAULT_MODEL = "grok-imagine-image";
|
|
153
165
|
var GrokApiAdapter = class {
|
|
154
166
|
async generate(params, apiKey) {
|
|
155
167
|
const xai = createXai({ apiKey });
|
|
156
168
|
try {
|
|
157
169
|
const { images } = await generateImage({
|
|
158
|
-
model: xai.image(
|
|
170
|
+
model: xai.image(params.model ?? DEFAULT_MODEL),
|
|
159
171
|
prompt: params.prompt,
|
|
160
172
|
aspectRatio: params.aspectRatio,
|
|
161
173
|
n: params.count
|
|
@@ -175,10 +187,10 @@ var GrokApiAdapter = class {
|
|
|
175
187
|
const xai = createXai({ apiKey });
|
|
176
188
|
try {
|
|
177
189
|
const { image } = await generateImage({
|
|
178
|
-
model: xai.image(
|
|
190
|
+
model: xai.image(params.model ?? DEFAULT_MODEL),
|
|
179
191
|
prompt: {
|
|
180
192
|
text: params.prompt,
|
|
181
|
-
images:
|
|
193
|
+
images: [params.imageSource]
|
|
182
194
|
},
|
|
183
195
|
aspectRatio: params.aspectRatio
|
|
184
196
|
});
|
|
@@ -197,46 +209,107 @@ var GrokApiAdapter = class {
|
|
|
197
209
|
|
|
198
210
|
//#endregion
|
|
199
211
|
//#region src/infrastructure/adapters/keychain.adapter.ts
|
|
200
|
-
const execFileAsync = promisify(execFile);
|
|
201
212
|
const SERVICE = "grok-image-cli";
|
|
202
213
|
const ACCOUNT = "api-key";
|
|
203
214
|
var KeychainAdapter = class {
|
|
204
215
|
async save(key) {
|
|
205
|
-
await
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
216
|
+
await setPassword(SERVICE, ACCOUNT, key);
|
|
217
|
+
}
|
|
218
|
+
async get() {
|
|
219
|
+
return await getPassword(SERVICE, ACCOUNT) ?? null;
|
|
220
|
+
}
|
|
221
|
+
async remove() {
|
|
222
|
+
await deletePassword(SERVICE, ACCOUNT);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
//#endregion
|
|
227
|
+
//#region src/infrastructure/adapters/pass.adapter.ts
|
|
228
|
+
const SECRET_PATH = "grok-image-cli/api-key";
|
|
229
|
+
function detectTool() {
|
|
230
|
+
for (const tool of ["gopass", "pass"]) try {
|
|
231
|
+
execFileSync("which", [tool], { stdio: "ignore" });
|
|
232
|
+
return tool;
|
|
233
|
+
} catch {}
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
const tool = detectTool();
|
|
237
|
+
var PassAdapter = class {
|
|
238
|
+
async save(key) {
|
|
239
|
+
if (!tool) throw new Error("Neither gopass nor pass is installed");
|
|
240
|
+
execFileSync(tool, tool === "gopass" ? [
|
|
241
|
+
"insert",
|
|
242
|
+
"-f",
|
|
243
|
+
SECRET_PATH
|
|
244
|
+
] : [
|
|
245
|
+
"insert",
|
|
246
|
+
"-m",
|
|
247
|
+
"--force",
|
|
248
|
+
SECRET_PATH
|
|
249
|
+
], {
|
|
250
|
+
input: `${key}\n`,
|
|
251
|
+
stdio: [
|
|
252
|
+
"pipe",
|
|
253
|
+
"ignore",
|
|
254
|
+
"ignore"
|
|
255
|
+
]
|
|
256
|
+
});
|
|
215
257
|
}
|
|
216
258
|
async get() {
|
|
259
|
+
if (!tool) throw new Error("Neither gopass nor pass is installed");
|
|
217
260
|
try {
|
|
218
|
-
|
|
219
|
-
"
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
]
|
|
226
|
-
|
|
261
|
+
return execFileSync(tool, ["show", SECRET_PATH], {
|
|
262
|
+
encoding: "utf-8",
|
|
263
|
+
stdio: [
|
|
264
|
+
"ignore",
|
|
265
|
+
"pipe",
|
|
266
|
+
"ignore"
|
|
267
|
+
]
|
|
268
|
+
}).trim().split("\n")[0] ?? null;
|
|
269
|
+
} catch {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
async remove() {
|
|
274
|
+
if (!tool) throw new Error("Neither gopass nor pass is installed");
|
|
275
|
+
try {
|
|
276
|
+
execFileSync(tool, [
|
|
277
|
+
"rm",
|
|
278
|
+
"--force",
|
|
279
|
+
SECRET_PATH
|
|
280
|
+
], { stdio: "ignore" });
|
|
281
|
+
} catch {}
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
//#endregion
|
|
286
|
+
//#region src/infrastructure/key-store-chain.ts
|
|
287
|
+
var KeyStoreChain = class {
|
|
288
|
+
constructor(stores, onSave) {
|
|
289
|
+
this.stores = stores;
|
|
290
|
+
this.onSave = onSave;
|
|
291
|
+
}
|
|
292
|
+
async save(key) {
|
|
293
|
+
let lastError;
|
|
294
|
+
for (const { store, name } of this.stores) try {
|
|
295
|
+
await store.save(key);
|
|
296
|
+
this.onSave?.(name);
|
|
297
|
+
return;
|
|
298
|
+
} catch (e) {
|
|
299
|
+
lastError = e;
|
|
300
|
+
}
|
|
301
|
+
throw lastError;
|
|
302
|
+
}
|
|
303
|
+
async get() {
|
|
304
|
+
for (const { store } of this.stores) try {
|
|
305
|
+
const key = await store.get();
|
|
227
306
|
if (key) return key;
|
|
228
307
|
} catch {}
|
|
229
|
-
return
|
|
308
|
+
return null;
|
|
230
309
|
}
|
|
231
310
|
async remove() {
|
|
232
|
-
try {
|
|
233
|
-
await
|
|
234
|
-
"delete-generic-password",
|
|
235
|
-
"-s",
|
|
236
|
-
SERVICE,
|
|
237
|
-
"-a",
|
|
238
|
-
ACCOUNT
|
|
239
|
-
]);
|
|
311
|
+
for (const { store } of this.stores) try {
|
|
312
|
+
await store.remove();
|
|
240
313
|
} catch {}
|
|
241
314
|
}
|
|
242
315
|
};
|
|
@@ -257,7 +330,7 @@ function readInput(prompt) {
|
|
|
257
330
|
}
|
|
258
331
|
function createAuthCommand(useCases) {
|
|
259
332
|
const auth = new Command("auth").description("Manage API key authentication");
|
|
260
|
-
auth.command("login").description("Store your xAI API key in the system
|
|
333
|
+
auth.command("login").description("Store your xAI API key in the system credential store").action(async () => {
|
|
261
334
|
try {
|
|
262
335
|
const apiKey = await readInput(chalk.cyan("Enter your xAI API key: "));
|
|
263
336
|
if (!apiKey) {
|
|
@@ -265,16 +338,16 @@ function createAuthCommand(useCases) {
|
|
|
265
338
|
process.exit(1);
|
|
266
339
|
}
|
|
267
340
|
await useCases.login.execute(apiKey);
|
|
268
|
-
console.log(chalk.green("API key saved
|
|
341
|
+
console.log(chalk.green("API key saved successfully."));
|
|
269
342
|
} catch (error) {
|
|
270
343
|
console.error(chalk.red(`Failed to save API key: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
271
344
|
process.exit(1);
|
|
272
345
|
}
|
|
273
346
|
});
|
|
274
|
-
auth.command("logout").description("Remove your xAI API key from the system
|
|
347
|
+
auth.command("logout").description("Remove your xAI API key from the system credential store").action(async () => {
|
|
275
348
|
try {
|
|
276
349
|
await useCases.logout.execute();
|
|
277
|
-
console.log(chalk.green("API key removed
|
|
350
|
+
console.log(chalk.green("API key removed successfully."));
|
|
278
351
|
} catch (error) {
|
|
279
352
|
console.error(chalk.red(`Failed to remove API key: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
280
353
|
process.exit(1);
|
|
@@ -301,6 +374,11 @@ function createAuthCommand(useCases) {
|
|
|
301
374
|
|
|
302
375
|
//#endregion
|
|
303
376
|
//#region src/presentation/commands/edit.command.ts
|
|
377
|
+
const VALID_MODELS$1 = [
|
|
378
|
+
"grok-2-image-1212",
|
|
379
|
+
"grok-imagine-image-pro",
|
|
380
|
+
"grok-imagine-image"
|
|
381
|
+
];
|
|
304
382
|
const VALID_RATIOS$1 = [
|
|
305
383
|
"1:1",
|
|
306
384
|
"16:9",
|
|
@@ -318,7 +396,11 @@ const VALID_RATIOS$1 = [
|
|
|
318
396
|
"auto"
|
|
319
397
|
];
|
|
320
398
|
function createEditCommand(editUseCase) {
|
|
321
|
-
return new Command("edit").description("Edit an existing image with a text prompt").argument("<prompt>", "Text prompt describing the edit to apply").requiredOption("-i, --image <path>", "Source image (local file path or URL)").option("-a, --aspect-ratio <ratio>", "Aspect ratio", "auto").option("-o, --output <dir>", "Output directory", "./grok-images").action(async (prompt, options) => {
|
|
399
|
+
return new Command("edit").description("Edit an existing image with a text prompt").argument("<prompt>", "Text prompt describing the edit to apply").requiredOption("-i, --image <path>", "Source image (local file path or URL)").option("-m, --model <model>", "Model to use", "grok-imagine-image").option("-a, --aspect-ratio <ratio>", "Aspect ratio", "auto").option("-o, --output <dir>", "Output directory", "./grok-images").action(async (prompt, options) => {
|
|
400
|
+
if (!VALID_MODELS$1.includes(options.model)) {
|
|
401
|
+
console.error(chalk.red(`Invalid model. Valid options: ${VALID_MODELS$1.join(", ")}`));
|
|
402
|
+
process.exit(1);
|
|
403
|
+
}
|
|
322
404
|
if (!VALID_RATIOS$1.includes(options.aspectRatio)) {
|
|
323
405
|
console.error(chalk.red(`Invalid aspect ratio. Valid options: ${VALID_RATIOS$1.join(", ")}`));
|
|
324
406
|
process.exit(1);
|
|
@@ -330,7 +412,8 @@ function createEditCommand(editUseCase) {
|
|
|
330
412
|
const savedPath = await editUseCase.execute({
|
|
331
413
|
prompt,
|
|
332
414
|
imageSource,
|
|
333
|
-
aspectRatio: options.aspectRatio
|
|
415
|
+
aspectRatio: options.aspectRatio,
|
|
416
|
+
model: options.model
|
|
334
417
|
}, outputDir);
|
|
335
418
|
spinner.succeed(chalk.green("Image edited successfully:"));
|
|
336
419
|
console.log(chalk.dim(` ${savedPath}`));
|
|
@@ -347,6 +430,11 @@ function createEditCommand(editUseCase) {
|
|
|
347
430
|
|
|
348
431
|
//#endregion
|
|
349
432
|
//#region src/presentation/commands/generate.command.ts
|
|
433
|
+
const VALID_MODELS = [
|
|
434
|
+
"grok-2-image-1212",
|
|
435
|
+
"grok-imagine-image-pro",
|
|
436
|
+
"grok-imagine-image"
|
|
437
|
+
];
|
|
350
438
|
const VALID_RATIOS = [
|
|
351
439
|
"1:1",
|
|
352
440
|
"16:9",
|
|
@@ -364,12 +452,16 @@ const VALID_RATIOS = [
|
|
|
364
452
|
"auto"
|
|
365
453
|
];
|
|
366
454
|
function createGenerateCommand(generateUseCase) {
|
|
367
|
-
return new Command("generate").description("Generate images from a text prompt").argument("<prompt>", "Text prompt describing the image to generate").option("-a, --aspect-ratio <ratio>", "Aspect ratio", "auto").option("-n, --count <number>", "Number of images (1-10)", "1").option("-o, --output <dir>", "Output directory", "./grok-images").action(async (prompt, options) => {
|
|
455
|
+
return new Command("generate").description("Generate images from a text prompt").argument("<prompt>", "Text prompt describing the image to generate").option("-m, --model <model>", "Model to use", "grok-imagine-image").option("-a, --aspect-ratio <ratio>", "Aspect ratio", "auto").option("-n, --count <number>", "Number of images (1-10)", "1").option("-o, --output <dir>", "Output directory", "./grok-images").action(async (prompt, options) => {
|
|
368
456
|
const count = parseInt(options.count, 10);
|
|
369
457
|
if (Number.isNaN(count) || count < 1 || count > 10) {
|
|
370
458
|
console.error(chalk.red("Count must be a number between 1 and 10."));
|
|
371
459
|
process.exit(1);
|
|
372
460
|
}
|
|
461
|
+
if (!VALID_MODELS.includes(options.model)) {
|
|
462
|
+
console.error(chalk.red(`Invalid model. Valid options: ${VALID_MODELS.join(", ")}`));
|
|
463
|
+
process.exit(1);
|
|
464
|
+
}
|
|
373
465
|
if (!VALID_RATIOS.includes(options.aspectRatio)) {
|
|
374
466
|
console.error(chalk.red(`Invalid aspect ratio. Valid options: ${VALID_RATIOS.join(", ")}`));
|
|
375
467
|
process.exit(1);
|
|
@@ -380,7 +472,8 @@ function createGenerateCommand(generateUseCase) {
|
|
|
380
472
|
const paths = await generateUseCase.execute({
|
|
381
473
|
prompt,
|
|
382
474
|
count,
|
|
383
|
-
aspectRatio: options.aspectRatio
|
|
475
|
+
aspectRatio: options.aspectRatio,
|
|
476
|
+
model: options.model
|
|
384
477
|
}, outputDir);
|
|
385
478
|
spinner.succeed(chalk.green(`Generated ${paths.length} image${paths.length > 1 ? "s" : ""}:`));
|
|
386
479
|
for (const p of paths) console.log(chalk.dim(` ${p}`));
|
|
@@ -410,7 +503,20 @@ function createCli(useCases) {
|
|
|
410
503
|
|
|
411
504
|
//#endregion
|
|
412
505
|
//#region src/main.ts
|
|
413
|
-
const keyStore = new
|
|
506
|
+
const keyStore = new KeyStoreChain([
|
|
507
|
+
{
|
|
508
|
+
store: new KeychainAdapter(),
|
|
509
|
+
name: "Keychain"
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
store: new PassAdapter(),
|
|
513
|
+
name: "pass"
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
store: new EnvKeyStoreAdapter(),
|
|
517
|
+
name: "XAI_API_KEY"
|
|
518
|
+
}
|
|
519
|
+
], (name) => console.log(chalk.dim(`Key stored in: ${name}`)));
|
|
414
520
|
const imageGenerator = new GrokApiAdapter();
|
|
415
521
|
const fileStorage = new FileStorageAdapter();
|
|
416
522
|
createCli({
|
package/dist/main.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.mjs","names":["VALID_RATIOS"],"sources":["../src/domain/errors.ts","../src/application/usecases/edit-image.usecase.ts","../src/application/usecases/generate-image.usecase.ts","../src/application/usecases/get-auth-status.usecase.ts","../src/application/usecases/login.usecase.ts","../src/application/usecases/logout.usecase.ts","../src/infrastructure/adapters/file-storage.adapter.ts","../src/infrastructure/adapters/grok-api.adapter.ts","../src/infrastructure/adapters/keychain.adapter.ts","../src/presentation/commands/auth.command.ts","../src/presentation/commands/edit.command.ts","../src/presentation/commands/generate.command.ts","../src/presentation/cli.ts","../src/main.ts"],"sourcesContent":["export class ApiKeyMissingError extends Error {\n constructor() {\n super(\n \"API key not found. Run `grok-img auth login` or set XAI_API_KEY environment variable.\",\n )\n this.name = \"ApiKeyMissingError\"\n }\n}\n\nexport class ApiError extends Error {\n constructor(\n message: string,\n public readonly cause?: unknown,\n ) {\n super(message)\n this.name = \"ApiError\"\n }\n}\n\nexport class ImageNotFoundError extends Error {\n constructor(path: string) {\n super(`Image not found: ${path}`)\n this.name = \"ImageNotFoundError\"\n }\n}\n","import type { EditParams } from \"../../domain/entities/edit-params.js\"\nimport { ApiKeyMissingError } from \"../../domain/errors.js\"\nimport type { FileStoragePort } from \"../../domain/ports/file-storage.port.js\"\nimport type { ImageGeneratorPort } from \"../../domain/ports/image-generator.port.js\"\nimport type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport class EditImageUseCase {\n constructor(\n private readonly imageGenerator: ImageGeneratorPort,\n private readonly keyStore: KeyStorePort,\n private readonly fileStorage: FileStoragePort,\n ) {}\n\n async execute(params: EditParams, outputDir: string): Promise<string> {\n const apiKey = await this.keyStore.get()\n if (!apiKey) throw new ApiKeyMissingError()\n\n let imageSource = params.imageSource\n if (typeof imageSource === \"string\" && !imageSource.startsWith(\"http\")) {\n imageSource = await this.fileStorage.readImage(imageSource)\n }\n\n this.fileStorage.ensureDir(outputDir)\n\n const result = await this.imageGenerator.edit({ ...params, imageSource }, apiKey)\n\n const outputPath = this.fileStorage.generateOutputPath(outputDir, 0, result.mediaType)\n return this.fileStorage.saveImage(result.uint8Array, outputPath)\n }\n}\n","import type { GenerateParams } from \"../../domain/entities/generate-params.js\"\nimport { ApiKeyMissingError } from \"../../domain/errors.js\"\nimport type { FileStoragePort } from \"../../domain/ports/file-storage.port.js\"\nimport type { ImageGeneratorPort } from \"../../domain/ports/image-generator.port.js\"\nimport type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport class GenerateImageUseCase {\n constructor(\n private readonly imageGenerator: ImageGeneratorPort,\n private readonly keyStore: KeyStorePort,\n private readonly fileStorage: FileStoragePort,\n ) {}\n\n async execute(params: GenerateParams, outputDir: string): Promise<string[]> {\n const apiKey = await this.keyStore.get()\n if (!apiKey) throw new ApiKeyMissingError()\n\n this.fileStorage.ensureDir(outputDir)\n\n const results = await this.imageGenerator.generate(params, apiKey)\n\n const savedPaths: string[] = []\n for (let i = 0; i < results.length; i++) {\n const result = results[i]\n const outputPath = this.fileStorage.generateOutputPath(\n outputDir,\n i,\n result.mediaType,\n )\n const saved = await this.fileStorage.saveImage(result.uint8Array, outputPath)\n savedPaths.push(saved)\n }\n\n return savedPaths\n }\n}\n","import type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport type AuthStatus = {\n authenticated: boolean\n maskedKey: string | null\n source: \"keychain\" | \"env\" | null\n}\n\nexport class GetAuthStatusUseCase {\n constructor(private readonly keyStore: KeyStorePort) {}\n\n async execute(): Promise<AuthStatus> {\n const key = await this.keyStore.get()\n\n if (!key) {\n return { authenticated: false, maskedKey: null, source: null }\n }\n\n const masked =\n key.length > 8\n ? `${key.slice(0, 4)}${\"*\".repeat(key.length - 8)}${key.slice(-4)}`\n : \"****\"\n\n const source = process.env.XAI_API_KEY === key ? \"env\" : \"keychain\"\n\n return { authenticated: true, maskedKey: masked, source }\n }\n}\n","import type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport class LoginUseCase {\n constructor(private readonly keyStore: KeyStorePort) {}\n\n async execute(apiKey: string): Promise<void> {\n await this.keyStore.save(apiKey)\n }\n}\n","import type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport class LogoutUseCase {\n constructor(private readonly keyStore: KeyStorePort) {}\n\n async execute(): Promise<void> {\n await this.keyStore.remove()\n }\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport { ImageNotFoundError } from \"../../domain/errors.js\"\nimport type { FileStoragePort } from \"../../domain/ports/file-storage.port.js\"\n\nconst MEDIA_TYPE_EXT: Record<string, string> = {\n \"image/png\": \"png\",\n \"image/jpeg\": \"jpg\",\n \"image/webp\": \"webp\",\n \"image/gif\": \"gif\",\n}\n\nexport class FileStorageAdapter implements FileStoragePort {\n async saveImage(data: Uint8Array, outputPath: string): Promise<string> {\n writeFileSync(outputPath, data)\n return outputPath\n }\n\n async readImage(filePath: string): Promise<Uint8Array> {\n if (!existsSync(filePath)) {\n throw new ImageNotFoundError(filePath)\n }\n return new Uint8Array(readFileSync(filePath))\n }\n\n ensureDir(dir: string): void {\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true })\n }\n }\n\n generateOutputPath(dir: string, index: number, mediaType: string): string {\n const ext = MEDIA_TYPE_EXT[mediaType] ?? \"png\"\n const timestamp = Date.now()\n return join(dir, `grok-img-${timestamp}-${index}.${ext}`)\n }\n}\n","import { createXai } from \"@ai-sdk/xai\"\nimport { generateImage, NoImageGeneratedError } from \"ai\"\nimport type { EditParams } from \"../../domain/entities/edit-params.js\"\nimport type { GenerateParams } from \"../../domain/entities/generate-params.js\"\nimport type { ImageResult } from \"../../domain/entities/image-result.js\"\nimport { ApiError } from \"../../domain/errors.js\"\nimport type { ImageGeneratorPort } from \"../../domain/ports/image-generator.port.js\"\n\nconst MODEL = \"grok-imagine-image\"\n\nexport class GrokApiAdapter implements ImageGeneratorPort {\n async generate(params: GenerateParams, apiKey: string): Promise<ImageResult[]> {\n const xai = createXai({ apiKey })\n\n try {\n const { images } = await generateImage({\n model: xai.image(MODEL),\n prompt: params.prompt,\n aspectRatio: params.aspectRatio,\n n: params.count,\n })\n\n return images.map((img) => ({\n base64: img.base64,\n uint8Array: img.uint8Array,\n mediaType: img.mediaType,\n }))\n } catch (error) {\n if (NoImageGeneratedError.isInstance(error)) {\n throw new ApiError(\n \"Image generation failed: the model could not produce an image.\",\n error,\n )\n }\n if (error instanceof Error) {\n throw new ApiError(`API request failed: ${error.message}`, error)\n }\n throw new ApiError(\"An unexpected error occurred during image generation.\", error)\n }\n }\n\n async edit(params: EditParams, apiKey: string): Promise<ImageResult> {\n const xai = createXai({ apiKey })\n\n try {\n const { image } = await generateImage({\n model: xai.image(MODEL),\n prompt: {\n text: params.prompt,\n images:\n typeof params.imageSource === \"string\"\n ? [new URL(params.imageSource)]\n : [params.imageSource],\n },\n aspectRatio: params.aspectRatio,\n })\n\n return {\n base64: image.base64,\n uint8Array: image.uint8Array,\n mediaType: image.mediaType,\n }\n } catch (error) {\n if (NoImageGeneratedError.isInstance(error)) {\n throw new ApiError(\n \"Image editing failed: the model could not produce an image.\",\n error,\n )\n }\n if (error instanceof Error) {\n throw new ApiError(`API request failed: ${error.message}`, error)\n }\n throw new ApiError(\"An unexpected error occurred during image editing.\", error)\n }\n }\n}\n","import { execFile } from \"node:child_process\"\nimport { promisify } from \"node:util\"\nimport type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nconst execFileAsync = promisify(execFile)\n\nconst SERVICE = \"grok-image-cli\"\nconst ACCOUNT = \"api-key\"\n\nexport class KeychainAdapter implements KeyStorePort {\n async save(key: string): Promise<void> {\n await execFileAsync(\"security\", [\n \"add-generic-password\",\n \"-U\",\n \"-s\",\n SERVICE,\n \"-a\",\n ACCOUNT,\n \"-w\",\n key,\n ])\n }\n\n async get(): Promise<string | null> {\n try {\n const { stdout } = await execFileAsync(\"security\", [\n \"find-generic-password\",\n \"-s\",\n SERVICE,\n \"-a\",\n ACCOUNT,\n \"-w\",\n ])\n const key = stdout.trim()\n if (key) return key\n } catch {\n // not found in keychain\n }\n\n return process.env.XAI_API_KEY ?? null\n }\n\n async remove(): Promise<void> {\n try {\n await execFileAsync(\"security\", [\n \"delete-generic-password\",\n \"-s\",\n SERVICE,\n \"-a\",\n ACCOUNT,\n ])\n } catch {\n // already removed or never existed\n }\n }\n}\n","import { createInterface } from \"node:readline\"\nimport chalk from \"chalk\"\nimport { Command } from \"commander\"\nimport type { GetAuthStatusUseCase } from \"../../application/usecases/get-auth-status.usecase.js\"\nimport type { LoginUseCase } from \"../../application/usecases/login.usecase.js\"\nimport type { LogoutUseCase } from \"../../application/usecases/logout.usecase.js\"\n\nfunction readInput(prompt: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n })\n\n return new Promise((resolve) => {\n rl.question(prompt, (answer) => {\n rl.close()\n resolve(answer.trim())\n })\n })\n}\n\nexport function createAuthCommand(useCases: {\n login: LoginUseCase\n logout: LogoutUseCase\n getStatus: GetAuthStatusUseCase\n}): Command {\n const auth = new Command(\"auth\").description(\"Manage API key authentication\")\n\n auth\n .command(\"login\")\n .description(\"Store your xAI API key in the system keychain\")\n .action(async () => {\n try {\n const apiKey = await readInput(chalk.cyan(\"Enter your xAI API key: \"))\n\n if (!apiKey) {\n console.log(chalk.red(\"No API key provided.\"))\n process.exit(1)\n }\n\n await useCases.login.execute(apiKey)\n console.log(chalk.green(\"API key saved to keychain successfully.\"))\n } catch (error) {\n console.error(\n chalk.red(\n `Failed to save API key: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n ),\n )\n process.exit(1)\n }\n })\n\n auth\n .command(\"logout\")\n .description(\"Remove your xAI API key from the system keychain\")\n .action(async () => {\n try {\n await useCases.logout.execute()\n console.log(chalk.green(\"API key removed from keychain.\"))\n } catch (error) {\n console.error(\n chalk.red(\n `Failed to remove API key: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n ),\n )\n process.exit(1)\n }\n })\n\n auth\n .command(\"status\")\n .description(\"Check current authentication status\")\n .action(async () => {\n try {\n const status = await useCases.getStatus.execute()\n\n if (status.authenticated) {\n console.log(chalk.green(\"Authenticated\"))\n console.log(chalk.dim(` Key: ${status.maskedKey}`))\n console.log(chalk.dim(` Source: ${status.source}`))\n } else {\n console.log(chalk.yellow(\"Not authenticated\"))\n console.log(\n chalk.dim(\n \" Run `grok-img auth login` or set XAI_API_KEY environment variable.\",\n ),\n )\n }\n } catch (error) {\n console.error(\n chalk.red(\n `Failed to check status: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n ),\n )\n process.exit(1)\n }\n })\n\n return auth\n}\n","import { resolve } from \"node:path\"\nimport chalk from \"chalk\"\nimport { Command } from \"commander\"\nimport ora from \"ora\"\nimport type { EditImageUseCase } from \"../../application/usecases/edit-image.usecase.js\"\nimport { ApiError, ApiKeyMissingError, ImageNotFoundError } from \"../../domain/errors.js\"\n\nconst VALID_RATIOS = [\n \"1:1\",\n \"16:9\",\n \"9:16\",\n \"4:3\",\n \"3:4\",\n \"3:2\",\n \"2:3\",\n \"2:1\",\n \"1:2\",\n \"19.5:9\",\n \"9:19.5\",\n \"20:9\",\n \"9:20\",\n \"auto\",\n]\n\nexport function createEditCommand(editUseCase: EditImageUseCase): Command {\n return new Command(\"edit\")\n .description(\"Edit an existing image with a text prompt\")\n .argument(\"<prompt>\", \"Text prompt describing the edit to apply\")\n .requiredOption(\"-i, --image <path>\", \"Source image (local file path or URL)\")\n .option(\"-a, --aspect-ratio <ratio>\", \"Aspect ratio\", \"auto\")\n .option(\"-o, --output <dir>\", \"Output directory\", \"./grok-images\")\n .action(async (prompt: string, options) => {\n if (!VALID_RATIOS.includes(options.aspectRatio)) {\n console.error(\n chalk.red(`Invalid aspect ratio. Valid options: ${VALID_RATIOS.join(\", \")}`),\n )\n process.exit(1)\n }\n\n const imageSource = options.image.startsWith(\"http\")\n ? options.image\n : resolve(options.image)\n\n const outputDir = resolve(options.output)\n const spinner = ora(\"Editing image...\").start()\n\n try {\n const savedPath = await editUseCase.execute(\n { prompt, imageSource, aspectRatio: options.aspectRatio },\n outputDir,\n )\n\n spinner.succeed(chalk.green(\"Image edited successfully:\"))\n console.log(chalk.dim(` ${savedPath}`))\n } catch (error) {\n spinner.fail()\n if (error instanceof ApiKeyMissingError) {\n console.error(chalk.red(error.message))\n } else if (error instanceof ImageNotFoundError) {\n console.error(chalk.red(error.message))\n } else if (error instanceof ApiError) {\n console.error(chalk.red(`API Error: ${error.message}`))\n } else {\n console.error(\n chalk.red(\n `Unexpected error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n ),\n )\n }\n process.exit(1)\n }\n })\n}\n","import { resolve } from \"node:path\"\nimport chalk from \"chalk\"\nimport { Command } from \"commander\"\nimport ora from \"ora\"\nimport type { GenerateImageUseCase } from \"../../application/usecases/generate-image.usecase.js\"\nimport { ApiError, ApiKeyMissingError } from \"../../domain/errors.js\"\n\nconst VALID_RATIOS = [\n \"1:1\",\n \"16:9\",\n \"9:16\",\n \"4:3\",\n \"3:4\",\n \"3:2\",\n \"2:3\",\n \"2:1\",\n \"1:2\",\n \"19.5:9\",\n \"9:19.5\",\n \"20:9\",\n \"9:20\",\n \"auto\",\n]\n\nexport function createGenerateCommand(generateUseCase: GenerateImageUseCase): Command {\n return new Command(\"generate\")\n .description(\"Generate images from a text prompt\")\n .argument(\"<prompt>\", \"Text prompt describing the image to generate\")\n .option(\"-a, --aspect-ratio <ratio>\", \"Aspect ratio\", \"auto\")\n .option(\"-n, --count <number>\", \"Number of images (1-10)\", \"1\")\n .option(\"-o, --output <dir>\", \"Output directory\", \"./grok-images\")\n .action(async (prompt: string, options) => {\n const count = parseInt(options.count, 10)\n if (Number.isNaN(count) || count < 1 || count > 10) {\n console.error(chalk.red(\"Count must be a number between 1 and 10.\"))\n process.exit(1)\n }\n\n if (!VALID_RATIOS.includes(options.aspectRatio)) {\n console.error(\n chalk.red(`Invalid aspect ratio. Valid options: ${VALID_RATIOS.join(\", \")}`),\n )\n process.exit(1)\n }\n\n const outputDir = resolve(options.output)\n const spinner = ora(`Generating ${count} image${count > 1 ? \"s\" : \"\"}...`).start()\n\n try {\n const paths = await generateUseCase.execute(\n { prompt, count, aspectRatio: options.aspectRatio },\n outputDir,\n )\n\n spinner.succeed(\n chalk.green(`Generated ${paths.length} image${paths.length > 1 ? \"s\" : \"\"}:`),\n )\n for (const p of paths) {\n console.log(chalk.dim(` ${p}`))\n }\n } catch (error) {\n spinner.fail()\n if (error instanceof ApiKeyMissingError) {\n console.error(chalk.red(error.message))\n } else if (error instanceof ApiError) {\n console.error(chalk.red(`API Error: ${error.message}`))\n } else {\n console.error(\n chalk.red(\n `Unexpected error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n ),\n )\n }\n process.exit(1)\n }\n })\n}\n","import { Command } from \"commander\"\nimport type { EditImageUseCase } from \"../application/usecases/edit-image.usecase.js\"\nimport type { GenerateImageUseCase } from \"../application/usecases/generate-image.usecase.js\"\nimport type { GetAuthStatusUseCase } from \"../application/usecases/get-auth-status.usecase.js\"\nimport type { LoginUseCase } from \"../application/usecases/login.usecase.js\"\nimport type { LogoutUseCase } from \"../application/usecases/logout.usecase.js\"\nimport { createAuthCommand } from \"./commands/auth.command.js\"\nimport { createEditCommand } from \"./commands/edit.command.js\"\nimport { createGenerateCommand } from \"./commands/generate.command.js\"\n\nexport type UseCases = {\n generateImage: GenerateImageUseCase\n editImage: EditImageUseCase\n login: LoginUseCase\n logout: LogoutUseCase\n getAuthStatus: GetAuthStatusUseCase\n}\n\nexport function createCli(useCases: UseCases): Command {\n const program = new Command()\n .name(\"grok-img\")\n .description(\"CLI for generating and editing images with Grok API\")\n .version(\"1.0.0\")\n\n program.addCommand(\n createAuthCommand({\n login: useCases.login,\n logout: useCases.logout,\n getStatus: useCases.getAuthStatus,\n }),\n )\n\n program.addCommand(createGenerateCommand(useCases.generateImage))\n program.addCommand(createEditCommand(useCases.editImage))\n\n return program\n}\n","import { EditImageUseCase } from \"./application/usecases/edit-image.usecase.js\"\nimport { GenerateImageUseCase } from \"./application/usecases/generate-image.usecase.js\"\nimport { GetAuthStatusUseCase } from \"./application/usecases/get-auth-status.usecase.js\"\nimport { LoginUseCase } from \"./application/usecases/login.usecase.js\"\nimport { LogoutUseCase } from \"./application/usecases/logout.usecase.js\"\nimport { FileStorageAdapter } from \"./infrastructure/adapters/file-storage.adapter.js\"\nimport { GrokApiAdapter } from \"./infrastructure/adapters/grok-api.adapter.js\"\nimport { KeychainAdapter } from \"./infrastructure/adapters/keychain.adapter.js\"\nimport { createCli } from \"./presentation/cli.js\"\n\nconst keyStore = new KeychainAdapter()\nconst imageGenerator = new GrokApiAdapter()\nconst fileStorage = new FileStorageAdapter()\n\nconst generateImageUseCase = new GenerateImageUseCase(\n imageGenerator,\n keyStore,\n fileStorage,\n)\nconst editImageUseCase = new EditImageUseCase(imageGenerator, keyStore, fileStorage)\nconst loginUseCase = new LoginUseCase(keyStore)\nconst logoutUseCase = new LogoutUseCase(keyStore)\nconst getAuthStatusUseCase = new GetAuthStatusUseCase(keyStore)\n\nconst program = createCli({\n generateImage: generateImageUseCase,\n editImage: editImageUseCase,\n login: loginUseCase,\n logout: logoutUseCase,\n getAuthStatus: getAuthStatusUseCase,\n})\n\nprogram.parse()\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"main.mjs","names":["VALID_MODELS","VALID_RATIOS"],"sources":["../src/domain/errors.ts","../src/application/usecases/edit-image.usecase.ts","../src/application/usecases/generate-image.usecase.ts","../src/application/usecases/get-auth-status.usecase.ts","../src/application/usecases/login.usecase.ts","../src/application/usecases/logout.usecase.ts","../src/infrastructure/adapters/env-key-store.adapter.ts","../src/infrastructure/adapters/file-storage.adapter.ts","../src/infrastructure/adapters/grok-api.adapter.ts","../src/infrastructure/adapters/keychain.adapter.ts","../src/infrastructure/adapters/pass.adapter.ts","../src/infrastructure/key-store-chain.ts","../src/presentation/commands/auth.command.ts","../src/presentation/commands/edit.command.ts","../src/presentation/commands/generate.command.ts","../src/presentation/cli.ts","../src/main.ts"],"sourcesContent":["export class ApiKeyMissingError extends Error {\n constructor() {\n super(\n \"API key not found. Run `grok-img auth login` or set XAI_API_KEY environment variable.\",\n )\n this.name = \"ApiKeyMissingError\"\n }\n}\n\nexport class ApiError extends Error {\n constructor(\n message: string,\n public readonly cause?: unknown,\n ) {\n super(message)\n this.name = \"ApiError\"\n }\n}\n\nexport class ImageNotFoundError extends Error {\n constructor(path: string) {\n super(`Image not found: ${path}`)\n this.name = \"ImageNotFoundError\"\n }\n}\n","import type { EditParams } from \"../../domain/entities/edit-params.js\"\nimport { ApiKeyMissingError } from \"../../domain/errors.js\"\nimport type { FileStoragePort } from \"../../domain/ports/file-storage.port.js\"\nimport type { ImageGeneratorPort } from \"../../domain/ports/image-generator.port.js\"\nimport type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport class EditImageUseCase {\n constructor(\n private readonly imageGenerator: ImageGeneratorPort,\n private readonly keyStore: KeyStorePort,\n private readonly fileStorage: FileStoragePort,\n ) {}\n\n async execute(params: EditParams, outputDir: string): Promise<string> {\n const apiKey = await this.keyStore.get()\n if (!apiKey) throw new ApiKeyMissingError()\n\n let imageSource = params.imageSource\n if (typeof imageSource === \"string\" && !imageSource.startsWith(\"http\")) {\n imageSource = await this.fileStorage.readImage(imageSource)\n }\n\n this.fileStorage.ensureDir(outputDir)\n\n const result = await this.imageGenerator.edit({ ...params, imageSource }, apiKey)\n\n const outputPath = this.fileStorage.generateOutputPath(outputDir, 0, result.mediaType)\n return this.fileStorage.saveImage(result.uint8Array, outputPath)\n }\n}\n","import type { GenerateParams } from \"../../domain/entities/generate-params.js\"\nimport { ApiKeyMissingError } from \"../../domain/errors.js\"\nimport type { FileStoragePort } from \"../../domain/ports/file-storage.port.js\"\nimport type { ImageGeneratorPort } from \"../../domain/ports/image-generator.port.js\"\nimport type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport class GenerateImageUseCase {\n constructor(\n private readonly imageGenerator: ImageGeneratorPort,\n private readonly keyStore: KeyStorePort,\n private readonly fileStorage: FileStoragePort,\n ) {}\n\n async execute(params: GenerateParams, outputDir: string): Promise<string[]> {\n const apiKey = await this.keyStore.get()\n if (!apiKey) throw new ApiKeyMissingError()\n\n this.fileStorage.ensureDir(outputDir)\n\n const results = await this.imageGenerator.generate(params, apiKey)\n\n const savedPaths: string[] = []\n for (let i = 0; i < results.length; i++) {\n const result = results[i]\n const outputPath = this.fileStorage.generateOutputPath(\n outputDir,\n i,\n result.mediaType,\n )\n const saved = await this.fileStorage.saveImage(result.uint8Array, outputPath)\n savedPaths.push(saved)\n }\n\n return savedPaths\n }\n}\n","import type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport type AuthStatus = {\n authenticated: boolean\n maskedKey: string | null\n source: \"credential-store\" | \"env\" | null\n}\n\nexport class GetAuthStatusUseCase {\n constructor(private readonly keyStore: KeyStorePort) {}\n\n async execute(): Promise<AuthStatus> {\n const key = await this.keyStore.get()\n\n if (!key) {\n return { authenticated: false, maskedKey: null, source: null }\n }\n\n const masked =\n key.length > 8\n ? `${key.slice(0, 4)}${\"*\".repeat(key.length - 8)}${key.slice(-4)}`\n : \"****\"\n\n const source = process.env.XAI_API_KEY === key ? \"env\" : \"credential-store\"\n\n return { authenticated: true, maskedKey: masked, source }\n }\n}\n","import type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport class LoginUseCase {\n constructor(private readonly keyStore: KeyStorePort) {}\n\n async execute(apiKey: string): Promise<void> {\n await this.keyStore.save(apiKey)\n }\n}\n","import type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport class LogoutUseCase {\n constructor(private readonly keyStore: KeyStorePort) {}\n\n async execute(): Promise<void> {\n await this.keyStore.remove()\n }\n}\n","import type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport class EnvKeyStoreAdapter implements KeyStorePort {\n async get(): Promise<string | null> {\n return process.env.XAI_API_KEY ?? null\n }\n\n async save(_key: string): Promise<void> {\n throw new Error(\"XAI_API_KEY is a read-only environment variable\")\n }\n\n async remove(): Promise<void> {\n // no-op: environment variables cannot be deleted programmatically\n }\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport { ImageNotFoundError } from \"../../domain/errors.js\"\nimport type { FileStoragePort } from \"../../domain/ports/file-storage.port.js\"\n\nconst MEDIA_TYPE_EXT: Record<string, string> = {\n \"image/png\": \"png\",\n \"image/jpeg\": \"jpg\",\n \"image/webp\": \"webp\",\n \"image/gif\": \"gif\",\n}\n\nexport class FileStorageAdapter implements FileStoragePort {\n async saveImage(data: Uint8Array, outputPath: string): Promise<string> {\n writeFileSync(outputPath, data)\n return outputPath\n }\n\n async readImage(filePath: string): Promise<Uint8Array> {\n if (!existsSync(filePath)) {\n throw new ImageNotFoundError(filePath)\n }\n return new Uint8Array(readFileSync(filePath))\n }\n\n ensureDir(dir: string): void {\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true })\n }\n }\n\n generateOutputPath(dir: string, index: number, mediaType: string): string {\n const ext = MEDIA_TYPE_EXT[mediaType] ?? \"png\"\n const timestamp = Date.now()\n return join(dir, `grok-img-${timestamp}-${index}.${ext}`)\n }\n}\n","import { createXai } from \"@ai-sdk/xai\"\nimport { generateImage, NoImageGeneratedError } from \"ai\"\nimport type { EditParams } from \"../../domain/entities/edit-params.js\"\nimport type { GenerateParams } from \"../../domain/entities/generate-params.js\"\nimport type { ImageResult } from \"../../domain/entities/image-result.js\"\nimport { ApiError } from \"../../domain/errors.js\"\nimport type { ImageGeneratorPort } from \"../../domain/ports/image-generator.port.js\"\n\nconst DEFAULT_MODEL = \"grok-imagine-image\"\n\nexport class GrokApiAdapter implements ImageGeneratorPort {\n async generate(params: GenerateParams, apiKey: string): Promise<ImageResult[]> {\n const xai = createXai({ apiKey })\n\n try {\n const { images } = await generateImage({\n model: xai.image(params.model ?? DEFAULT_MODEL),\n prompt: params.prompt,\n aspectRatio: params.aspectRatio as `${number}:${number}`,\n n: params.count,\n })\n\n return images.map((img) => ({\n base64: img.base64,\n uint8Array: img.uint8Array,\n mediaType: img.mediaType,\n }))\n } catch (error) {\n if (NoImageGeneratedError.isInstance(error)) {\n throw new ApiError(\n \"Image generation failed: the model could not produce an image.\",\n error,\n )\n }\n if (error instanceof Error) {\n throw new ApiError(`API request failed: ${error.message}`, error)\n }\n throw new ApiError(\"An unexpected error occurred during image generation.\", error)\n }\n }\n\n async edit(params: EditParams, apiKey: string): Promise<ImageResult> {\n const xai = createXai({ apiKey })\n\n try {\n const { image } = await generateImage({\n model: xai.image(params.model ?? DEFAULT_MODEL),\n prompt: {\n text: params.prompt,\n images: [params.imageSource],\n },\n aspectRatio: params.aspectRatio as `${number}:${number}`,\n })\n\n return {\n base64: image.base64,\n uint8Array: image.uint8Array,\n mediaType: image.mediaType,\n }\n } catch (error) {\n if (NoImageGeneratedError.isInstance(error)) {\n throw new ApiError(\n \"Image editing failed: the model could not produce an image.\",\n error,\n )\n }\n if (error instanceof Error) {\n throw new ApiError(`API request failed: ${error.message}`, error)\n }\n throw new ApiError(\"An unexpected error occurred during image editing.\", error)\n }\n }\n}\n","import { deletePassword, getPassword, setPassword } from \"cross-keychain\"\nimport type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nconst SERVICE = \"grok-image-cli\"\nconst ACCOUNT = \"api-key\"\n\nexport class KeychainAdapter implements KeyStorePort {\n async save(key: string): Promise<void> {\n await setPassword(SERVICE, ACCOUNT, key)\n }\n\n async get(): Promise<string | null> {\n const key = await getPassword(SERVICE, ACCOUNT)\n return key ?? null\n }\n\n async remove(): Promise<void> {\n await deletePassword(SERVICE, ACCOUNT)\n }\n}\n","import { execFileSync } from \"node:child_process\"\nimport type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nconst SECRET_PATH = \"grok-image-cli/api-key\"\n\nfunction detectTool(): string | null {\n for (const tool of [\"gopass\", \"pass\"]) {\n try {\n execFileSync(\"which\", [tool], { stdio: \"ignore\" })\n return tool\n } catch {}\n }\n return null\n}\n\nconst tool = detectTool()\n\nexport class PassAdapter implements KeyStorePort {\n async save(key: string): Promise<void> {\n if (!tool) throw new Error(\"Neither gopass nor pass is installed\")\n\n const args =\n tool === \"gopass\"\n ? [\"insert\", \"-f\", SECRET_PATH]\n : [\"insert\", \"-m\", \"--force\", SECRET_PATH]\n\n execFileSync(tool, args, { input: `${key}\\n`, stdio: [\"pipe\", \"ignore\", \"ignore\"] })\n }\n\n async get(): Promise<string | null> {\n if (!tool) throw new Error(\"Neither gopass nor pass is installed\")\n\n try {\n const output = execFileSync(tool, [\"show\", SECRET_PATH], {\n encoding: \"utf-8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n })\n return output.trim().split(\"\\n\")[0] ?? null\n } catch {\n return null\n }\n }\n\n async remove(): Promise<void> {\n if (!tool) throw new Error(\"Neither gopass nor pass is installed\")\n\n try {\n execFileSync(tool, [\"rm\", \"--force\", SECRET_PATH], { stdio: \"ignore\" })\n } catch {}\n }\n}\n","import type { KeyStorePort } from \"../domain/ports/key-store.port.js\"\n\ntype NamedStore = { store: KeyStorePort; name: string }\n\nexport class KeyStoreChain implements KeyStorePort {\n constructor(\n private readonly stores: ReadonlyArray<NamedStore>,\n private readonly onSave?: (storeName: string) => void,\n ) {}\n\n async save(key: string): Promise<void> {\n let lastError: unknown\n for (const { store, name } of this.stores) {\n try {\n await store.save(key)\n this.onSave?.(name)\n return\n } catch (e) {\n lastError = e\n }\n }\n throw lastError\n }\n\n async get(): Promise<string | null> {\n for (const { store } of this.stores) {\n try {\n const key = await store.get()\n if (key) return key\n } catch {}\n }\n return null\n }\n\n async remove(): Promise<void> {\n for (const { store } of this.stores) {\n try {\n await store.remove()\n } catch {}\n }\n }\n}\n","import { createInterface } from \"node:readline\"\nimport chalk from \"chalk\"\nimport { Command } from \"commander\"\nimport type { GetAuthStatusUseCase } from \"../../application/usecases/get-auth-status.usecase.js\"\nimport type { LoginUseCase } from \"../../application/usecases/login.usecase.js\"\nimport type { LogoutUseCase } from \"../../application/usecases/logout.usecase.js\"\n\nfunction readInput(prompt: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n })\n\n return new Promise((resolve) => {\n rl.question(prompt, (answer) => {\n rl.close()\n resolve(answer.trim())\n })\n })\n}\n\nexport function createAuthCommand(useCases: {\n login: LoginUseCase\n logout: LogoutUseCase\n getStatus: GetAuthStatusUseCase\n}): Command {\n const auth = new Command(\"auth\").description(\"Manage API key authentication\")\n\n auth\n .command(\"login\")\n .description(\"Store your xAI API key in the system credential store\")\n .action(async () => {\n try {\n const apiKey = await readInput(chalk.cyan(\"Enter your xAI API key: \"))\n\n if (!apiKey) {\n console.log(chalk.red(\"No API key provided.\"))\n process.exit(1)\n }\n\n await useCases.login.execute(apiKey)\n console.log(chalk.green(\"API key saved successfully.\"))\n } catch (error) {\n console.error(\n chalk.red(\n `Failed to save API key: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n ),\n )\n process.exit(1)\n }\n })\n\n auth\n .command(\"logout\")\n .description(\"Remove your xAI API key from the system credential store\")\n .action(async () => {\n try {\n await useCases.logout.execute()\n console.log(chalk.green(\"API key removed successfully.\"))\n } catch (error) {\n console.error(\n chalk.red(\n `Failed to remove API key: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n ),\n )\n process.exit(1)\n }\n })\n\n auth\n .command(\"status\")\n .description(\"Check current authentication status\")\n .action(async () => {\n try {\n const status = await useCases.getStatus.execute()\n\n if (status.authenticated) {\n console.log(chalk.green(\"Authenticated\"))\n console.log(chalk.dim(` Key: ${status.maskedKey}`))\n console.log(chalk.dim(` Source: ${status.source}`))\n } else {\n console.log(chalk.yellow(\"Not authenticated\"))\n console.log(\n chalk.dim(\n \" Run `grok-img auth login` or set XAI_API_KEY environment variable.\",\n ),\n )\n }\n } catch (error) {\n console.error(\n chalk.red(\n `Failed to check status: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n ),\n )\n process.exit(1)\n }\n })\n\n return auth\n}\n","import { resolve } from \"node:path\"\nimport chalk from \"chalk\"\nimport { Command } from \"commander\"\nimport ora from \"ora\"\nimport type { EditImageUseCase } from \"../../application/usecases/edit-image.usecase.js\"\nimport type { Model } from \"../../domain/entities/generate-params.js\"\nimport { ApiError, ApiKeyMissingError, ImageNotFoundError } from \"../../domain/errors.js\"\n\nconst VALID_MODELS: Model[] = [\n \"grok-2-image-1212\",\n \"grok-imagine-image-pro\",\n \"grok-imagine-image\",\n]\n\nconst VALID_RATIOS = [\n \"1:1\",\n \"16:9\",\n \"9:16\",\n \"4:3\",\n \"3:4\",\n \"3:2\",\n \"2:3\",\n \"2:1\",\n \"1:2\",\n \"19.5:9\",\n \"9:19.5\",\n \"20:9\",\n \"9:20\",\n \"auto\",\n]\n\nexport function createEditCommand(editUseCase: EditImageUseCase): Command {\n return new Command(\"edit\")\n .description(\"Edit an existing image with a text prompt\")\n .argument(\"<prompt>\", \"Text prompt describing the edit to apply\")\n .requiredOption(\"-i, --image <path>\", \"Source image (local file path or URL)\")\n .option(\"-m, --model <model>\", \"Model to use\", \"grok-imagine-image\")\n .option(\"-a, --aspect-ratio <ratio>\", \"Aspect ratio\", \"auto\")\n .option(\"-o, --output <dir>\", \"Output directory\", \"./grok-images\")\n .action(async (prompt: string, options) => {\n if (!VALID_MODELS.includes(options.model)) {\n console.error(\n chalk.red(`Invalid model. Valid options: ${VALID_MODELS.join(\", \")}`),\n )\n process.exit(1)\n }\n\n if (!VALID_RATIOS.includes(options.aspectRatio)) {\n console.error(\n chalk.red(`Invalid aspect ratio. Valid options: ${VALID_RATIOS.join(\", \")}`),\n )\n process.exit(1)\n }\n\n const imageSource = options.image.startsWith(\"http\")\n ? options.image\n : resolve(options.image)\n\n const outputDir = resolve(options.output)\n const spinner = ora(\"Editing image...\").start()\n\n try {\n const savedPath = await editUseCase.execute(\n { prompt, imageSource, aspectRatio: options.aspectRatio, model: options.model },\n outputDir,\n )\n\n spinner.succeed(chalk.green(\"Image edited successfully:\"))\n console.log(chalk.dim(` ${savedPath}`))\n } catch (error) {\n spinner.fail()\n if (error instanceof ApiKeyMissingError) {\n console.error(chalk.red(error.message))\n } else if (error instanceof ImageNotFoundError) {\n console.error(chalk.red(error.message))\n } else if (error instanceof ApiError) {\n console.error(chalk.red(`API Error: ${error.message}`))\n } else {\n console.error(\n chalk.red(\n `Unexpected error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n ),\n )\n }\n process.exit(1)\n }\n })\n}\n","import { resolve } from \"node:path\"\nimport chalk from \"chalk\"\nimport { Command } from \"commander\"\nimport ora from \"ora\"\nimport type { GenerateImageUseCase } from \"../../application/usecases/generate-image.usecase.js\"\nimport type { Model } from \"../../domain/entities/generate-params.js\"\nimport { ApiError, ApiKeyMissingError } from \"../../domain/errors.js\"\n\nconst VALID_MODELS: Model[] = [\n \"grok-2-image-1212\",\n \"grok-imagine-image-pro\",\n \"grok-imagine-image\",\n]\n\nconst VALID_RATIOS = [\n \"1:1\",\n \"16:9\",\n \"9:16\",\n \"4:3\",\n \"3:4\",\n \"3:2\",\n \"2:3\",\n \"2:1\",\n \"1:2\",\n \"19.5:9\",\n \"9:19.5\",\n \"20:9\",\n \"9:20\",\n \"auto\",\n]\n\nexport function createGenerateCommand(generateUseCase: GenerateImageUseCase): Command {\n return new Command(\"generate\")\n .description(\"Generate images from a text prompt\")\n .argument(\"<prompt>\", \"Text prompt describing the image to generate\")\n .option(\"-m, --model <model>\", \"Model to use\", \"grok-imagine-image\")\n .option(\"-a, --aspect-ratio <ratio>\", \"Aspect ratio\", \"auto\")\n .option(\"-n, --count <number>\", \"Number of images (1-10)\", \"1\")\n .option(\"-o, --output <dir>\", \"Output directory\", \"./grok-images\")\n .action(async (prompt: string, options) => {\n const count = parseInt(options.count, 10)\n if (Number.isNaN(count) || count < 1 || count > 10) {\n console.error(chalk.red(\"Count must be a number between 1 and 10.\"))\n process.exit(1)\n }\n\n if (!VALID_MODELS.includes(options.model)) {\n console.error(\n chalk.red(`Invalid model. Valid options: ${VALID_MODELS.join(\", \")}`),\n )\n process.exit(1)\n }\n\n if (!VALID_RATIOS.includes(options.aspectRatio)) {\n console.error(\n chalk.red(`Invalid aspect ratio. Valid options: ${VALID_RATIOS.join(\", \")}`),\n )\n process.exit(1)\n }\n\n const outputDir = resolve(options.output)\n const spinner = ora(`Generating ${count} image${count > 1 ? \"s\" : \"\"}...`).start()\n\n try {\n const paths = await generateUseCase.execute(\n { prompt, count, aspectRatio: options.aspectRatio, model: options.model },\n outputDir,\n )\n\n spinner.succeed(\n chalk.green(`Generated ${paths.length} image${paths.length > 1 ? \"s\" : \"\"}:`),\n )\n for (const p of paths) {\n console.log(chalk.dim(` ${p}`))\n }\n } catch (error) {\n spinner.fail()\n if (error instanceof ApiKeyMissingError) {\n console.error(chalk.red(error.message))\n } else if (error instanceof ApiError) {\n console.error(chalk.red(`API Error: ${error.message}`))\n } else {\n console.error(\n chalk.red(\n `Unexpected error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n ),\n )\n }\n process.exit(1)\n }\n })\n}\n","import { Command } from \"commander\"\nimport type { EditImageUseCase } from \"../application/usecases/edit-image.usecase.js\"\nimport type { GenerateImageUseCase } from \"../application/usecases/generate-image.usecase.js\"\nimport type { GetAuthStatusUseCase } from \"../application/usecases/get-auth-status.usecase.js\"\nimport type { LoginUseCase } from \"../application/usecases/login.usecase.js\"\nimport type { LogoutUseCase } from \"../application/usecases/logout.usecase.js\"\nimport { createAuthCommand } from \"./commands/auth.command.js\"\nimport { createEditCommand } from \"./commands/edit.command.js\"\nimport { createGenerateCommand } from \"./commands/generate.command.js\"\n\nexport type UseCases = {\n generateImage: GenerateImageUseCase\n editImage: EditImageUseCase\n login: LoginUseCase\n logout: LogoutUseCase\n getAuthStatus: GetAuthStatusUseCase\n}\n\nexport function createCli(useCases: UseCases): Command {\n const program = new Command()\n .name(\"grok-img\")\n .description(\"CLI for generating and editing images with Grok API\")\n .version(\"1.0.0\")\n\n program.addCommand(\n createAuthCommand({\n login: useCases.login,\n logout: useCases.logout,\n getStatus: useCases.getAuthStatus,\n }),\n )\n\n program.addCommand(createGenerateCommand(useCases.generateImage))\n program.addCommand(createEditCommand(useCases.editImage))\n\n return program\n}\n","import chalk from \"chalk\"\nimport { EditImageUseCase } from \"./application/usecases/edit-image.usecase.js\"\nimport { GenerateImageUseCase } from \"./application/usecases/generate-image.usecase.js\"\nimport { GetAuthStatusUseCase } from \"./application/usecases/get-auth-status.usecase.js\"\nimport { LoginUseCase } from \"./application/usecases/login.usecase.js\"\nimport { LogoutUseCase } from \"./application/usecases/logout.usecase.js\"\nimport { EnvKeyStoreAdapter } from \"./infrastructure/adapters/env-key-store.adapter.js\"\nimport { FileStorageAdapter } from \"./infrastructure/adapters/file-storage.adapter.js\"\nimport { GrokApiAdapter } from \"./infrastructure/adapters/grok-api.adapter.js\"\nimport { KeychainAdapter } from \"./infrastructure/adapters/keychain.adapter.js\"\nimport { PassAdapter } from \"./infrastructure/adapters/pass.adapter.js\"\nimport { KeyStoreChain } from \"./infrastructure/key-store-chain.js\"\nimport { createCli } from \"./presentation/cli.js\"\n\nconst keyStore = new KeyStoreChain(\n [\n { store: new KeychainAdapter(), name: \"Keychain\" },\n { store: new PassAdapter(), name: \"pass\" },\n { store: new EnvKeyStoreAdapter(), name: \"XAI_API_KEY\" },\n ],\n (name) => console.log(chalk.dim(`Key stored in: ${name}`)),\n)\nconst imageGenerator = new GrokApiAdapter()\nconst fileStorage = new FileStorageAdapter()\n\nconst generateImageUseCase = new GenerateImageUseCase(\n imageGenerator,\n keyStore,\n fileStorage,\n)\nconst editImageUseCase = new EditImageUseCase(imageGenerator, keyStore, fileStorage)\nconst loginUseCase = new LoginUseCase(keyStore)\nconst logoutUseCase = new LogoutUseCase(keyStore)\nconst getAuthStatusUseCase = new GetAuthStatusUseCase(keyStore)\n\nconst program = createCli({\n generateImage: generateImageUseCase,\n editImage: editImageUseCase,\n login: loginUseCase,\n logout: logoutUseCase,\n getAuthStatus: getAuthStatusUseCase,\n})\n\nprogram.parse()\n"],"mappings":";;;;;;;;;;;;;AAAA,IAAa,qBAAb,cAAwC,MAAM;CAC5C,cAAc;AACZ,QACE,wFACD;AACD,OAAK,OAAO;;;AAIhB,IAAa,WAAb,cAA8B,MAAM;CAClC,YACE,SACA,AAAgB,OAChB;AACA,QAAM,QAAQ;EAFE;AAGhB,OAAK,OAAO;;;AAIhB,IAAa,qBAAb,cAAwC,MAAM;CAC5C,YAAY,MAAc;AACxB,QAAM,oBAAoB,OAAO;AACjC,OAAK,OAAO;;;;;;AChBhB,IAAa,mBAAb,MAA8B;CAC5B,YACE,AAAiB,gBACjB,AAAiB,UACjB,AAAiB,aACjB;EAHiB;EACA;EACA;;CAGnB,MAAM,QAAQ,QAAoB,WAAoC;EACpE,MAAM,SAAS,MAAM,KAAK,SAAS,KAAK;AACxC,MAAI,CAAC,OAAQ,OAAM,IAAI,oBAAoB;EAE3C,IAAI,cAAc,OAAO;AACzB,MAAI,OAAO,gBAAgB,YAAY,CAAC,YAAY,WAAW,OAAO,CACpE,eAAc,MAAM,KAAK,YAAY,UAAU,YAAY;AAG7D,OAAK,YAAY,UAAU,UAAU;EAErC,MAAM,SAAS,MAAM,KAAK,eAAe,KAAK;GAAE,GAAG;GAAQ;GAAa,EAAE,OAAO;EAEjF,MAAM,aAAa,KAAK,YAAY,mBAAmB,WAAW,GAAG,OAAO,UAAU;AACtF,SAAO,KAAK,YAAY,UAAU,OAAO,YAAY,WAAW;;;;;;ACrBpE,IAAa,uBAAb,MAAkC;CAChC,YACE,AAAiB,gBACjB,AAAiB,UACjB,AAAiB,aACjB;EAHiB;EACA;EACA;;CAGnB,MAAM,QAAQ,QAAwB,WAAsC;EAC1E,MAAM,SAAS,MAAM,KAAK,SAAS,KAAK;AACxC,MAAI,CAAC,OAAQ,OAAM,IAAI,oBAAoB;AAE3C,OAAK,YAAY,UAAU,UAAU;EAErC,MAAM,UAAU,MAAM,KAAK,eAAe,SAAS,QAAQ,OAAO;EAElE,MAAM,aAAuB,EAAE;AAC/B,OAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;GACvC,MAAM,SAAS,QAAQ;GACvB,MAAM,aAAa,KAAK,YAAY,mBAClC,WACA,GACA,OAAO,UACR;GACD,MAAM,QAAQ,MAAM,KAAK,YAAY,UAAU,OAAO,YAAY,WAAW;AAC7E,cAAW,KAAK,MAAM;;AAGxB,SAAO;;;;;;ACzBX,IAAa,uBAAb,MAAkC;CAChC,YAAY,AAAiB,UAAwB;EAAxB;;CAE7B,MAAM,UAA+B;EACnC,MAAM,MAAM,MAAM,KAAK,SAAS,KAAK;AAErC,MAAI,CAAC,IACH,QAAO;GAAE,eAAe;GAAO,WAAW;GAAM,QAAQ;GAAM;AAUhE,SAAO;GAAE,eAAe;GAAM,WAN5B,IAAI,SAAS,IACT,GAAG,IAAI,MAAM,GAAG,EAAE,GAAG,IAAI,OAAO,IAAI,SAAS,EAAE,GAAG,IAAI,MAAM,GAAG,KAC/D;GAI2C,QAFlC,QAAQ,IAAI,gBAAgB,MAAM,QAAQ;GAEA;;;;;;ACvB7D,IAAa,eAAb,MAA0B;CACxB,YAAY,AAAiB,UAAwB;EAAxB;;CAE7B,MAAM,QAAQ,QAA+B;AAC3C,QAAM,KAAK,SAAS,KAAK,OAAO;;;;;;ACJpC,IAAa,gBAAb,MAA2B;CACzB,YAAY,AAAiB,UAAwB;EAAxB;;CAE7B,MAAM,UAAyB;AAC7B,QAAM,KAAK,SAAS,QAAQ;;;;;;ACJhC,IAAa,qBAAb,MAAwD;CACtD,MAAM,MAA8B;AAClC,SAAO,QAAQ,IAAI,eAAe;;CAGpC,MAAM,KAAK,MAA6B;AACtC,QAAM,IAAI,MAAM,kDAAkD;;CAGpE,MAAM,SAAwB;;;;;ACNhC,MAAM,iBAAyC;CAC7C,aAAa;CACb,cAAc;CACd,cAAc;CACd,aAAa;CACd;AAED,IAAa,qBAAb,MAA2D;CACzD,MAAM,UAAU,MAAkB,YAAqC;AACrE,gBAAc,YAAY,KAAK;AAC/B,SAAO;;CAGT,MAAM,UAAU,UAAuC;AACrD,MAAI,CAAC,WAAW,SAAS,CACvB,OAAM,IAAI,mBAAmB,SAAS;AAExC,SAAO,IAAI,WAAW,aAAa,SAAS,CAAC;;CAG/C,UAAU,KAAmB;AAC3B,MAAI,CAAC,WAAW,IAAI,CAClB,WAAU,KAAK,EAAE,WAAW,MAAM,CAAC;;CAIvC,mBAAmB,KAAa,OAAe,WAA2B;EACxE,MAAM,MAAM,eAAe,cAAc;AAEzC,SAAO,KAAK,KAAK,YADC,KAAK,KAAK,CACW,GAAG,MAAM,GAAG,MAAM;;;;;;AC1B7D,MAAM,gBAAgB;AAEtB,IAAa,iBAAb,MAA0D;CACxD,MAAM,SAAS,QAAwB,QAAwC;EAC7E,MAAM,MAAM,UAAU,EAAE,QAAQ,CAAC;AAEjC,MAAI;GACF,MAAM,EAAE,WAAW,MAAM,cAAc;IACrC,OAAO,IAAI,MAAM,OAAO,SAAS,cAAc;IAC/C,QAAQ,OAAO;IACf,aAAa,OAAO;IACpB,GAAG,OAAO;IACX,CAAC;AAEF,UAAO,OAAO,KAAK,SAAS;IAC1B,QAAQ,IAAI;IACZ,YAAY,IAAI;IAChB,WAAW,IAAI;IAChB,EAAE;WACI,OAAO;AACd,OAAI,sBAAsB,WAAW,MAAM,CACzC,OAAM,IAAI,SACR,kEACA,MACD;AAEH,OAAI,iBAAiB,MACnB,OAAM,IAAI,SAAS,uBAAuB,MAAM,WAAW,MAAM;AAEnE,SAAM,IAAI,SAAS,yDAAyD,MAAM;;;CAItF,MAAM,KAAK,QAAoB,QAAsC;EACnE,MAAM,MAAM,UAAU,EAAE,QAAQ,CAAC;AAEjC,MAAI;GACF,MAAM,EAAE,UAAU,MAAM,cAAc;IACpC,OAAO,IAAI,MAAM,OAAO,SAAS,cAAc;IAC/C,QAAQ;KACN,MAAM,OAAO;KACb,QAAQ,CAAC,OAAO,YAAY;KAC7B;IACD,aAAa,OAAO;IACrB,CAAC;AAEF,UAAO;IACL,QAAQ,MAAM;IACd,YAAY,MAAM;IAClB,WAAW,MAAM;IAClB;WACM,OAAO;AACd,OAAI,sBAAsB,WAAW,MAAM,CACzC,OAAM,IAAI,SACR,+DACA,MACD;AAEH,OAAI,iBAAiB,MACnB,OAAM,IAAI,SAAS,uBAAuB,MAAM,WAAW,MAAM;AAEnE,SAAM,IAAI,SAAS,sDAAsD,MAAM;;;;;;;AClErF,MAAM,UAAU;AAChB,MAAM,UAAU;AAEhB,IAAa,kBAAb,MAAqD;CACnD,MAAM,KAAK,KAA4B;AACrC,QAAM,YAAY,SAAS,SAAS,IAAI;;CAG1C,MAAM,MAA8B;AAElC,SADY,MAAM,YAAY,SAAS,QAAQ,IACjC;;CAGhB,MAAM,SAAwB;AAC5B,QAAM,eAAe,SAAS,QAAQ;;;;;;ACd1C,MAAM,cAAc;AAEpB,SAAS,aAA4B;AACnC,MAAK,MAAM,QAAQ,CAAC,UAAU,OAAO,CACnC,KAAI;AACF,eAAa,SAAS,CAAC,KAAK,EAAE,EAAE,OAAO,UAAU,CAAC;AAClD,SAAO;SACD;AAEV,QAAO;;AAGT,MAAM,OAAO,YAAY;AAEzB,IAAa,cAAb,MAAiD;CAC/C,MAAM,KAAK,KAA4B;AACrC,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,uCAAuC;AAOlE,eAAa,MAJX,SAAS,WACL;GAAC;GAAU;GAAM;GAAY,GAC7B;GAAC;GAAU;GAAM;GAAW;GAAY,EAErB;GAAE,OAAO,GAAG,IAAI;GAAK,OAAO;IAAC;IAAQ;IAAU;IAAS;GAAE,CAAC;;CAGtF,MAAM,MAA8B;AAClC,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,uCAAuC;AAElE,MAAI;AAKF,UAJe,aAAa,MAAM,CAAC,QAAQ,YAAY,EAAE;IACvD,UAAU;IACV,OAAO;KAAC;KAAU;KAAQ;KAAS;IACpC,CAAC,CACY,MAAM,CAAC,MAAM,KAAK,CAAC,MAAM;UACjC;AACN,UAAO;;;CAIX,MAAM,SAAwB;AAC5B,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,uCAAuC;AAElE,MAAI;AACF,gBAAa,MAAM;IAAC;IAAM;IAAW;IAAY,EAAE,EAAE,OAAO,UAAU,CAAC;UACjE;;;;;;AC5CZ,IAAa,gBAAb,MAAmD;CACjD,YACE,AAAiB,QACjB,AAAiB,QACjB;EAFiB;EACA;;CAGnB,MAAM,KAAK,KAA4B;EACrC,IAAI;AACJ,OAAK,MAAM,EAAE,OAAO,UAAU,KAAK,OACjC,KAAI;AACF,SAAM,MAAM,KAAK,IAAI;AACrB,QAAK,SAAS,KAAK;AACnB;WACO,GAAG;AACV,eAAY;;AAGhB,QAAM;;CAGR,MAAM,MAA8B;AAClC,OAAK,MAAM,EAAE,WAAW,KAAK,OAC3B,KAAI;GACF,MAAM,MAAM,MAAM,MAAM,KAAK;AAC7B,OAAI,IAAK,QAAO;UACV;AAEV,SAAO;;CAGT,MAAM,SAAwB;AAC5B,OAAK,MAAM,EAAE,WAAW,KAAK,OAC3B,KAAI;AACF,SAAM,MAAM,QAAQ;UACd;;;;;;AC/Bd,SAAS,UAAU,QAAiC;CAClD,MAAM,KAAK,gBAAgB;EACzB,OAAO,QAAQ;EACf,QAAQ,QAAQ;EACjB,CAAC;AAEF,QAAO,IAAI,SAAS,YAAY;AAC9B,KAAG,SAAS,SAAS,WAAW;AAC9B,MAAG,OAAO;AACV,WAAQ,OAAO,MAAM,CAAC;IACtB;GACF;;AAGJ,SAAgB,kBAAkB,UAItB;CACV,MAAM,OAAO,IAAI,QAAQ,OAAO,CAAC,YAAY,gCAAgC;AAE7E,MACG,QAAQ,QAAQ,CAChB,YAAY,wDAAwD,CACpE,OAAO,YAAY;AAClB,MAAI;GACF,MAAM,SAAS,MAAM,UAAU,MAAM,KAAK,2BAA2B,CAAC;AAEtE,OAAI,CAAC,QAAQ;AACX,YAAQ,IAAI,MAAM,IAAI,uBAAuB,CAAC;AAC9C,YAAQ,KAAK,EAAE;;AAGjB,SAAM,SAAS,MAAM,QAAQ,OAAO;AACpC,WAAQ,IAAI,MAAM,MAAM,8BAA8B,CAAC;WAChD,OAAO;AACd,WAAQ,MACN,MAAM,IACJ,2BAA2B,iBAAiB,QAAQ,MAAM,UAAU,kBACrE,CACF;AACD,WAAQ,KAAK,EAAE;;GAEjB;AAEJ,MACG,QAAQ,SAAS,CACjB,YAAY,2DAA2D,CACvE,OAAO,YAAY;AAClB,MAAI;AACF,SAAM,SAAS,OAAO,SAAS;AAC/B,WAAQ,IAAI,MAAM,MAAM,gCAAgC,CAAC;WAClD,OAAO;AACd,WAAQ,MACN,MAAM,IACJ,6BAA6B,iBAAiB,QAAQ,MAAM,UAAU,kBACvE,CACF;AACD,WAAQ,KAAK,EAAE;;GAEjB;AAEJ,MACG,QAAQ,SAAS,CACjB,YAAY,sCAAsC,CAClD,OAAO,YAAY;AAClB,MAAI;GACF,MAAM,SAAS,MAAM,SAAS,UAAU,SAAS;AAEjD,OAAI,OAAO,eAAe;AACxB,YAAQ,IAAI,MAAM,MAAM,gBAAgB,CAAC;AACzC,YAAQ,IAAI,MAAM,IAAI,UAAU,OAAO,YAAY,CAAC;AACpD,YAAQ,IAAI,MAAM,IAAI,aAAa,OAAO,SAAS,CAAC;UAC/C;AACL,YAAQ,IAAI,MAAM,OAAO,oBAAoB,CAAC;AAC9C,YAAQ,IACN,MAAM,IACJ,uEACD,CACF;;WAEI,OAAO;AACd,WAAQ,MACN,MAAM,IACJ,2BAA2B,iBAAiB,QAAQ,MAAM,UAAU,kBACrE,CACF;AACD,WAAQ,KAAK,EAAE;;GAEjB;AAEJ,QAAO;;;;;AC1FT,MAAMA,iBAAwB;CAC5B;CACA;CACA;CACD;AAED,MAAMC,iBAAe;CACnB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,SAAgB,kBAAkB,aAAwC;AACxE,QAAO,IAAI,QAAQ,OAAO,CACvB,YAAY,4CAA4C,CACxD,SAAS,YAAY,2CAA2C,CAChE,eAAe,sBAAsB,wCAAwC,CAC7E,OAAO,uBAAuB,gBAAgB,qBAAqB,CACnE,OAAO,8BAA8B,gBAAgB,OAAO,CAC5D,OAAO,sBAAsB,oBAAoB,gBAAgB,CACjE,OAAO,OAAO,QAAgB,YAAY;AACzC,MAAI,CAACD,eAAa,SAAS,QAAQ,MAAM,EAAE;AACzC,WAAQ,MACN,MAAM,IAAI,iCAAiCA,eAAa,KAAK,KAAK,GAAG,CACtE;AACD,WAAQ,KAAK,EAAE;;AAGjB,MAAI,CAACC,eAAa,SAAS,QAAQ,YAAY,EAAE;AAC/C,WAAQ,MACN,MAAM,IAAI,wCAAwCA,eAAa,KAAK,KAAK,GAAG,CAC7E;AACD,WAAQ,KAAK,EAAE;;EAGjB,MAAM,cAAc,QAAQ,MAAM,WAAW,OAAO,GAChD,QAAQ,QACR,QAAQ,QAAQ,MAAM;EAE1B,MAAM,YAAY,QAAQ,QAAQ,OAAO;EACzC,MAAM,UAAU,IAAI,mBAAmB,CAAC,OAAO;AAE/C,MAAI;GACF,MAAM,YAAY,MAAM,YAAY,QAClC;IAAE;IAAQ;IAAa,aAAa,QAAQ;IAAa,OAAO,QAAQ;IAAO,EAC/E,UACD;AAED,WAAQ,QAAQ,MAAM,MAAM,6BAA6B,CAAC;AAC1D,WAAQ,IAAI,MAAM,IAAI,KAAK,YAAY,CAAC;WACjC,OAAO;AACd,WAAQ,MAAM;AACd,OAAI,iBAAiB,mBACnB,SAAQ,MAAM,MAAM,IAAI,MAAM,QAAQ,CAAC;YAC9B,iBAAiB,mBAC1B,SAAQ,MAAM,MAAM,IAAI,MAAM,QAAQ,CAAC;YAC9B,iBAAiB,SAC1B,SAAQ,MAAM,MAAM,IAAI,cAAc,MAAM,UAAU,CAAC;OAEvD,SAAQ,MACN,MAAM,IACJ,qBAAqB,iBAAiB,QAAQ,MAAM,UAAU,kBAC/D,CACF;AAEH,WAAQ,KAAK,EAAE;;GAEjB;;;;;AC9EN,MAAM,eAAwB;CAC5B;CACA;CACA;CACD;AAED,MAAM,eAAe;CACnB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,SAAgB,sBAAsB,iBAAgD;AACpF,QAAO,IAAI,QAAQ,WAAW,CAC3B,YAAY,qCAAqC,CACjD,SAAS,YAAY,+CAA+C,CACpE,OAAO,uBAAuB,gBAAgB,qBAAqB,CACnE,OAAO,8BAA8B,gBAAgB,OAAO,CAC5D,OAAO,wBAAwB,2BAA2B,IAAI,CAC9D,OAAO,sBAAsB,oBAAoB,gBAAgB,CACjE,OAAO,OAAO,QAAgB,YAAY;EACzC,MAAM,QAAQ,SAAS,QAAQ,OAAO,GAAG;AACzC,MAAI,OAAO,MAAM,MAAM,IAAI,QAAQ,KAAK,QAAQ,IAAI;AAClD,WAAQ,MAAM,MAAM,IAAI,2CAA2C,CAAC;AACpE,WAAQ,KAAK,EAAE;;AAGjB,MAAI,CAAC,aAAa,SAAS,QAAQ,MAAM,EAAE;AACzC,WAAQ,MACN,MAAM,IAAI,iCAAiC,aAAa,KAAK,KAAK,GAAG,CACtE;AACD,WAAQ,KAAK,EAAE;;AAGjB,MAAI,CAAC,aAAa,SAAS,QAAQ,YAAY,EAAE;AAC/C,WAAQ,MACN,MAAM,IAAI,wCAAwC,aAAa,KAAK,KAAK,GAAG,CAC7E;AACD,WAAQ,KAAK,EAAE;;EAGjB,MAAM,YAAY,QAAQ,QAAQ,OAAO;EACzC,MAAM,UAAU,IAAI,cAAc,MAAM,QAAQ,QAAQ,IAAI,MAAM,GAAG,KAAK,CAAC,OAAO;AAElF,MAAI;GACF,MAAM,QAAQ,MAAM,gBAAgB,QAClC;IAAE;IAAQ;IAAO,aAAa,QAAQ;IAAa,OAAO,QAAQ;IAAO,EACzE,UACD;AAED,WAAQ,QACN,MAAM,MAAM,aAAa,MAAM,OAAO,QAAQ,MAAM,SAAS,IAAI,MAAM,GAAG,GAAG,CAC9E;AACD,QAAK,MAAM,KAAK,MACd,SAAQ,IAAI,MAAM,IAAI,KAAK,IAAI,CAAC;WAE3B,OAAO;AACd,WAAQ,MAAM;AACd,OAAI,iBAAiB,mBACnB,SAAQ,MAAM,MAAM,IAAI,MAAM,QAAQ,CAAC;YAC9B,iBAAiB,SAC1B,SAAQ,MAAM,MAAM,IAAI,cAAc,MAAM,UAAU,CAAC;OAEvD,SAAQ,MACN,MAAM,IACJ,qBAAqB,iBAAiB,QAAQ,MAAM,UAAU,kBAC/D,CACF;AAEH,WAAQ,KAAK,EAAE;;GAEjB;;;;;ACxEN,SAAgB,UAAU,UAA6B;CACrD,MAAM,UAAU,IAAI,SAAS,CAC1B,KAAK,WAAW,CAChB,YAAY,sDAAsD,CAClE,QAAQ,QAAQ;AAEnB,SAAQ,WACN,kBAAkB;EAChB,OAAO,SAAS;EAChB,QAAQ,SAAS;EACjB,WAAW,SAAS;EACrB,CAAC,CACH;AAED,SAAQ,WAAW,sBAAsB,SAAS,cAAc,CAAC;AACjE,SAAQ,WAAW,kBAAkB,SAAS,UAAU,CAAC;AAEzD,QAAO;;;;;ACrBT,MAAM,WAAW,IAAI,cACnB;CACE;EAAE,OAAO,IAAI,iBAAiB;EAAE,MAAM;EAAY;CAClD;EAAE,OAAO,IAAI,aAAa;EAAE,MAAM;EAAQ;CAC1C;EAAE,OAAO,IAAI,oBAAoB;EAAE,MAAM;EAAe;CACzD,GACA,SAAS,QAAQ,IAAI,MAAM,IAAI,kBAAkB,OAAO,CAAC,CAC3D;AACD,MAAM,iBAAiB,IAAI,gBAAgB;AAC3C,MAAM,cAAc,IAAI,oBAAoB;AAY5B,UAAU;CACxB,eAX2B,IAAI,qBAC/B,gBACA,UACA,YACD;CAQC,WAPuB,IAAI,iBAAiB,gBAAgB,UAAU,YAAY;CAQlF,OAPmB,IAAI,aAAa,SAAS;CAQ7C,QAPoB,IAAI,cAAc,SAAS;CAQ/C,eAP2B,IAAI,qBAAqB,SAAS;CAQ9D,CAAC,CAEM,OAAO"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grok-image-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "CLI for generating and editing images with Grok API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"ai": "6.0.80",
|
|
48
48
|
"chalk": "5.6.2",
|
|
49
49
|
"commander": "13.1.0",
|
|
50
|
+
"cross-keychain": "^1.1.0",
|
|
50
51
|
"ora": "8.2.0"
|
|
51
52
|
},
|
|
52
53
|
"devDependencies": {
|