grok-image-cli 0.1.1 → 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 +23 -22
- package/dist/main.mjs +57 -62
- 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,10 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { deletePassword, getPassword, setPassword } from "cross-keychain";
|
|
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 { execFile } from "node:child_process";
|
|
7
|
-
import { promisify } from "node:util";
|
|
8
7
|
import { Command } from "commander";
|
|
9
8
|
import { createInterface } from "node:readline";
|
|
10
9
|
import chalk from "chalk";
|
|
@@ -94,7 +93,7 @@ var GetAuthStatusUseCase = class {
|
|
|
94
93
|
return {
|
|
95
94
|
authenticated: true,
|
|
96
95
|
maskedKey: key.length > 8 ? `${key.slice(0, 4)}${"*".repeat(key.length - 8)}${key.slice(-4)}` : "****",
|
|
97
|
-
source: process.env.XAI_API_KEY === key ? "env" : "
|
|
96
|
+
source: process.env.XAI_API_KEY === key ? "env" : "credential-store"
|
|
98
97
|
};
|
|
99
98
|
}
|
|
100
99
|
};
|
|
@@ -121,6 +120,28 @@ var LogoutUseCase = class {
|
|
|
121
120
|
}
|
|
122
121
|
};
|
|
123
122
|
|
|
123
|
+
//#endregion
|
|
124
|
+
//#region src/infrastructure/adapters/credential-store.adapter.ts
|
|
125
|
+
const SERVICE = "grok-image-cli";
|
|
126
|
+
const ACCOUNT = "api-key";
|
|
127
|
+
var CredentialStoreAdapter = class {
|
|
128
|
+
async save(key) {
|
|
129
|
+
await setPassword(SERVICE, ACCOUNT, key);
|
|
130
|
+
}
|
|
131
|
+
async get() {
|
|
132
|
+
try {
|
|
133
|
+
const key = await getPassword(SERVICE, ACCOUNT);
|
|
134
|
+
if (key) return key;
|
|
135
|
+
} catch {}
|
|
136
|
+
return process.env.XAI_API_KEY ?? null;
|
|
137
|
+
}
|
|
138
|
+
async remove() {
|
|
139
|
+
try {
|
|
140
|
+
await deletePassword(SERVICE, ACCOUNT);
|
|
141
|
+
} catch {}
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
124
145
|
//#endregion
|
|
125
146
|
//#region src/infrastructure/adapters/file-storage.adapter.ts
|
|
126
147
|
const MEDIA_TYPE_EXT = {
|
|
@@ -149,13 +170,13 @@ var FileStorageAdapter = class {
|
|
|
149
170
|
|
|
150
171
|
//#endregion
|
|
151
172
|
//#region src/infrastructure/adapters/grok-api.adapter.ts
|
|
152
|
-
const
|
|
173
|
+
const DEFAULT_MODEL = "grok-imagine-image";
|
|
153
174
|
var GrokApiAdapter = class {
|
|
154
175
|
async generate(params, apiKey) {
|
|
155
176
|
const xai = createXai({ apiKey });
|
|
156
177
|
try {
|
|
157
178
|
const { images } = await generateImage({
|
|
158
|
-
model: xai.image(
|
|
179
|
+
model: xai.image(params.model ?? DEFAULT_MODEL),
|
|
159
180
|
prompt: params.prompt,
|
|
160
181
|
aspectRatio: params.aspectRatio,
|
|
161
182
|
n: params.count
|
|
@@ -175,10 +196,10 @@ var GrokApiAdapter = class {
|
|
|
175
196
|
const xai = createXai({ apiKey });
|
|
176
197
|
try {
|
|
177
198
|
const { image } = await generateImage({
|
|
178
|
-
model: xai.image(
|
|
199
|
+
model: xai.image(params.model ?? DEFAULT_MODEL),
|
|
179
200
|
prompt: {
|
|
180
201
|
text: params.prompt,
|
|
181
|
-
images:
|
|
202
|
+
images: [params.imageSource]
|
|
182
203
|
},
|
|
183
204
|
aspectRatio: params.aspectRatio
|
|
184
205
|
});
|
|
@@ -195,52 +216,6 @@ var GrokApiAdapter = class {
|
|
|
195
216
|
}
|
|
196
217
|
};
|
|
197
218
|
|
|
198
|
-
//#endregion
|
|
199
|
-
//#region src/infrastructure/adapters/keychain.adapter.ts
|
|
200
|
-
const execFileAsync = promisify(execFile);
|
|
201
|
-
const SERVICE = "grok-image-cli";
|
|
202
|
-
const ACCOUNT = "api-key";
|
|
203
|
-
var KeychainAdapter = class {
|
|
204
|
-
async save(key) {
|
|
205
|
-
await execFileAsync("security", [
|
|
206
|
-
"add-generic-password",
|
|
207
|
-
"-U",
|
|
208
|
-
"-s",
|
|
209
|
-
SERVICE,
|
|
210
|
-
"-a",
|
|
211
|
-
ACCOUNT,
|
|
212
|
-
"-w",
|
|
213
|
-
key
|
|
214
|
-
]);
|
|
215
|
-
}
|
|
216
|
-
async get() {
|
|
217
|
-
try {
|
|
218
|
-
const { stdout } = await execFileAsync("security", [
|
|
219
|
-
"find-generic-password",
|
|
220
|
-
"-s",
|
|
221
|
-
SERVICE,
|
|
222
|
-
"-a",
|
|
223
|
-
ACCOUNT,
|
|
224
|
-
"-w"
|
|
225
|
-
]);
|
|
226
|
-
const key = stdout.trim();
|
|
227
|
-
if (key) return key;
|
|
228
|
-
} catch {}
|
|
229
|
-
return process.env.XAI_API_KEY ?? null;
|
|
230
|
-
}
|
|
231
|
-
async remove() {
|
|
232
|
-
try {
|
|
233
|
-
await execFileAsync("security", [
|
|
234
|
-
"delete-generic-password",
|
|
235
|
-
"-s",
|
|
236
|
-
SERVICE,
|
|
237
|
-
"-a",
|
|
238
|
-
ACCOUNT
|
|
239
|
-
]);
|
|
240
|
-
} catch {}
|
|
241
|
-
}
|
|
242
|
-
};
|
|
243
|
-
|
|
244
219
|
//#endregion
|
|
245
220
|
//#region src/presentation/commands/auth.command.ts
|
|
246
221
|
function readInput(prompt) {
|
|
@@ -257,7 +232,7 @@ function readInput(prompt) {
|
|
|
257
232
|
}
|
|
258
233
|
function createAuthCommand(useCases) {
|
|
259
234
|
const auth = new Command("auth").description("Manage API key authentication");
|
|
260
|
-
auth.command("login").description("Store your xAI API key in the system
|
|
235
|
+
auth.command("login").description("Store your xAI API key in the system credential store").action(async () => {
|
|
261
236
|
try {
|
|
262
237
|
const apiKey = await readInput(chalk.cyan("Enter your xAI API key: "));
|
|
263
238
|
if (!apiKey) {
|
|
@@ -265,16 +240,16 @@ function createAuthCommand(useCases) {
|
|
|
265
240
|
process.exit(1);
|
|
266
241
|
}
|
|
267
242
|
await useCases.login.execute(apiKey);
|
|
268
|
-
console.log(chalk.green("API key saved
|
|
243
|
+
console.log(chalk.green("API key saved successfully."));
|
|
269
244
|
} catch (error) {
|
|
270
245
|
console.error(chalk.red(`Failed to save API key: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
271
246
|
process.exit(1);
|
|
272
247
|
}
|
|
273
248
|
});
|
|
274
|
-
auth.command("logout").description("Remove your xAI API key from the system
|
|
249
|
+
auth.command("logout").description("Remove your xAI API key from the system credential store").action(async () => {
|
|
275
250
|
try {
|
|
276
251
|
await useCases.logout.execute();
|
|
277
|
-
console.log(chalk.green("API key removed
|
|
252
|
+
console.log(chalk.green("API key removed successfully."));
|
|
278
253
|
} catch (error) {
|
|
279
254
|
console.error(chalk.red(`Failed to remove API key: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
280
255
|
process.exit(1);
|
|
@@ -301,6 +276,11 @@ function createAuthCommand(useCases) {
|
|
|
301
276
|
|
|
302
277
|
//#endregion
|
|
303
278
|
//#region src/presentation/commands/edit.command.ts
|
|
279
|
+
const VALID_MODELS$1 = [
|
|
280
|
+
"grok-2-image-1212",
|
|
281
|
+
"grok-imagine-image-pro",
|
|
282
|
+
"grok-imagine-image"
|
|
283
|
+
];
|
|
304
284
|
const VALID_RATIOS$1 = [
|
|
305
285
|
"1:1",
|
|
306
286
|
"16:9",
|
|
@@ -318,7 +298,11 @@ const VALID_RATIOS$1 = [
|
|
|
318
298
|
"auto"
|
|
319
299
|
];
|
|
320
300
|
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) => {
|
|
301
|
+
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) => {
|
|
302
|
+
if (!VALID_MODELS$1.includes(options.model)) {
|
|
303
|
+
console.error(chalk.red(`Invalid model. Valid options: ${VALID_MODELS$1.join(", ")}`));
|
|
304
|
+
process.exit(1);
|
|
305
|
+
}
|
|
322
306
|
if (!VALID_RATIOS$1.includes(options.aspectRatio)) {
|
|
323
307
|
console.error(chalk.red(`Invalid aspect ratio. Valid options: ${VALID_RATIOS$1.join(", ")}`));
|
|
324
308
|
process.exit(1);
|
|
@@ -330,7 +314,8 @@ function createEditCommand(editUseCase) {
|
|
|
330
314
|
const savedPath = await editUseCase.execute({
|
|
331
315
|
prompt,
|
|
332
316
|
imageSource,
|
|
333
|
-
aspectRatio: options.aspectRatio
|
|
317
|
+
aspectRatio: options.aspectRatio,
|
|
318
|
+
model: options.model
|
|
334
319
|
}, outputDir);
|
|
335
320
|
spinner.succeed(chalk.green("Image edited successfully:"));
|
|
336
321
|
console.log(chalk.dim(` ${savedPath}`));
|
|
@@ -347,6 +332,11 @@ function createEditCommand(editUseCase) {
|
|
|
347
332
|
|
|
348
333
|
//#endregion
|
|
349
334
|
//#region src/presentation/commands/generate.command.ts
|
|
335
|
+
const VALID_MODELS = [
|
|
336
|
+
"grok-2-image-1212",
|
|
337
|
+
"grok-imagine-image-pro",
|
|
338
|
+
"grok-imagine-image"
|
|
339
|
+
];
|
|
350
340
|
const VALID_RATIOS = [
|
|
351
341
|
"1:1",
|
|
352
342
|
"16:9",
|
|
@@ -364,12 +354,16 @@ const VALID_RATIOS = [
|
|
|
364
354
|
"auto"
|
|
365
355
|
];
|
|
366
356
|
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) => {
|
|
357
|
+
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
358
|
const count = parseInt(options.count, 10);
|
|
369
359
|
if (Number.isNaN(count) || count < 1 || count > 10) {
|
|
370
360
|
console.error(chalk.red("Count must be a number between 1 and 10."));
|
|
371
361
|
process.exit(1);
|
|
372
362
|
}
|
|
363
|
+
if (!VALID_MODELS.includes(options.model)) {
|
|
364
|
+
console.error(chalk.red(`Invalid model. Valid options: ${VALID_MODELS.join(", ")}`));
|
|
365
|
+
process.exit(1);
|
|
366
|
+
}
|
|
373
367
|
if (!VALID_RATIOS.includes(options.aspectRatio)) {
|
|
374
368
|
console.error(chalk.red(`Invalid aspect ratio. Valid options: ${VALID_RATIOS.join(", ")}`));
|
|
375
369
|
process.exit(1);
|
|
@@ -380,7 +374,8 @@ function createGenerateCommand(generateUseCase) {
|
|
|
380
374
|
const paths = await generateUseCase.execute({
|
|
381
375
|
prompt,
|
|
382
376
|
count,
|
|
383
|
-
aspectRatio: options.aspectRatio
|
|
377
|
+
aspectRatio: options.aspectRatio,
|
|
378
|
+
model: options.model
|
|
384
379
|
}, outputDir);
|
|
385
380
|
spinner.succeed(chalk.green(`Generated ${paths.length} image${paths.length > 1 ? "s" : ""}:`));
|
|
386
381
|
for (const p of paths) console.log(chalk.dim(` ${p}`));
|
|
@@ -410,7 +405,7 @@ function createCli(useCases) {
|
|
|
410
405
|
|
|
411
406
|
//#endregion
|
|
412
407
|
//#region src/main.ts
|
|
413
|
-
const keyStore = new
|
|
408
|
+
const keyStore = new CredentialStoreAdapter();
|
|
414
409
|
const imageGenerator = new GrokApiAdapter();
|
|
415
410
|
const fileStorage = new FileStorageAdapter();
|
|
416
411
|
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/credential-store.adapter.ts","../src/infrastructure/adapters/file-storage.adapter.ts","../src/infrastructure/adapters/grok-api.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: \"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 { 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 CredentialStoreAdapter implements KeyStorePort {\n async save(key: string): Promise<void> {\n await setPassword(SERVICE, ACCOUNT, key)\n }\n\n async get(): Promise<string | null> {\n try {\n const key = await getPassword(SERVICE, ACCOUNT)\n if (key) return key\n } catch {}\n\n return process.env.XAI_API_KEY ?? null\n }\n\n async remove(): Promise<void> {\n try {\n await deletePassword(SERVICE, ACCOUNT)\n } catch {}\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 { 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 { 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 { CredentialStoreAdapter } from \"./infrastructure/adapters/credential-store.adapter.js\"\nimport { FileStorageAdapter } from \"./infrastructure/adapters/file-storage.adapter.js\"\nimport { GrokApiAdapter } from \"./infrastructure/adapters/grok-api.adapter.js\"\nimport { createCli } from \"./presentation/cli.js\"\n\nconst keyStore = new CredentialStoreAdapter()\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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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grok-image-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.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": {
|