grok-image-cli 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 cyberash
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # grok-img
2
+
3
+ CLI for generating and editing images with Grok API, powered by `@ai-sdk/xai`.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g grok-image-cli
9
+ ```
10
+
11
+ ### From source
12
+
13
+ ```bash
14
+ git clone https://github.com/cyberash/grok-image-cli.git
15
+ cd grok-image-cli
16
+ npm install
17
+ npm run build
18
+ npm link
19
+ ```
20
+
21
+ ## Authentication
22
+
23
+ The CLI stores your xAI API key securely in the macOS Keychain. Alternatively, set the `XAI_API_KEY` environment variable.
24
+
25
+ ```bash
26
+ # Save API key to keychain
27
+ grok-img auth login
28
+
29
+ # Check authentication status
30
+ grok-img auth status
31
+
32
+ # Remove API key from keychain
33
+ grok-img auth logout
34
+ ```
35
+
36
+ ## Image Generation
37
+
38
+ ```bash
39
+ # Generate a single image
40
+ grok-img generate "A futuristic city skyline at night"
41
+
42
+ # Generate multiple images with specific aspect ratio
43
+ grok-img generate "Mountain landscape at sunrise" -n 4 -a 16:9
44
+
45
+ # Specify output directory
46
+ grok-img generate "A serene Japanese garden" -o ./my-images
47
+ ```
48
+
49
+ ### Options
50
+
51
+ | Option | Description | Default |
52
+ |--------|-------------|---------|
53
+ | `-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
+ | `-n, --count <number>` | Number of images (1-10) | `1` |
55
+ | `-o, --output <dir>` | Output directory | `./grok-images` |
56
+
57
+ ## Image Editing
58
+
59
+ ```bash
60
+ # Edit a local image
61
+ grok-img edit "Make it look like a watercolor painting" -i ./photo.jpg
62
+
63
+ # Edit using a URL
64
+ grok-img edit "Change the sky to sunset colors" -i https://example.com/photo.jpg
65
+
66
+ # Specify aspect ratio and output directory
67
+ grok-img edit "Add a vintage film grain effect" -i ./photo.jpg -a 3:2 -o ./edited
68
+ ```
69
+
70
+ ### Options
71
+
72
+ | Option | Description | Default |
73
+ |--------|-------------|---------|
74
+ | `-i, --image <path>` | Source image (local path or URL) | **required** |
75
+ | `-a, --aspect-ratio <ratio>` | Aspect ratio | `auto` |
76
+ | `-o, --output <dir>` | Output directory | `./grok-images` |
77
+
78
+ ## Development
79
+
80
+ ```bash
81
+ npm install
82
+ npm run dev # watch mode
83
+ npm run build # production build
84
+ npm run lint # check linting
85
+ npm run lint:fix # auto-fix lint issues
86
+ npm run format # format code
87
+ ```
88
+
89
+ ## Architecture
90
+
91
+ This project follows Clean Architecture principles:
92
+
93
+ ```
94
+ src/
95
+ main.ts # Composition root
96
+ domain/ # Entities & port interfaces (zero deps)
97
+ application/ # Use cases (depends on domain only)
98
+ infrastructure/ # Adapters (@ai-sdk/xai, keychain, fs)
99
+ presentation/ # CLI commands (commander)
100
+ ```
101
+
102
+ ## Requirements
103
+
104
+ - Node.js >= 20.19.0
105
+ - macOS (for Keychain support) or `XAI_API_KEY` environment variable
106
+ - xAI API key from [console.x.ai](https://console.x.ai)
107
+
108
+ ## License
109
+
110
+ MIT
package/dist/main.mjs ADDED
@@ -0,0 +1,426 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { join, resolve } from "node:path";
4
+ import { createXai } from "@ai-sdk/xai";
5
+ import { NoImageGeneratedError, generateImage } from "ai";
6
+ import { execFile } from "node:child_process";
7
+ import { promisify } from "node:util";
8
+ import { Command } from "commander";
9
+ import { createInterface } from "node:readline";
10
+ import chalk from "chalk";
11
+ import ora from "ora";
12
+
13
+ //#region src/domain/errors.ts
14
+ var ApiKeyMissingError = class extends Error {
15
+ constructor() {
16
+ super("API key not found. Run `grok-img auth login` or set XAI_API_KEY environment variable.");
17
+ this.name = "ApiKeyMissingError";
18
+ }
19
+ };
20
+ var ApiError = class extends Error {
21
+ constructor(message, cause) {
22
+ super(message);
23
+ this.cause = cause;
24
+ this.name = "ApiError";
25
+ }
26
+ };
27
+ var ImageNotFoundError = class extends Error {
28
+ constructor(path) {
29
+ super(`Image not found: ${path}`);
30
+ this.name = "ImageNotFoundError";
31
+ }
32
+ };
33
+
34
+ //#endregion
35
+ //#region src/application/usecases/edit-image.usecase.ts
36
+ var EditImageUseCase = class {
37
+ constructor(imageGenerator, keyStore, fileStorage) {
38
+ this.imageGenerator = imageGenerator;
39
+ this.keyStore = keyStore;
40
+ this.fileStorage = fileStorage;
41
+ }
42
+ async execute(params, outputDir) {
43
+ const apiKey = await this.keyStore.get();
44
+ if (!apiKey) throw new ApiKeyMissingError();
45
+ let imageSource = params.imageSource;
46
+ if (typeof imageSource === "string" && !imageSource.startsWith("http")) imageSource = await this.fileStorage.readImage(imageSource);
47
+ this.fileStorage.ensureDir(outputDir);
48
+ const result = await this.imageGenerator.edit({
49
+ ...params,
50
+ imageSource
51
+ }, apiKey);
52
+ const outputPath = this.fileStorage.generateOutputPath(outputDir, 0, result.mediaType);
53
+ return this.fileStorage.saveImage(result.uint8Array, outputPath);
54
+ }
55
+ };
56
+
57
+ //#endregion
58
+ //#region src/application/usecases/generate-image.usecase.ts
59
+ var GenerateImageUseCase = class {
60
+ constructor(imageGenerator, keyStore, fileStorage) {
61
+ this.imageGenerator = imageGenerator;
62
+ this.keyStore = keyStore;
63
+ this.fileStorage = fileStorage;
64
+ }
65
+ async execute(params, outputDir) {
66
+ const apiKey = await this.keyStore.get();
67
+ if (!apiKey) throw new ApiKeyMissingError();
68
+ this.fileStorage.ensureDir(outputDir);
69
+ const results = await this.imageGenerator.generate(params, apiKey);
70
+ const savedPaths = [];
71
+ for (let i = 0; i < results.length; i++) {
72
+ const result = results[i];
73
+ const outputPath = this.fileStorage.generateOutputPath(outputDir, i, result.mediaType);
74
+ const saved = await this.fileStorage.saveImage(result.uint8Array, outputPath);
75
+ savedPaths.push(saved);
76
+ }
77
+ return savedPaths;
78
+ }
79
+ };
80
+
81
+ //#endregion
82
+ //#region src/application/usecases/get-auth-status.usecase.ts
83
+ var GetAuthStatusUseCase = class {
84
+ constructor(keyStore) {
85
+ this.keyStore = keyStore;
86
+ }
87
+ async execute() {
88
+ const key = await this.keyStore.get();
89
+ if (!key) return {
90
+ authenticated: false,
91
+ maskedKey: null,
92
+ source: null
93
+ };
94
+ return {
95
+ authenticated: true,
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" : "keychain"
98
+ };
99
+ }
100
+ };
101
+
102
+ //#endregion
103
+ //#region src/application/usecases/login.usecase.ts
104
+ var LoginUseCase = class {
105
+ constructor(keyStore) {
106
+ this.keyStore = keyStore;
107
+ }
108
+ async execute(apiKey) {
109
+ await this.keyStore.save(apiKey);
110
+ }
111
+ };
112
+
113
+ //#endregion
114
+ //#region src/application/usecases/logout.usecase.ts
115
+ var LogoutUseCase = class {
116
+ constructor(keyStore) {
117
+ this.keyStore = keyStore;
118
+ }
119
+ async execute() {
120
+ await this.keyStore.remove();
121
+ }
122
+ };
123
+
124
+ //#endregion
125
+ //#region src/infrastructure/adapters/file-storage.adapter.ts
126
+ const MEDIA_TYPE_EXT = {
127
+ "image/png": "png",
128
+ "image/jpeg": "jpg",
129
+ "image/webp": "webp",
130
+ "image/gif": "gif"
131
+ };
132
+ var FileStorageAdapter = class {
133
+ async saveImage(data, outputPath) {
134
+ writeFileSync(outputPath, data);
135
+ return outputPath;
136
+ }
137
+ async readImage(filePath) {
138
+ if (!existsSync(filePath)) throw new ImageNotFoundError(filePath);
139
+ return new Uint8Array(readFileSync(filePath));
140
+ }
141
+ ensureDir(dir) {
142
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
143
+ }
144
+ generateOutputPath(dir, index, mediaType) {
145
+ const ext = MEDIA_TYPE_EXT[mediaType] ?? "png";
146
+ return join(dir, `grok-img-${Date.now()}-${index}.${ext}`);
147
+ }
148
+ };
149
+
150
+ //#endregion
151
+ //#region src/infrastructure/adapters/grok-api.adapter.ts
152
+ const MODEL = "grok-imagine-image";
153
+ var GrokApiAdapter = class {
154
+ async generate(params, apiKey) {
155
+ const xai = createXai({ apiKey });
156
+ try {
157
+ const { images } = await generateImage({
158
+ model: xai.image(MODEL),
159
+ prompt: params.prompt,
160
+ aspectRatio: params.aspectRatio,
161
+ n: params.count
162
+ });
163
+ return images.map((img) => ({
164
+ base64: img.base64,
165
+ uint8Array: img.uint8Array,
166
+ mediaType: img.mediaType
167
+ }));
168
+ } catch (error) {
169
+ if (NoImageGeneratedError.isInstance(error)) throw new ApiError("Image generation failed: the model could not produce an image.", error);
170
+ if (error instanceof Error) throw new ApiError(`API request failed: ${error.message}`, error);
171
+ throw new ApiError("An unexpected error occurred during image generation.", error);
172
+ }
173
+ }
174
+ async edit(params, apiKey) {
175
+ const xai = createXai({ apiKey });
176
+ try {
177
+ const { image } = await generateImage({
178
+ model: xai.image(MODEL),
179
+ prompt: {
180
+ text: params.prompt,
181
+ images: typeof params.imageSource === "string" ? [new URL(params.imageSource)] : [params.imageSource]
182
+ },
183
+ aspectRatio: params.aspectRatio
184
+ });
185
+ return {
186
+ base64: image.base64,
187
+ uint8Array: image.uint8Array,
188
+ mediaType: image.mediaType
189
+ };
190
+ } catch (error) {
191
+ if (NoImageGeneratedError.isInstance(error)) throw new ApiError("Image editing failed: the model could not produce an image.", error);
192
+ if (error instanceof Error) throw new ApiError(`API request failed: ${error.message}`, error);
193
+ throw new ApiError("An unexpected error occurred during image editing.", error);
194
+ }
195
+ }
196
+ };
197
+
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
+ //#endregion
245
+ //#region src/presentation/commands/auth.command.ts
246
+ function readInput(prompt) {
247
+ const rl = createInterface({
248
+ input: process.stdin,
249
+ output: process.stdout
250
+ });
251
+ return new Promise((resolve) => {
252
+ rl.question(prompt, (answer) => {
253
+ rl.close();
254
+ resolve(answer.trim());
255
+ });
256
+ });
257
+ }
258
+ function createAuthCommand(useCases) {
259
+ const auth = new Command("auth").description("Manage API key authentication");
260
+ auth.command("login").description("Store your xAI API key in the system keychain").action(async () => {
261
+ try {
262
+ const apiKey = await readInput(chalk.cyan("Enter your xAI API key: "));
263
+ if (!apiKey) {
264
+ console.log(chalk.red("No API key provided."));
265
+ process.exit(1);
266
+ }
267
+ await useCases.login.execute(apiKey);
268
+ console.log(chalk.green("API key saved to keychain successfully."));
269
+ } catch (error) {
270
+ console.error(chalk.red(`Failed to save API key: ${error instanceof Error ? error.message : "Unknown error"}`));
271
+ process.exit(1);
272
+ }
273
+ });
274
+ auth.command("logout").description("Remove your xAI API key from the system keychain").action(async () => {
275
+ try {
276
+ await useCases.logout.execute();
277
+ console.log(chalk.green("API key removed from keychain."));
278
+ } catch (error) {
279
+ console.error(chalk.red(`Failed to remove API key: ${error instanceof Error ? error.message : "Unknown error"}`));
280
+ process.exit(1);
281
+ }
282
+ });
283
+ auth.command("status").description("Check current authentication status").action(async () => {
284
+ try {
285
+ const status = await useCases.getStatus.execute();
286
+ if (status.authenticated) {
287
+ console.log(chalk.green("Authenticated"));
288
+ console.log(chalk.dim(` Key: ${status.maskedKey}`));
289
+ console.log(chalk.dim(` Source: ${status.source}`));
290
+ } else {
291
+ console.log(chalk.yellow("Not authenticated"));
292
+ console.log(chalk.dim(" Run `grok-img auth login` or set XAI_API_KEY environment variable."));
293
+ }
294
+ } catch (error) {
295
+ console.error(chalk.red(`Failed to check status: ${error instanceof Error ? error.message : "Unknown error"}`));
296
+ process.exit(1);
297
+ }
298
+ });
299
+ return auth;
300
+ }
301
+
302
+ //#endregion
303
+ //#region src/presentation/commands/edit.command.ts
304
+ const VALID_RATIOS$1 = [
305
+ "1:1",
306
+ "16:9",
307
+ "9:16",
308
+ "4:3",
309
+ "3:4",
310
+ "3:2",
311
+ "2:3",
312
+ "2:1",
313
+ "1:2",
314
+ "19.5:9",
315
+ "9:19.5",
316
+ "20:9",
317
+ "9:20",
318
+ "auto"
319
+ ];
320
+ 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) => {
322
+ if (!VALID_RATIOS$1.includes(options.aspectRatio)) {
323
+ console.error(chalk.red(`Invalid aspect ratio. Valid options: ${VALID_RATIOS$1.join(", ")}`));
324
+ process.exit(1);
325
+ }
326
+ const imageSource = options.image.startsWith("http") ? options.image : resolve(options.image);
327
+ const outputDir = resolve(options.output);
328
+ const spinner = ora("Editing image...").start();
329
+ try {
330
+ const savedPath = await editUseCase.execute({
331
+ prompt,
332
+ imageSource,
333
+ aspectRatio: options.aspectRatio
334
+ }, outputDir);
335
+ spinner.succeed(chalk.green("Image edited successfully:"));
336
+ console.log(chalk.dim(` ${savedPath}`));
337
+ } catch (error) {
338
+ spinner.fail();
339
+ if (error instanceof ApiKeyMissingError) console.error(chalk.red(error.message));
340
+ else if (error instanceof ImageNotFoundError) console.error(chalk.red(error.message));
341
+ else if (error instanceof ApiError) console.error(chalk.red(`API Error: ${error.message}`));
342
+ else console.error(chalk.red(`Unexpected error: ${error instanceof Error ? error.message : "Unknown error"}`));
343
+ process.exit(1);
344
+ }
345
+ });
346
+ }
347
+
348
+ //#endregion
349
+ //#region src/presentation/commands/generate.command.ts
350
+ const VALID_RATIOS = [
351
+ "1:1",
352
+ "16:9",
353
+ "9:16",
354
+ "4:3",
355
+ "3:4",
356
+ "3:2",
357
+ "2:3",
358
+ "2:1",
359
+ "1:2",
360
+ "19.5:9",
361
+ "9:19.5",
362
+ "20:9",
363
+ "9:20",
364
+ "auto"
365
+ ];
366
+ 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) => {
368
+ const count = parseInt(options.count, 10);
369
+ if (Number.isNaN(count) || count < 1 || count > 10) {
370
+ console.error(chalk.red("Count must be a number between 1 and 10."));
371
+ process.exit(1);
372
+ }
373
+ if (!VALID_RATIOS.includes(options.aspectRatio)) {
374
+ console.error(chalk.red(`Invalid aspect ratio. Valid options: ${VALID_RATIOS.join(", ")}`));
375
+ process.exit(1);
376
+ }
377
+ const outputDir = resolve(options.output);
378
+ const spinner = ora(`Generating ${count} image${count > 1 ? "s" : ""}...`).start();
379
+ try {
380
+ const paths = await generateUseCase.execute({
381
+ prompt,
382
+ count,
383
+ aspectRatio: options.aspectRatio
384
+ }, outputDir);
385
+ spinner.succeed(chalk.green(`Generated ${paths.length} image${paths.length > 1 ? "s" : ""}:`));
386
+ for (const p of paths) console.log(chalk.dim(` ${p}`));
387
+ } catch (error) {
388
+ spinner.fail();
389
+ if (error instanceof ApiKeyMissingError) console.error(chalk.red(error.message));
390
+ else if (error instanceof ApiError) console.error(chalk.red(`API Error: ${error.message}`));
391
+ else console.error(chalk.red(`Unexpected error: ${error instanceof Error ? error.message : "Unknown error"}`));
392
+ process.exit(1);
393
+ }
394
+ });
395
+ }
396
+
397
+ //#endregion
398
+ //#region src/presentation/cli.ts
399
+ function createCli(useCases) {
400
+ const program = new Command().name("grok-img").description("CLI for generating and editing images with Grok API").version("1.0.0");
401
+ program.addCommand(createAuthCommand({
402
+ login: useCases.login,
403
+ logout: useCases.logout,
404
+ getStatus: useCases.getAuthStatus
405
+ }));
406
+ program.addCommand(createGenerateCommand(useCases.generateImage));
407
+ program.addCommand(createEditCommand(useCases.editImage));
408
+ return program;
409
+ }
410
+
411
+ //#endregion
412
+ //#region src/main.ts
413
+ const keyStore = new KeychainAdapter();
414
+ const imageGenerator = new GrokApiAdapter();
415
+ const fileStorage = new FileStorageAdapter();
416
+ createCli({
417
+ generateImage: new GenerateImageUseCase(imageGenerator, keyStore, fileStorage),
418
+ editImage: new EditImageUseCase(imageGenerator, keyStore, fileStorage),
419
+ login: new LoginUseCase(keyStore),
420
+ logout: new LogoutUseCase(keyStore),
421
+ getAuthStatus: new GetAuthStatusUseCase(keyStore)
422
+ }).parse();
423
+
424
+ //#endregion
425
+ export { };
426
+ //# sourceMappingURL=main.mjs.map
@@ -0,0 +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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"}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "grok-image-cli",
3
+ "version": "0.1.0",
4
+ "description": "CLI for generating and editing images with Grok API",
5
+ "type": "module",
6
+ "bin": {
7
+ "grok-img": "./dist/main.mjs"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "README.md",
12
+ "LICENSE"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsdown",
16
+ "dev": "tsdown --watch",
17
+ "start": "node dist/main.mjs",
18
+ "lint": "biome check .",
19
+ "lint:fix": "biome check --write .",
20
+ "format": "biome format --write .",
21
+ "prepublishOnly": "npm run lint && npm run build"
22
+ },
23
+ "keywords": [
24
+ "grok",
25
+ "xai",
26
+ "image",
27
+ "cli",
28
+ "ai",
29
+ "image-generation",
30
+ "image-editing"
31
+ ],
32
+ "author": "",
33
+ "license": "MIT",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/cyberash-dev/grok-image-cli.git"
37
+ },
38
+ "homepage": "https://github.com/cyberash-dev/grok-image-cli#readme",
39
+ "bugs": {
40
+ "url": "https://github.com/cyberash-dev/grok-image-cli/issues"
41
+ },
42
+ "engines": {
43
+ "node": ">=20.19.0"
44
+ },
45
+ "dependencies": {
46
+ "@ai-sdk/xai": "3.0.53",
47
+ "ai": "6.0.80",
48
+ "chalk": "5.6.2",
49
+ "commander": "13.1.0",
50
+ "ora": "8.2.0"
51
+ },
52
+ "devDependencies": {
53
+ "@biomejs/biome": "2.3.14",
54
+ "@types/node": "22.19.11",
55
+ "tsdown": "0.20.3",
56
+ "typescript": "5.9.3"
57
+ }
58
+ }