grok-image-cli 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md 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. Alternatively, set the `XAI_API_KEY` environment variable.
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 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
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 (for Keychain support) or `XAI_API_KEY` environment variable
106
+ - macOS, Windows, or Linux
106
107
  - xAI API key from [console.x.ai](https://console.x.ai)
107
108
 
108
109
  ## License
package/dist/main.mjs CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
+ import chalk from "chalk";
2
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
4
  import { join, resolve } from "node:path";
4
5
  import { createXai } from "@ai-sdk/xai";
5
6
  import { NoImageGeneratedError, generateImage } from "ai";
6
- import { execFile } from "node:child_process";
7
- import { promisify } from "node:util";
7
+ import { deletePassword, getPassword, setPassword } from "cross-keychain";
8
+ import { execFileSync } from "node:child_process";
8
9
  import { Command } from "commander";
9
10
  import { createInterface } from "node:readline";
10
- import chalk from "chalk";
11
11
  import ora from "ora";
12
12
 
13
13
  //#region src/domain/errors.ts
@@ -94,7 +94,7 @@ var GetAuthStatusUseCase = class {
94
94
  return {
95
95
  authenticated: true,
96
96
  maskedKey: key.length > 8 ? `${key.slice(0, 4)}${"*".repeat(key.length - 8)}${key.slice(-4)}` : "****",
97
- source: process.env.XAI_API_KEY === key ? "env" : "keychain"
97
+ source: process.env.XAI_API_KEY === key ? "env" : "credential-store"
98
98
  };
99
99
  }
100
100
  };
@@ -121,6 +121,18 @@ var LogoutUseCase = class {
121
121
  }
122
122
  };
123
123
 
124
+ //#endregion
125
+ //#region src/infrastructure/adapters/env-key-store.adapter.ts
126
+ var EnvKeyStoreAdapter = class {
127
+ async get() {
128
+ return process.env.XAI_API_KEY ?? null;
129
+ }
130
+ async save(_key) {
131
+ throw new Error("XAI_API_KEY is a read-only environment variable");
132
+ }
133
+ async remove() {}
134
+ };
135
+
124
136
  //#endregion
125
137
  //#region src/infrastructure/adapters/file-storage.adapter.ts
126
138
  const MEDIA_TYPE_EXT = {
@@ -149,13 +161,13 @@ var FileStorageAdapter = class {
149
161
 
150
162
  //#endregion
151
163
  //#region src/infrastructure/adapters/grok-api.adapter.ts
152
- const MODEL = "grok-imagine-image";
164
+ const DEFAULT_MODEL = "grok-imagine-image";
153
165
  var GrokApiAdapter = class {
154
166
  async generate(params, apiKey) {
155
167
  const xai = createXai({ apiKey });
156
168
  try {
157
169
  const { images } = await generateImage({
158
- model: xai.image(MODEL),
170
+ model: xai.image(params.model ?? DEFAULT_MODEL),
159
171
  prompt: params.prompt,
160
172
  aspectRatio: params.aspectRatio,
161
173
  n: params.count
@@ -175,10 +187,10 @@ var GrokApiAdapter = class {
175
187
  const xai = createXai({ apiKey });
176
188
  try {
177
189
  const { image } = await generateImage({
178
- model: xai.image(MODEL),
190
+ model: xai.image(params.model ?? DEFAULT_MODEL),
179
191
  prompt: {
180
192
  text: params.prompt,
181
- images: typeof params.imageSource === "string" ? [new URL(params.imageSource)] : [params.imageSource]
193
+ images: [params.imageSource]
182
194
  },
183
195
  aspectRatio: params.aspectRatio
184
196
  });
@@ -197,46 +209,107 @@ var GrokApiAdapter = class {
197
209
 
198
210
  //#endregion
199
211
  //#region src/infrastructure/adapters/keychain.adapter.ts
200
- const execFileAsync = promisify(execFile);
201
212
  const SERVICE = "grok-image-cli";
202
213
  const ACCOUNT = "api-key";
203
214
  var KeychainAdapter = class {
204
215
  async save(key) {
205
- await execFileAsync("security", [
206
- "add-generic-password",
207
- "-U",
208
- "-s",
209
- SERVICE,
210
- "-a",
211
- ACCOUNT,
212
- "-w",
213
- key
214
- ]);
216
+ await setPassword(SERVICE, ACCOUNT, key);
217
+ }
218
+ async get() {
219
+ return await getPassword(SERVICE, ACCOUNT) ?? null;
220
+ }
221
+ async remove() {
222
+ await deletePassword(SERVICE, ACCOUNT);
223
+ }
224
+ };
225
+
226
+ //#endregion
227
+ //#region src/infrastructure/adapters/pass.adapter.ts
228
+ const SECRET_PATH = "grok-image-cli/api-key";
229
+ function detectTool() {
230
+ for (const tool of ["gopass", "pass"]) try {
231
+ execFileSync("which", [tool], { stdio: "ignore" });
232
+ return tool;
233
+ } catch {}
234
+ return null;
235
+ }
236
+ const tool = detectTool();
237
+ var PassAdapter = class {
238
+ async save(key) {
239
+ if (!tool) throw new Error("Neither gopass nor pass is installed");
240
+ execFileSync(tool, tool === "gopass" ? [
241
+ "insert",
242
+ "-f",
243
+ SECRET_PATH
244
+ ] : [
245
+ "insert",
246
+ "-m",
247
+ "--force",
248
+ SECRET_PATH
249
+ ], {
250
+ input: `${key}\n`,
251
+ stdio: [
252
+ "pipe",
253
+ "ignore",
254
+ "ignore"
255
+ ]
256
+ });
215
257
  }
216
258
  async get() {
259
+ if (!tool) throw new Error("Neither gopass nor pass is installed");
217
260
  try {
218
- 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();
261
+ return execFileSync(tool, ["show", SECRET_PATH], {
262
+ encoding: "utf-8",
263
+ stdio: [
264
+ "ignore",
265
+ "pipe",
266
+ "ignore"
267
+ ]
268
+ }).trim().split("\n")[0] ?? null;
269
+ } catch {
270
+ return null;
271
+ }
272
+ }
273
+ async remove() {
274
+ if (!tool) throw new Error("Neither gopass nor pass is installed");
275
+ try {
276
+ execFileSync(tool, [
277
+ "rm",
278
+ "--force",
279
+ SECRET_PATH
280
+ ], { stdio: "ignore" });
281
+ } catch {}
282
+ }
283
+ };
284
+
285
+ //#endregion
286
+ //#region src/infrastructure/key-store-chain.ts
287
+ var KeyStoreChain = class {
288
+ constructor(stores, onSave) {
289
+ this.stores = stores;
290
+ this.onSave = onSave;
291
+ }
292
+ async save(key) {
293
+ let lastError;
294
+ for (const { store, name } of this.stores) try {
295
+ await store.save(key);
296
+ this.onSave?.(name);
297
+ return;
298
+ } catch (e) {
299
+ lastError = e;
300
+ }
301
+ throw lastError;
302
+ }
303
+ async get() {
304
+ for (const { store } of this.stores) try {
305
+ const key = await store.get();
227
306
  if (key) return key;
228
307
  } catch {}
229
- return process.env.XAI_API_KEY ?? null;
308
+ return null;
230
309
  }
231
310
  async remove() {
232
- try {
233
- await execFileAsync("security", [
234
- "delete-generic-password",
235
- "-s",
236
- SERVICE,
237
- "-a",
238
- ACCOUNT
239
- ]);
311
+ for (const { store } of this.stores) try {
312
+ await store.remove();
240
313
  } catch {}
241
314
  }
242
315
  };
@@ -257,7 +330,7 @@ function readInput(prompt) {
257
330
  }
258
331
  function createAuthCommand(useCases) {
259
332
  const auth = new Command("auth").description("Manage API key authentication");
260
- auth.command("login").description("Store your xAI API key in the system keychain").action(async () => {
333
+ auth.command("login").description("Store your xAI API key in the system credential store").action(async () => {
261
334
  try {
262
335
  const apiKey = await readInput(chalk.cyan("Enter your xAI API key: "));
263
336
  if (!apiKey) {
@@ -265,16 +338,16 @@ function createAuthCommand(useCases) {
265
338
  process.exit(1);
266
339
  }
267
340
  await useCases.login.execute(apiKey);
268
- console.log(chalk.green("API key saved to keychain successfully."));
341
+ console.log(chalk.green("API key saved successfully."));
269
342
  } catch (error) {
270
343
  console.error(chalk.red(`Failed to save API key: ${error instanceof Error ? error.message : "Unknown error"}`));
271
344
  process.exit(1);
272
345
  }
273
346
  });
274
- auth.command("logout").description("Remove your xAI API key from the system keychain").action(async () => {
347
+ auth.command("logout").description("Remove your xAI API key from the system credential store").action(async () => {
275
348
  try {
276
349
  await useCases.logout.execute();
277
- console.log(chalk.green("API key removed from keychain."));
350
+ console.log(chalk.green("API key removed successfully."));
278
351
  } catch (error) {
279
352
  console.error(chalk.red(`Failed to remove API key: ${error instanceof Error ? error.message : "Unknown error"}`));
280
353
  process.exit(1);
@@ -301,6 +374,11 @@ function createAuthCommand(useCases) {
301
374
 
302
375
  //#endregion
303
376
  //#region src/presentation/commands/edit.command.ts
377
+ const VALID_MODELS$1 = [
378
+ "grok-2-image-1212",
379
+ "grok-imagine-image-pro",
380
+ "grok-imagine-image"
381
+ ];
304
382
  const VALID_RATIOS$1 = [
305
383
  "1:1",
306
384
  "16:9",
@@ -318,7 +396,11 @@ const VALID_RATIOS$1 = [
318
396
  "auto"
319
397
  ];
320
398
  function createEditCommand(editUseCase) {
321
- return new Command("edit").description("Edit an existing image with a text prompt").argument("<prompt>", "Text prompt describing the edit to apply").requiredOption("-i, --image <path>", "Source image (local file path or URL)").option("-a, --aspect-ratio <ratio>", "Aspect ratio", "auto").option("-o, --output <dir>", "Output directory", "./grok-images").action(async (prompt, options) => {
399
+ return new Command("edit").description("Edit an existing image with a text prompt").argument("<prompt>", "Text prompt describing the edit to apply").requiredOption("-i, --image <path>", "Source image (local file path or URL)").option("-m, --model <model>", "Model to use", "grok-imagine-image").option("-a, --aspect-ratio <ratio>", "Aspect ratio", "auto").option("-o, --output <dir>", "Output directory", "./grok-images").action(async (prompt, options) => {
400
+ if (!VALID_MODELS$1.includes(options.model)) {
401
+ console.error(chalk.red(`Invalid model. Valid options: ${VALID_MODELS$1.join(", ")}`));
402
+ process.exit(1);
403
+ }
322
404
  if (!VALID_RATIOS$1.includes(options.aspectRatio)) {
323
405
  console.error(chalk.red(`Invalid aspect ratio. Valid options: ${VALID_RATIOS$1.join(", ")}`));
324
406
  process.exit(1);
@@ -330,7 +412,8 @@ function createEditCommand(editUseCase) {
330
412
  const savedPath = await editUseCase.execute({
331
413
  prompt,
332
414
  imageSource,
333
- aspectRatio: options.aspectRatio
415
+ aspectRatio: options.aspectRatio,
416
+ model: options.model
334
417
  }, outputDir);
335
418
  spinner.succeed(chalk.green("Image edited successfully:"));
336
419
  console.log(chalk.dim(` ${savedPath}`));
@@ -347,6 +430,11 @@ function createEditCommand(editUseCase) {
347
430
 
348
431
  //#endregion
349
432
  //#region src/presentation/commands/generate.command.ts
433
+ const VALID_MODELS = [
434
+ "grok-2-image-1212",
435
+ "grok-imagine-image-pro",
436
+ "grok-imagine-image"
437
+ ];
350
438
  const VALID_RATIOS = [
351
439
  "1:1",
352
440
  "16:9",
@@ -364,12 +452,16 @@ const VALID_RATIOS = [
364
452
  "auto"
365
453
  ];
366
454
  function createGenerateCommand(generateUseCase) {
367
- return new Command("generate").description("Generate images from a text prompt").argument("<prompt>", "Text prompt describing the image to generate").option("-a, --aspect-ratio <ratio>", "Aspect ratio", "auto").option("-n, --count <number>", "Number of images (1-10)", "1").option("-o, --output <dir>", "Output directory", "./grok-images").action(async (prompt, options) => {
455
+ return new Command("generate").description("Generate images from a text prompt").argument("<prompt>", "Text prompt describing the image to generate").option("-m, --model <model>", "Model to use", "grok-imagine-image").option("-a, --aspect-ratio <ratio>", "Aspect ratio", "auto").option("-n, --count <number>", "Number of images (1-10)", "1").option("-o, --output <dir>", "Output directory", "./grok-images").action(async (prompt, options) => {
368
456
  const count = parseInt(options.count, 10);
369
457
  if (Number.isNaN(count) || count < 1 || count > 10) {
370
458
  console.error(chalk.red("Count must be a number between 1 and 10."));
371
459
  process.exit(1);
372
460
  }
461
+ if (!VALID_MODELS.includes(options.model)) {
462
+ console.error(chalk.red(`Invalid model. Valid options: ${VALID_MODELS.join(", ")}`));
463
+ process.exit(1);
464
+ }
373
465
  if (!VALID_RATIOS.includes(options.aspectRatio)) {
374
466
  console.error(chalk.red(`Invalid aspect ratio. Valid options: ${VALID_RATIOS.join(", ")}`));
375
467
  process.exit(1);
@@ -380,7 +472,8 @@ function createGenerateCommand(generateUseCase) {
380
472
  const paths = await generateUseCase.execute({
381
473
  prompt,
382
474
  count,
383
- aspectRatio: options.aspectRatio
475
+ aspectRatio: options.aspectRatio,
476
+ model: options.model
384
477
  }, outputDir);
385
478
  spinner.succeed(chalk.green(`Generated ${paths.length} image${paths.length > 1 ? "s" : ""}:`));
386
479
  for (const p of paths) console.log(chalk.dim(` ${p}`));
@@ -410,7 +503,20 @@ function createCli(useCases) {
410
503
 
411
504
  //#endregion
412
505
  //#region src/main.ts
413
- const keyStore = new KeychainAdapter();
506
+ const keyStore = new KeyStoreChain([
507
+ {
508
+ store: new KeychainAdapter(),
509
+ name: "Keychain"
510
+ },
511
+ {
512
+ store: new PassAdapter(),
513
+ name: "pass"
514
+ },
515
+ {
516
+ store: new EnvKeyStoreAdapter(),
517
+ name: "XAI_API_KEY"
518
+ }
519
+ ], (name) => console.log(chalk.dim(`Key stored in: ${name}`)));
414
520
  const imageGenerator = new GrokApiAdapter();
415
521
  const fileStorage = new FileStorageAdapter();
416
522
  createCli({
package/dist/main.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"main.mjs","names":["VALID_RATIOS"],"sources":["../src/domain/errors.ts","../src/application/usecases/edit-image.usecase.ts","../src/application/usecases/generate-image.usecase.ts","../src/application/usecases/get-auth-status.usecase.ts","../src/application/usecases/login.usecase.ts","../src/application/usecases/logout.usecase.ts","../src/infrastructure/adapters/file-storage.adapter.ts","../src/infrastructure/adapters/grok-api.adapter.ts","../src/infrastructure/adapters/keychain.adapter.ts","../src/presentation/commands/auth.command.ts","../src/presentation/commands/edit.command.ts","../src/presentation/commands/generate.command.ts","../src/presentation/cli.ts","../src/main.ts"],"sourcesContent":["export class ApiKeyMissingError extends Error {\n constructor() {\n super(\n \"API key not found. Run `grok-img auth login` or set XAI_API_KEY environment variable.\",\n )\n this.name = \"ApiKeyMissingError\"\n }\n}\n\nexport class ApiError extends Error {\n constructor(\n message: string,\n public readonly cause?: unknown,\n ) {\n super(message)\n this.name = \"ApiError\"\n }\n}\n\nexport class ImageNotFoundError extends Error {\n constructor(path: string) {\n super(`Image not found: ${path}`)\n this.name = \"ImageNotFoundError\"\n }\n}\n","import type { EditParams } from \"../../domain/entities/edit-params.js\"\nimport { ApiKeyMissingError } from \"../../domain/errors.js\"\nimport type { FileStoragePort } from \"../../domain/ports/file-storage.port.js\"\nimport type { ImageGeneratorPort } from \"../../domain/ports/image-generator.port.js\"\nimport type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport class EditImageUseCase {\n constructor(\n private readonly imageGenerator: ImageGeneratorPort,\n private readonly keyStore: KeyStorePort,\n private readonly fileStorage: FileStoragePort,\n ) {}\n\n async execute(params: EditParams, outputDir: string): Promise<string> {\n const apiKey = await this.keyStore.get()\n if (!apiKey) throw new ApiKeyMissingError()\n\n let imageSource = params.imageSource\n if (typeof imageSource === \"string\" && !imageSource.startsWith(\"http\")) {\n imageSource = await this.fileStorage.readImage(imageSource)\n }\n\n this.fileStorage.ensureDir(outputDir)\n\n const result = await this.imageGenerator.edit({ ...params, imageSource }, apiKey)\n\n const outputPath = this.fileStorage.generateOutputPath(outputDir, 0, result.mediaType)\n return this.fileStorage.saveImage(result.uint8Array, outputPath)\n }\n}\n","import type { GenerateParams } from \"../../domain/entities/generate-params.js\"\nimport { ApiKeyMissingError } from \"../../domain/errors.js\"\nimport type { FileStoragePort } from \"../../domain/ports/file-storage.port.js\"\nimport type { ImageGeneratorPort } from \"../../domain/ports/image-generator.port.js\"\nimport type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport class GenerateImageUseCase {\n constructor(\n private readonly imageGenerator: ImageGeneratorPort,\n private readonly keyStore: KeyStorePort,\n private readonly fileStorage: FileStoragePort,\n ) {}\n\n async execute(params: GenerateParams, outputDir: string): Promise<string[]> {\n const apiKey = await this.keyStore.get()\n if (!apiKey) throw new ApiKeyMissingError()\n\n this.fileStorage.ensureDir(outputDir)\n\n const results = await this.imageGenerator.generate(params, apiKey)\n\n const savedPaths: string[] = []\n for (let i = 0; i < results.length; i++) {\n const result = results[i]\n const outputPath = this.fileStorage.generateOutputPath(\n outputDir,\n i,\n result.mediaType,\n )\n const saved = await this.fileStorage.saveImage(result.uint8Array, outputPath)\n savedPaths.push(saved)\n }\n\n return savedPaths\n }\n}\n","import type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport type AuthStatus = {\n authenticated: boolean\n maskedKey: string | null\n source: \"keychain\" | \"env\" | null\n}\n\nexport class GetAuthStatusUseCase {\n constructor(private readonly keyStore: KeyStorePort) {}\n\n async execute(): Promise<AuthStatus> {\n const key = await this.keyStore.get()\n\n if (!key) {\n return { authenticated: false, maskedKey: null, source: null }\n }\n\n const masked =\n key.length > 8\n ? `${key.slice(0, 4)}${\"*\".repeat(key.length - 8)}${key.slice(-4)}`\n : \"****\"\n\n const source = process.env.XAI_API_KEY === key ? \"env\" : \"keychain\"\n\n return { authenticated: true, maskedKey: masked, source }\n }\n}\n","import type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport class LoginUseCase {\n constructor(private readonly keyStore: KeyStorePort) {}\n\n async execute(apiKey: string): Promise<void> {\n await this.keyStore.save(apiKey)\n }\n}\n","import type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport class LogoutUseCase {\n constructor(private readonly keyStore: KeyStorePort) {}\n\n async execute(): Promise<void> {\n await this.keyStore.remove()\n }\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport { ImageNotFoundError } from \"../../domain/errors.js\"\nimport type { FileStoragePort } from \"../../domain/ports/file-storage.port.js\"\n\nconst MEDIA_TYPE_EXT: Record<string, string> = {\n \"image/png\": \"png\",\n \"image/jpeg\": \"jpg\",\n \"image/webp\": \"webp\",\n \"image/gif\": \"gif\",\n}\n\nexport class FileStorageAdapter implements FileStoragePort {\n async saveImage(data: Uint8Array, outputPath: string): Promise<string> {\n writeFileSync(outputPath, data)\n return outputPath\n }\n\n async readImage(filePath: string): Promise<Uint8Array> {\n if (!existsSync(filePath)) {\n throw new ImageNotFoundError(filePath)\n }\n return new Uint8Array(readFileSync(filePath))\n }\n\n ensureDir(dir: string): void {\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true })\n }\n }\n\n generateOutputPath(dir: string, index: number, mediaType: string): string {\n const ext = MEDIA_TYPE_EXT[mediaType] ?? \"png\"\n const timestamp = Date.now()\n return join(dir, `grok-img-${timestamp}-${index}.${ext}`)\n }\n}\n","import { createXai } from \"@ai-sdk/xai\"\nimport { generateImage, NoImageGeneratedError } from \"ai\"\nimport type { EditParams } from \"../../domain/entities/edit-params.js\"\nimport type { GenerateParams } from \"../../domain/entities/generate-params.js\"\nimport type { ImageResult } from \"../../domain/entities/image-result.js\"\nimport { ApiError } from \"../../domain/errors.js\"\nimport type { ImageGeneratorPort } from \"../../domain/ports/image-generator.port.js\"\n\nconst MODEL = \"grok-imagine-image\"\n\nexport class GrokApiAdapter implements ImageGeneratorPort {\n async generate(params: GenerateParams, apiKey: string): Promise<ImageResult[]> {\n const xai = createXai({ apiKey })\n\n try {\n const { images } = await generateImage({\n model: xai.image(MODEL),\n prompt: params.prompt,\n aspectRatio: params.aspectRatio,\n n: params.count,\n })\n\n return images.map((img) => ({\n base64: img.base64,\n uint8Array: img.uint8Array,\n mediaType: img.mediaType,\n }))\n } catch (error) {\n if (NoImageGeneratedError.isInstance(error)) {\n throw new ApiError(\n \"Image generation failed: the model could not produce an image.\",\n error,\n )\n }\n if (error instanceof Error) {\n throw new ApiError(`API request failed: ${error.message}`, error)\n }\n throw new ApiError(\"An unexpected error occurred during image generation.\", error)\n }\n }\n\n async edit(params: EditParams, apiKey: string): Promise<ImageResult> {\n const xai = createXai({ apiKey })\n\n try {\n const { image } = await generateImage({\n model: xai.image(MODEL),\n prompt: {\n text: params.prompt,\n images:\n typeof params.imageSource === \"string\"\n ? [new URL(params.imageSource)]\n : [params.imageSource],\n },\n aspectRatio: params.aspectRatio,\n })\n\n return {\n base64: image.base64,\n uint8Array: image.uint8Array,\n mediaType: image.mediaType,\n }\n } catch (error) {\n if (NoImageGeneratedError.isInstance(error)) {\n throw new ApiError(\n \"Image editing failed: the model could not produce an image.\",\n error,\n )\n }\n if (error instanceof Error) {\n throw new ApiError(`API request failed: ${error.message}`, error)\n }\n throw new ApiError(\"An unexpected error occurred during image editing.\", error)\n }\n }\n}\n","import { execFile } from \"node:child_process\"\nimport { promisify } from \"node:util\"\nimport type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nconst execFileAsync = promisify(execFile)\n\nconst SERVICE = \"grok-image-cli\"\nconst ACCOUNT = \"api-key\"\n\nexport class KeychainAdapter implements KeyStorePort {\n async save(key: string): Promise<void> {\n await execFileAsync(\"security\", [\n \"add-generic-password\",\n \"-U\",\n \"-s\",\n SERVICE,\n \"-a\",\n ACCOUNT,\n \"-w\",\n key,\n ])\n }\n\n async get(): Promise<string | null> {\n try {\n const { stdout } = await execFileAsync(\"security\", [\n \"find-generic-password\",\n \"-s\",\n SERVICE,\n \"-a\",\n ACCOUNT,\n \"-w\",\n ])\n const key = stdout.trim()\n if (key) return key\n } catch {\n // not found in keychain\n }\n\n return process.env.XAI_API_KEY ?? null\n }\n\n async remove(): Promise<void> {\n try {\n await execFileAsync(\"security\", [\n \"delete-generic-password\",\n \"-s\",\n SERVICE,\n \"-a\",\n ACCOUNT,\n ])\n } catch {\n // already removed or never existed\n }\n }\n}\n","import { createInterface } from \"node:readline\"\nimport chalk from \"chalk\"\nimport { Command } from \"commander\"\nimport type { GetAuthStatusUseCase } from \"../../application/usecases/get-auth-status.usecase.js\"\nimport type { LoginUseCase } from \"../../application/usecases/login.usecase.js\"\nimport type { LogoutUseCase } from \"../../application/usecases/logout.usecase.js\"\n\nfunction readInput(prompt: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n })\n\n return new Promise((resolve) => {\n rl.question(prompt, (answer) => {\n rl.close()\n resolve(answer.trim())\n })\n })\n}\n\nexport function createAuthCommand(useCases: {\n login: LoginUseCase\n logout: LogoutUseCase\n getStatus: GetAuthStatusUseCase\n}): Command {\n const auth = new Command(\"auth\").description(\"Manage API key authentication\")\n\n auth\n .command(\"login\")\n .description(\"Store your xAI API key in the system keychain\")\n .action(async () => {\n try {\n const apiKey = await readInput(chalk.cyan(\"Enter your xAI API key: \"))\n\n if (!apiKey) {\n console.log(chalk.red(\"No API key provided.\"))\n process.exit(1)\n }\n\n await useCases.login.execute(apiKey)\n console.log(chalk.green(\"API key saved to keychain successfully.\"))\n } catch (error) {\n console.error(\n chalk.red(\n `Failed to save API key: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n ),\n )\n process.exit(1)\n }\n })\n\n auth\n .command(\"logout\")\n .description(\"Remove your xAI API key from the system keychain\")\n .action(async () => {\n try {\n await useCases.logout.execute()\n console.log(chalk.green(\"API key removed from keychain.\"))\n } catch (error) {\n console.error(\n chalk.red(\n `Failed to remove API key: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n ),\n )\n process.exit(1)\n }\n })\n\n auth\n .command(\"status\")\n .description(\"Check current authentication status\")\n .action(async () => {\n try {\n const status = await useCases.getStatus.execute()\n\n if (status.authenticated) {\n console.log(chalk.green(\"Authenticated\"))\n console.log(chalk.dim(` Key: ${status.maskedKey}`))\n console.log(chalk.dim(` Source: ${status.source}`))\n } else {\n console.log(chalk.yellow(\"Not authenticated\"))\n console.log(\n chalk.dim(\n \" Run `grok-img auth login` or set XAI_API_KEY environment variable.\",\n ),\n )\n }\n } catch (error) {\n console.error(\n chalk.red(\n `Failed to check status: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n ),\n )\n process.exit(1)\n }\n })\n\n return auth\n}\n","import { resolve } from \"node:path\"\nimport chalk from \"chalk\"\nimport { Command } from \"commander\"\nimport ora from \"ora\"\nimport type { EditImageUseCase } from \"../../application/usecases/edit-image.usecase.js\"\nimport { ApiError, ApiKeyMissingError, ImageNotFoundError } from \"../../domain/errors.js\"\n\nconst VALID_RATIOS = [\n \"1:1\",\n \"16:9\",\n \"9:16\",\n \"4:3\",\n \"3:4\",\n \"3:2\",\n \"2:3\",\n \"2:1\",\n \"1:2\",\n \"19.5:9\",\n \"9:19.5\",\n \"20:9\",\n \"9:20\",\n \"auto\",\n]\n\nexport function createEditCommand(editUseCase: EditImageUseCase): Command {\n return new Command(\"edit\")\n .description(\"Edit an existing image with a text prompt\")\n .argument(\"<prompt>\", \"Text prompt describing the edit to apply\")\n .requiredOption(\"-i, --image <path>\", \"Source image (local file path or URL)\")\n .option(\"-a, --aspect-ratio <ratio>\", \"Aspect ratio\", \"auto\")\n .option(\"-o, --output <dir>\", \"Output directory\", \"./grok-images\")\n .action(async (prompt: string, options) => {\n if (!VALID_RATIOS.includes(options.aspectRatio)) {\n console.error(\n chalk.red(`Invalid aspect ratio. Valid options: ${VALID_RATIOS.join(\", \")}`),\n )\n process.exit(1)\n }\n\n const imageSource = options.image.startsWith(\"http\")\n ? options.image\n : resolve(options.image)\n\n const outputDir = resolve(options.output)\n const spinner = ora(\"Editing image...\").start()\n\n try {\n const savedPath = await editUseCase.execute(\n { prompt, imageSource, aspectRatio: options.aspectRatio },\n outputDir,\n )\n\n spinner.succeed(chalk.green(\"Image edited successfully:\"))\n console.log(chalk.dim(` ${savedPath}`))\n } catch (error) {\n spinner.fail()\n if (error instanceof ApiKeyMissingError) {\n console.error(chalk.red(error.message))\n } else if (error instanceof ImageNotFoundError) {\n console.error(chalk.red(error.message))\n } else if (error instanceof ApiError) {\n console.error(chalk.red(`API Error: ${error.message}`))\n } else {\n console.error(\n chalk.red(\n `Unexpected error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n ),\n )\n }\n process.exit(1)\n }\n })\n}\n","import { resolve } from \"node:path\"\nimport chalk from \"chalk\"\nimport { Command } from \"commander\"\nimport ora from \"ora\"\nimport type { GenerateImageUseCase } from \"../../application/usecases/generate-image.usecase.js\"\nimport { ApiError, ApiKeyMissingError } from \"../../domain/errors.js\"\n\nconst VALID_RATIOS = [\n \"1:1\",\n \"16:9\",\n \"9:16\",\n \"4:3\",\n \"3:4\",\n \"3:2\",\n \"2:3\",\n \"2:1\",\n \"1:2\",\n \"19.5:9\",\n \"9:19.5\",\n \"20:9\",\n \"9:20\",\n \"auto\",\n]\n\nexport function createGenerateCommand(generateUseCase: GenerateImageUseCase): Command {\n return new Command(\"generate\")\n .description(\"Generate images from a text prompt\")\n .argument(\"<prompt>\", \"Text prompt describing the image to generate\")\n .option(\"-a, --aspect-ratio <ratio>\", \"Aspect ratio\", \"auto\")\n .option(\"-n, --count <number>\", \"Number of images (1-10)\", \"1\")\n .option(\"-o, --output <dir>\", \"Output directory\", \"./grok-images\")\n .action(async (prompt: string, options) => {\n const count = parseInt(options.count, 10)\n if (Number.isNaN(count) || count < 1 || count > 10) {\n console.error(chalk.red(\"Count must be a number between 1 and 10.\"))\n process.exit(1)\n }\n\n if (!VALID_RATIOS.includes(options.aspectRatio)) {\n console.error(\n chalk.red(`Invalid aspect ratio. Valid options: ${VALID_RATIOS.join(\", \")}`),\n )\n process.exit(1)\n }\n\n const outputDir = resolve(options.output)\n const spinner = ora(`Generating ${count} image${count > 1 ? \"s\" : \"\"}...`).start()\n\n try {\n const paths = await generateUseCase.execute(\n { prompt, count, aspectRatio: options.aspectRatio },\n outputDir,\n )\n\n spinner.succeed(\n chalk.green(`Generated ${paths.length} image${paths.length > 1 ? \"s\" : \"\"}:`),\n )\n for (const p of paths) {\n console.log(chalk.dim(` ${p}`))\n }\n } catch (error) {\n spinner.fail()\n if (error instanceof ApiKeyMissingError) {\n console.error(chalk.red(error.message))\n } else if (error instanceof ApiError) {\n console.error(chalk.red(`API Error: ${error.message}`))\n } else {\n console.error(\n chalk.red(\n `Unexpected error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n ),\n )\n }\n process.exit(1)\n }\n })\n}\n","import { Command } from \"commander\"\nimport type { EditImageUseCase } from \"../application/usecases/edit-image.usecase.js\"\nimport type { GenerateImageUseCase } from \"../application/usecases/generate-image.usecase.js\"\nimport type { GetAuthStatusUseCase } from \"../application/usecases/get-auth-status.usecase.js\"\nimport type { LoginUseCase } from \"../application/usecases/login.usecase.js\"\nimport type { LogoutUseCase } from \"../application/usecases/logout.usecase.js\"\nimport { createAuthCommand } from \"./commands/auth.command.js\"\nimport { createEditCommand } from \"./commands/edit.command.js\"\nimport { createGenerateCommand } from \"./commands/generate.command.js\"\n\nexport type UseCases = {\n generateImage: GenerateImageUseCase\n editImage: EditImageUseCase\n login: LoginUseCase\n logout: LogoutUseCase\n getAuthStatus: GetAuthStatusUseCase\n}\n\nexport function createCli(useCases: UseCases): Command {\n const program = new Command()\n .name(\"grok-img\")\n .description(\"CLI for generating and editing images with Grok API\")\n .version(\"1.0.0\")\n\n program.addCommand(\n createAuthCommand({\n login: useCases.login,\n logout: useCases.logout,\n getStatus: useCases.getAuthStatus,\n }),\n )\n\n program.addCommand(createGenerateCommand(useCases.generateImage))\n program.addCommand(createEditCommand(useCases.editImage))\n\n return program\n}\n","import { EditImageUseCase } from \"./application/usecases/edit-image.usecase.js\"\nimport { GenerateImageUseCase } from \"./application/usecases/generate-image.usecase.js\"\nimport { GetAuthStatusUseCase } from \"./application/usecases/get-auth-status.usecase.js\"\nimport { LoginUseCase } from \"./application/usecases/login.usecase.js\"\nimport { LogoutUseCase } from \"./application/usecases/logout.usecase.js\"\nimport { FileStorageAdapter } from \"./infrastructure/adapters/file-storage.adapter.js\"\nimport { GrokApiAdapter } from \"./infrastructure/adapters/grok-api.adapter.js\"\nimport { KeychainAdapter } from \"./infrastructure/adapters/keychain.adapter.js\"\nimport { createCli } from \"./presentation/cli.js\"\n\nconst keyStore = new KeychainAdapter()\nconst imageGenerator = new GrokApiAdapter()\nconst fileStorage = new FileStorageAdapter()\n\nconst generateImageUseCase = new GenerateImageUseCase(\n imageGenerator,\n keyStore,\n fileStorage,\n)\nconst editImageUseCase = new EditImageUseCase(imageGenerator, keyStore, fileStorage)\nconst loginUseCase = new LoginUseCase(keyStore)\nconst logoutUseCase = new LogoutUseCase(keyStore)\nconst getAuthStatusUseCase = new GetAuthStatusUseCase(keyStore)\n\nconst program = createCli({\n generateImage: generateImageUseCase,\n editImage: editImageUseCase,\n login: loginUseCase,\n logout: logoutUseCase,\n getAuthStatus: getAuthStatusUseCase,\n})\n\nprogram.parse()\n"],"mappings":"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"}
1
+ {"version":3,"file":"main.mjs","names":["VALID_MODELS","VALID_RATIOS"],"sources":["../src/domain/errors.ts","../src/application/usecases/edit-image.usecase.ts","../src/application/usecases/generate-image.usecase.ts","../src/application/usecases/get-auth-status.usecase.ts","../src/application/usecases/login.usecase.ts","../src/application/usecases/logout.usecase.ts","../src/infrastructure/adapters/env-key-store.adapter.ts","../src/infrastructure/adapters/file-storage.adapter.ts","../src/infrastructure/adapters/grok-api.adapter.ts","../src/infrastructure/adapters/keychain.adapter.ts","../src/infrastructure/adapters/pass.adapter.ts","../src/infrastructure/key-store-chain.ts","../src/presentation/commands/auth.command.ts","../src/presentation/commands/edit.command.ts","../src/presentation/commands/generate.command.ts","../src/presentation/cli.ts","../src/main.ts"],"sourcesContent":["export class ApiKeyMissingError extends Error {\n constructor() {\n super(\n \"API key not found. Run `grok-img auth login` or set XAI_API_KEY environment variable.\",\n )\n this.name = \"ApiKeyMissingError\"\n }\n}\n\nexport class ApiError extends Error {\n constructor(\n message: string,\n public readonly cause?: unknown,\n ) {\n super(message)\n this.name = \"ApiError\"\n }\n}\n\nexport class ImageNotFoundError extends Error {\n constructor(path: string) {\n super(`Image not found: ${path}`)\n this.name = \"ImageNotFoundError\"\n }\n}\n","import type { EditParams } from \"../../domain/entities/edit-params.js\"\nimport { ApiKeyMissingError } from \"../../domain/errors.js\"\nimport type { FileStoragePort } from \"../../domain/ports/file-storage.port.js\"\nimport type { ImageGeneratorPort } from \"../../domain/ports/image-generator.port.js\"\nimport type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport class EditImageUseCase {\n constructor(\n private readonly imageGenerator: ImageGeneratorPort,\n private readonly keyStore: KeyStorePort,\n private readonly fileStorage: FileStoragePort,\n ) {}\n\n async execute(params: EditParams, outputDir: string): Promise<string> {\n const apiKey = await this.keyStore.get()\n if (!apiKey) throw new ApiKeyMissingError()\n\n let imageSource = params.imageSource\n if (typeof imageSource === \"string\" && !imageSource.startsWith(\"http\")) {\n imageSource = await this.fileStorage.readImage(imageSource)\n }\n\n this.fileStorage.ensureDir(outputDir)\n\n const result = await this.imageGenerator.edit({ ...params, imageSource }, apiKey)\n\n const outputPath = this.fileStorage.generateOutputPath(outputDir, 0, result.mediaType)\n return this.fileStorage.saveImage(result.uint8Array, outputPath)\n }\n}\n","import type { GenerateParams } from \"../../domain/entities/generate-params.js\"\nimport { ApiKeyMissingError } from \"../../domain/errors.js\"\nimport type { FileStoragePort } from \"../../domain/ports/file-storage.port.js\"\nimport type { ImageGeneratorPort } from \"../../domain/ports/image-generator.port.js\"\nimport type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport class GenerateImageUseCase {\n constructor(\n private readonly imageGenerator: ImageGeneratorPort,\n private readonly keyStore: KeyStorePort,\n private readonly fileStorage: FileStoragePort,\n ) {}\n\n async execute(params: GenerateParams, outputDir: string): Promise<string[]> {\n const apiKey = await this.keyStore.get()\n if (!apiKey) throw new ApiKeyMissingError()\n\n this.fileStorage.ensureDir(outputDir)\n\n const results = await this.imageGenerator.generate(params, apiKey)\n\n const savedPaths: string[] = []\n for (let i = 0; i < results.length; i++) {\n const result = results[i]\n const outputPath = this.fileStorage.generateOutputPath(\n outputDir,\n i,\n result.mediaType,\n )\n const saved = await this.fileStorage.saveImage(result.uint8Array, outputPath)\n savedPaths.push(saved)\n }\n\n return savedPaths\n }\n}\n","import type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport type AuthStatus = {\n authenticated: boolean\n maskedKey: string | null\n source: \"credential-store\" | \"env\" | null\n}\n\nexport class GetAuthStatusUseCase {\n constructor(private readonly keyStore: KeyStorePort) {}\n\n async execute(): Promise<AuthStatus> {\n const key = await this.keyStore.get()\n\n if (!key) {\n return { authenticated: false, maskedKey: null, source: null }\n }\n\n const masked =\n key.length > 8\n ? `${key.slice(0, 4)}${\"*\".repeat(key.length - 8)}${key.slice(-4)}`\n : \"****\"\n\n const source = process.env.XAI_API_KEY === key ? \"env\" : \"credential-store\"\n\n return { authenticated: true, maskedKey: masked, source }\n }\n}\n","import type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport class LoginUseCase {\n constructor(private readonly keyStore: KeyStorePort) {}\n\n async execute(apiKey: string): Promise<void> {\n await this.keyStore.save(apiKey)\n }\n}\n","import type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport class LogoutUseCase {\n constructor(private readonly keyStore: KeyStorePort) {}\n\n async execute(): Promise<void> {\n await this.keyStore.remove()\n }\n}\n","import type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nexport class EnvKeyStoreAdapter implements KeyStorePort {\n async get(): Promise<string | null> {\n return process.env.XAI_API_KEY ?? null\n }\n\n async save(_key: string): Promise<void> {\n throw new Error(\"XAI_API_KEY is a read-only environment variable\")\n }\n\n async remove(): Promise<void> {\n // no-op: environment variables cannot be deleted programmatically\n }\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport { ImageNotFoundError } from \"../../domain/errors.js\"\nimport type { FileStoragePort } from \"../../domain/ports/file-storage.port.js\"\n\nconst MEDIA_TYPE_EXT: Record<string, string> = {\n \"image/png\": \"png\",\n \"image/jpeg\": \"jpg\",\n \"image/webp\": \"webp\",\n \"image/gif\": \"gif\",\n}\n\nexport class FileStorageAdapter implements FileStoragePort {\n async saveImage(data: Uint8Array, outputPath: string): Promise<string> {\n writeFileSync(outputPath, data)\n return outputPath\n }\n\n async readImage(filePath: string): Promise<Uint8Array> {\n if (!existsSync(filePath)) {\n throw new ImageNotFoundError(filePath)\n }\n return new Uint8Array(readFileSync(filePath))\n }\n\n ensureDir(dir: string): void {\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true })\n }\n }\n\n generateOutputPath(dir: string, index: number, mediaType: string): string {\n const ext = MEDIA_TYPE_EXT[mediaType] ?? \"png\"\n const timestamp = Date.now()\n return join(dir, `grok-img-${timestamp}-${index}.${ext}`)\n }\n}\n","import { createXai } from \"@ai-sdk/xai\"\nimport { generateImage, NoImageGeneratedError } from \"ai\"\nimport type { EditParams } from \"../../domain/entities/edit-params.js\"\nimport type { GenerateParams } from \"../../domain/entities/generate-params.js\"\nimport type { ImageResult } from \"../../domain/entities/image-result.js\"\nimport { ApiError } from \"../../domain/errors.js\"\nimport type { ImageGeneratorPort } from \"../../domain/ports/image-generator.port.js\"\n\nconst DEFAULT_MODEL = \"grok-imagine-image\"\n\nexport class GrokApiAdapter implements ImageGeneratorPort {\n async generate(params: GenerateParams, apiKey: string): Promise<ImageResult[]> {\n const xai = createXai({ apiKey })\n\n try {\n const { images } = await generateImage({\n model: xai.image(params.model ?? DEFAULT_MODEL),\n prompt: params.prompt,\n aspectRatio: params.aspectRatio as `${number}:${number}`,\n n: params.count,\n })\n\n return images.map((img) => ({\n base64: img.base64,\n uint8Array: img.uint8Array,\n mediaType: img.mediaType,\n }))\n } catch (error) {\n if (NoImageGeneratedError.isInstance(error)) {\n throw new ApiError(\n \"Image generation failed: the model could not produce an image.\",\n error,\n )\n }\n if (error instanceof Error) {\n throw new ApiError(`API request failed: ${error.message}`, error)\n }\n throw new ApiError(\"An unexpected error occurred during image generation.\", error)\n }\n }\n\n async edit(params: EditParams, apiKey: string): Promise<ImageResult> {\n const xai = createXai({ apiKey })\n\n try {\n const { image } = await generateImage({\n model: xai.image(params.model ?? DEFAULT_MODEL),\n prompt: {\n text: params.prompt,\n images: [params.imageSource],\n },\n aspectRatio: params.aspectRatio as `${number}:${number}`,\n })\n\n return {\n base64: image.base64,\n uint8Array: image.uint8Array,\n mediaType: image.mediaType,\n }\n } catch (error) {\n if (NoImageGeneratedError.isInstance(error)) {\n throw new ApiError(\n \"Image editing failed: the model could not produce an image.\",\n error,\n )\n }\n if (error instanceof Error) {\n throw new ApiError(`API request failed: ${error.message}`, error)\n }\n throw new ApiError(\"An unexpected error occurred during image editing.\", error)\n }\n }\n}\n","import { deletePassword, getPassword, setPassword } from \"cross-keychain\"\nimport type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nconst SERVICE = \"grok-image-cli\"\nconst ACCOUNT = \"api-key\"\n\nexport class KeychainAdapter implements KeyStorePort {\n async save(key: string): Promise<void> {\n await setPassword(SERVICE, ACCOUNT, key)\n }\n\n async get(): Promise<string | null> {\n const key = await getPassword(SERVICE, ACCOUNT)\n return key ?? null\n }\n\n async remove(): Promise<void> {\n await deletePassword(SERVICE, ACCOUNT)\n }\n}\n","import { execFileSync } from \"node:child_process\"\nimport type { KeyStorePort } from \"../../domain/ports/key-store.port.js\"\n\nconst SECRET_PATH = \"grok-image-cli/api-key\"\n\nfunction detectTool(): string | null {\n for (const tool of [\"gopass\", \"pass\"]) {\n try {\n execFileSync(\"which\", [tool], { stdio: \"ignore\" })\n return tool\n } catch {}\n }\n return null\n}\n\nconst tool = detectTool()\n\nexport class PassAdapter implements KeyStorePort {\n async save(key: string): Promise<void> {\n if (!tool) throw new Error(\"Neither gopass nor pass is installed\")\n\n const args =\n tool === \"gopass\"\n ? [\"insert\", \"-f\", SECRET_PATH]\n : [\"insert\", \"-m\", \"--force\", SECRET_PATH]\n\n execFileSync(tool, args, { input: `${key}\\n`, stdio: [\"pipe\", \"ignore\", \"ignore\"] })\n }\n\n async get(): Promise<string | null> {\n if (!tool) throw new Error(\"Neither gopass nor pass is installed\")\n\n try {\n const output = execFileSync(tool, [\"show\", SECRET_PATH], {\n encoding: \"utf-8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n })\n return output.trim().split(\"\\n\")[0] ?? null\n } catch {\n return null\n }\n }\n\n async remove(): Promise<void> {\n if (!tool) throw new Error(\"Neither gopass nor pass is installed\")\n\n try {\n execFileSync(tool, [\"rm\", \"--force\", SECRET_PATH], { stdio: \"ignore\" })\n } catch {}\n }\n}\n","import type { KeyStorePort } from \"../domain/ports/key-store.port.js\"\n\ntype NamedStore = { store: KeyStorePort; name: string }\n\nexport class KeyStoreChain implements KeyStorePort {\n constructor(\n private readonly stores: ReadonlyArray<NamedStore>,\n private readonly onSave?: (storeName: string) => void,\n ) {}\n\n async save(key: string): Promise<void> {\n let lastError: unknown\n for (const { store, name } of this.stores) {\n try {\n await store.save(key)\n this.onSave?.(name)\n return\n } catch (e) {\n lastError = e\n }\n }\n throw lastError\n }\n\n async get(): Promise<string | null> {\n for (const { store } of this.stores) {\n try {\n const key = await store.get()\n if (key) return key\n } catch {}\n }\n return null\n }\n\n async remove(): Promise<void> {\n for (const { store } of this.stores) {\n try {\n await store.remove()\n } catch {}\n }\n }\n}\n","import { createInterface } from \"node:readline\"\nimport chalk from \"chalk\"\nimport { Command } from \"commander\"\nimport type { GetAuthStatusUseCase } from \"../../application/usecases/get-auth-status.usecase.js\"\nimport type { LoginUseCase } from \"../../application/usecases/login.usecase.js\"\nimport type { LogoutUseCase } from \"../../application/usecases/logout.usecase.js\"\n\nfunction readInput(prompt: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n })\n\n return new Promise((resolve) => {\n rl.question(prompt, (answer) => {\n rl.close()\n resolve(answer.trim())\n })\n })\n}\n\nexport function createAuthCommand(useCases: {\n login: LoginUseCase\n logout: LogoutUseCase\n getStatus: GetAuthStatusUseCase\n}): Command {\n const auth = new Command(\"auth\").description(\"Manage API key authentication\")\n\n auth\n .command(\"login\")\n .description(\"Store your xAI API key in the system credential store\")\n .action(async () => {\n try {\n const apiKey = await readInput(chalk.cyan(\"Enter your xAI API key: \"))\n\n if (!apiKey) {\n console.log(chalk.red(\"No API key provided.\"))\n process.exit(1)\n }\n\n await useCases.login.execute(apiKey)\n console.log(chalk.green(\"API key saved successfully.\"))\n } catch (error) {\n console.error(\n chalk.red(\n `Failed to save API key: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n ),\n )\n process.exit(1)\n }\n })\n\n auth\n .command(\"logout\")\n .description(\"Remove your xAI API key from the system credential store\")\n .action(async () => {\n try {\n await useCases.logout.execute()\n console.log(chalk.green(\"API key removed successfully.\"))\n } catch (error) {\n console.error(\n chalk.red(\n `Failed to remove API key: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n ),\n )\n process.exit(1)\n }\n })\n\n auth\n .command(\"status\")\n .description(\"Check current authentication status\")\n .action(async () => {\n try {\n const status = await useCases.getStatus.execute()\n\n if (status.authenticated) {\n console.log(chalk.green(\"Authenticated\"))\n console.log(chalk.dim(` Key: ${status.maskedKey}`))\n console.log(chalk.dim(` Source: ${status.source}`))\n } else {\n console.log(chalk.yellow(\"Not authenticated\"))\n console.log(\n chalk.dim(\n \" Run `grok-img auth login` or set XAI_API_KEY environment variable.\",\n ),\n )\n }\n } catch (error) {\n console.error(\n chalk.red(\n `Failed to check status: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n ),\n )\n process.exit(1)\n }\n })\n\n return auth\n}\n","import { resolve } from \"node:path\"\nimport chalk from \"chalk\"\nimport { Command } from \"commander\"\nimport ora from \"ora\"\nimport type { EditImageUseCase } from \"../../application/usecases/edit-image.usecase.js\"\nimport type { Model } from \"../../domain/entities/generate-params.js\"\nimport { ApiError, ApiKeyMissingError, ImageNotFoundError } from \"../../domain/errors.js\"\n\nconst VALID_MODELS: Model[] = [\n \"grok-2-image-1212\",\n \"grok-imagine-image-pro\",\n \"grok-imagine-image\",\n]\n\nconst VALID_RATIOS = [\n \"1:1\",\n \"16:9\",\n \"9:16\",\n \"4:3\",\n \"3:4\",\n \"3:2\",\n \"2:3\",\n \"2:1\",\n \"1:2\",\n \"19.5:9\",\n \"9:19.5\",\n \"20:9\",\n \"9:20\",\n \"auto\",\n]\n\nexport function createEditCommand(editUseCase: EditImageUseCase): Command {\n return new Command(\"edit\")\n .description(\"Edit an existing image with a text prompt\")\n .argument(\"<prompt>\", \"Text prompt describing the edit to apply\")\n .requiredOption(\"-i, --image <path>\", \"Source image (local file path or URL)\")\n .option(\"-m, --model <model>\", \"Model to use\", \"grok-imagine-image\")\n .option(\"-a, --aspect-ratio <ratio>\", \"Aspect ratio\", \"auto\")\n .option(\"-o, --output <dir>\", \"Output directory\", \"./grok-images\")\n .action(async (prompt: string, options) => {\n if (!VALID_MODELS.includes(options.model)) {\n console.error(\n chalk.red(`Invalid model. Valid options: ${VALID_MODELS.join(\", \")}`),\n )\n process.exit(1)\n }\n\n if (!VALID_RATIOS.includes(options.aspectRatio)) {\n console.error(\n chalk.red(`Invalid aspect ratio. Valid options: ${VALID_RATIOS.join(\", \")}`),\n )\n process.exit(1)\n }\n\n const imageSource = options.image.startsWith(\"http\")\n ? options.image\n : resolve(options.image)\n\n const outputDir = resolve(options.output)\n const spinner = ora(\"Editing image...\").start()\n\n try {\n const savedPath = await editUseCase.execute(\n { prompt, imageSource, aspectRatio: options.aspectRatio, model: options.model },\n outputDir,\n )\n\n spinner.succeed(chalk.green(\"Image edited successfully:\"))\n console.log(chalk.dim(` ${savedPath}`))\n } catch (error) {\n spinner.fail()\n if (error instanceof ApiKeyMissingError) {\n console.error(chalk.red(error.message))\n } else if (error instanceof ImageNotFoundError) {\n console.error(chalk.red(error.message))\n } else if (error instanceof ApiError) {\n console.error(chalk.red(`API Error: ${error.message}`))\n } else {\n console.error(\n chalk.red(\n `Unexpected error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n ),\n )\n }\n process.exit(1)\n }\n })\n}\n","import { resolve } from \"node:path\"\nimport chalk from \"chalk\"\nimport { Command } from \"commander\"\nimport ora from \"ora\"\nimport type { GenerateImageUseCase } from \"../../application/usecases/generate-image.usecase.js\"\nimport type { Model } from \"../../domain/entities/generate-params.js\"\nimport { ApiError, ApiKeyMissingError } from \"../../domain/errors.js\"\n\nconst VALID_MODELS: Model[] = [\n \"grok-2-image-1212\",\n \"grok-imagine-image-pro\",\n \"grok-imagine-image\",\n]\n\nconst VALID_RATIOS = [\n \"1:1\",\n \"16:9\",\n \"9:16\",\n \"4:3\",\n \"3:4\",\n \"3:2\",\n \"2:3\",\n \"2:1\",\n \"1:2\",\n \"19.5:9\",\n \"9:19.5\",\n \"20:9\",\n \"9:20\",\n \"auto\",\n]\n\nexport function createGenerateCommand(generateUseCase: GenerateImageUseCase): Command {\n return new Command(\"generate\")\n .description(\"Generate images from a text prompt\")\n .argument(\"<prompt>\", \"Text prompt describing the image to generate\")\n .option(\"-m, --model <model>\", \"Model to use\", \"grok-imagine-image\")\n .option(\"-a, --aspect-ratio <ratio>\", \"Aspect ratio\", \"auto\")\n .option(\"-n, --count <number>\", \"Number of images (1-10)\", \"1\")\n .option(\"-o, --output <dir>\", \"Output directory\", \"./grok-images\")\n .action(async (prompt: string, options) => {\n const count = parseInt(options.count, 10)\n if (Number.isNaN(count) || count < 1 || count > 10) {\n console.error(chalk.red(\"Count must be a number between 1 and 10.\"))\n process.exit(1)\n }\n\n if (!VALID_MODELS.includes(options.model)) {\n console.error(\n chalk.red(`Invalid model. Valid options: ${VALID_MODELS.join(\", \")}`),\n )\n process.exit(1)\n }\n\n if (!VALID_RATIOS.includes(options.aspectRatio)) {\n console.error(\n chalk.red(`Invalid aspect ratio. Valid options: ${VALID_RATIOS.join(\", \")}`),\n )\n process.exit(1)\n }\n\n const outputDir = resolve(options.output)\n const spinner = ora(`Generating ${count} image${count > 1 ? \"s\" : \"\"}...`).start()\n\n try {\n const paths = await generateUseCase.execute(\n { prompt, count, aspectRatio: options.aspectRatio, model: options.model },\n outputDir,\n )\n\n spinner.succeed(\n chalk.green(`Generated ${paths.length} image${paths.length > 1 ? \"s\" : \"\"}:`),\n )\n for (const p of paths) {\n console.log(chalk.dim(` ${p}`))\n }\n } catch (error) {\n spinner.fail()\n if (error instanceof ApiKeyMissingError) {\n console.error(chalk.red(error.message))\n } else if (error instanceof ApiError) {\n console.error(chalk.red(`API Error: ${error.message}`))\n } else {\n console.error(\n chalk.red(\n `Unexpected error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n ),\n )\n }\n process.exit(1)\n }\n })\n}\n","import { Command } from \"commander\"\nimport type { EditImageUseCase } from \"../application/usecases/edit-image.usecase.js\"\nimport type { GenerateImageUseCase } from \"../application/usecases/generate-image.usecase.js\"\nimport type { GetAuthStatusUseCase } from \"../application/usecases/get-auth-status.usecase.js\"\nimport type { LoginUseCase } from \"../application/usecases/login.usecase.js\"\nimport type { LogoutUseCase } from \"../application/usecases/logout.usecase.js\"\nimport { createAuthCommand } from \"./commands/auth.command.js\"\nimport { createEditCommand } from \"./commands/edit.command.js\"\nimport { createGenerateCommand } from \"./commands/generate.command.js\"\n\nexport type UseCases = {\n generateImage: GenerateImageUseCase\n editImage: EditImageUseCase\n login: LoginUseCase\n logout: LogoutUseCase\n getAuthStatus: GetAuthStatusUseCase\n}\n\nexport function createCli(useCases: UseCases): Command {\n const program = new Command()\n .name(\"grok-img\")\n .description(\"CLI for generating and editing images with Grok API\")\n .version(\"1.0.0\")\n\n program.addCommand(\n createAuthCommand({\n login: useCases.login,\n logout: useCases.logout,\n getStatus: useCases.getAuthStatus,\n }),\n )\n\n program.addCommand(createGenerateCommand(useCases.generateImage))\n program.addCommand(createEditCommand(useCases.editImage))\n\n return program\n}\n","import chalk from \"chalk\"\nimport { EditImageUseCase } from \"./application/usecases/edit-image.usecase.js\"\nimport { GenerateImageUseCase } from \"./application/usecases/generate-image.usecase.js\"\nimport { GetAuthStatusUseCase } from \"./application/usecases/get-auth-status.usecase.js\"\nimport { LoginUseCase } from \"./application/usecases/login.usecase.js\"\nimport { LogoutUseCase } from \"./application/usecases/logout.usecase.js\"\nimport { EnvKeyStoreAdapter } from \"./infrastructure/adapters/env-key-store.adapter.js\"\nimport { FileStorageAdapter } from \"./infrastructure/adapters/file-storage.adapter.js\"\nimport { GrokApiAdapter } from \"./infrastructure/adapters/grok-api.adapter.js\"\nimport { KeychainAdapter } from \"./infrastructure/adapters/keychain.adapter.js\"\nimport { PassAdapter } from \"./infrastructure/adapters/pass.adapter.js\"\nimport { KeyStoreChain } from \"./infrastructure/key-store-chain.js\"\nimport { createCli } from \"./presentation/cli.js\"\n\nconst keyStore = new KeyStoreChain(\n [\n { store: new KeychainAdapter(), name: \"Keychain\" },\n { store: new PassAdapter(), name: \"pass\" },\n { store: new EnvKeyStoreAdapter(), name: \"XAI_API_KEY\" },\n ],\n (name) => console.log(chalk.dim(`Key stored in: ${name}`)),\n)\nconst imageGenerator = new GrokApiAdapter()\nconst fileStorage = new FileStorageAdapter()\n\nconst generateImageUseCase = new GenerateImageUseCase(\n imageGenerator,\n keyStore,\n fileStorage,\n)\nconst editImageUseCase = new EditImageUseCase(imageGenerator, keyStore, fileStorage)\nconst loginUseCase = new LoginUseCase(keyStore)\nconst logoutUseCase = new LogoutUseCase(keyStore)\nconst getAuthStatusUseCase = new GetAuthStatusUseCase(keyStore)\n\nconst program = createCli({\n generateImage: generateImageUseCase,\n editImage: editImageUseCase,\n login: loginUseCase,\n logout: logoutUseCase,\n getAuthStatus: getAuthStatusUseCase,\n})\n\nprogram.parse()\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grok-image-cli",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "CLI for generating and editing images with Grok API",
5
5
  "type": "module",
6
6
  "bin": {
@@ -47,6 +47,7 @@
47
47
  "ai": "6.0.80",
48
48
  "chalk": "5.6.2",
49
49
  "commander": "13.1.0",
50
+ "cross-keychain": "^1.1.0",
50
51
  "ora": "8.2.0"
51
52
  },
52
53
  "devDependencies": {