egaki 0.4.0 → 0.5.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/CHANGELOG.md +27 -0
- package/dist/cli.js +84 -9
- package/dist/model-catalog.d.ts.map +1 -1
- package/dist/model-catalog.js +21 -2
- package/package.json +1 -1
- package/skills/egaki/SKILL.md +10 -1
- package/src/cli.ts +94 -9
- package/src/model-catalog.ts +22 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 0.5.0
|
|
6
|
+
|
|
7
|
+
1. **Interactive model picker** — omitting `--model` now shows a select prompt with popular models instead of silently using a default:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Shows a model picker in the terminal
|
|
11
|
+
egaki image "a dreamy watercolor landscape" -o landscape.png
|
|
12
|
+
|
|
13
|
+
# Explicit model skips the picker
|
|
14
|
+
egaki image "a dreamy watercolor landscape" -m imagen-4.0-generate-001 -o landscape.png
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Works for both `egaki image` and `egaki video`. In non-interactive (piped/scripted) mode, the default model is used automatically so existing scripts are unaffected.
|
|
18
|
+
|
|
19
|
+
## 0.4.1
|
|
20
|
+
|
|
21
|
+
1. **Added `gpt-image-2` model** — OpenAI's new flagship image generation model (released 2026-04-21) is now available in the model catalog:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
egaki image "a dreamy watercolor landscape" -m gpt-image-2 -o landscape.png
|
|
25
|
+
egaki models --json | jq '.[] | select(.id == "gpt-image-2")'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Supports editing, inpainting, and multiple output images. Token-based pricing (~$0.053 for medium-quality 1024×1024).
|
|
29
|
+
|
|
30
|
+
2. **`chatgpt-image-latest` now tracks `gpt-image-2`** — the rolling alias has been updated from `gpt-image-1.5` to `gpt-image-2`, with pricing adjusted accordingly.
|
|
31
|
+
|
|
5
32
|
## 0.4.0
|
|
6
33
|
|
|
7
34
|
1. **ChatGPT login for OpenAI image generation** — use your ChatGPT subscription in `egaki` without a separate OpenAI platform API key:
|
package/dist/cli.js
CHANGED
|
@@ -11,6 +11,7 @@ import { goke } from 'goke';
|
|
|
11
11
|
import { z } from 'zod';
|
|
12
12
|
import dedent from 'string-dedent';
|
|
13
13
|
import { generateImage as aiGenerateImage, generateText, experimental_generateVideo as aiGenerateVideo, } from 'ai';
|
|
14
|
+
import { select, isCancel, cancel } from '@clack/prompts';
|
|
14
15
|
import fs from 'node:fs';
|
|
15
16
|
import path from 'node:path';
|
|
16
17
|
import pc from 'picocolors';
|
|
@@ -18,6 +19,7 @@ import { createParser } from 'eventsource-parser';
|
|
|
18
19
|
import pkg from '../package.json' with { type: 'json' };
|
|
19
20
|
import { injectCredentialsToEnv, PROVIDERS, getChatGptAuth, saveChatGptAuth, getKeyStatus, } from './credentials.js';
|
|
20
21
|
import { IMAGE_MODELS, VIDEO_MODELS, DEFAULT_MODEL, DEFAULT_VIDEO_MODEL, getModelConfig, createImageModel, createTextModel, createVideoModel, shouldUseResponsesApi, } from './models.js';
|
|
22
|
+
import { CATALOG } from './model-catalog.js';
|
|
21
23
|
import { VIDEO_CATALOG } from './video-model-catalog.js';
|
|
22
24
|
import { loginInteractive, loginNonInteractive, showLoginStatus, removeLogin, readKeyFromStdin, } from './login.js';
|
|
23
25
|
import { subscribeInteractive, subscribeNonInteractive, unsubscribe, showUsage, } from './subscription.js';
|
|
@@ -144,7 +146,7 @@ cli
|
|
|
144
146
|
from the model ID: imagen-* uses the image API, everything else uses
|
|
145
147
|
the text API with image output enabled.
|
|
146
148
|
`)
|
|
147
|
-
.option('-m, --model [model]', z.enum(IMAGE_MODELS).
|
|
149
|
+
.option('-m, --model [model]', z.enum(IMAGE_MODELS).describe('Model ID for generation. If omitted, shows an interactive picker (or uses default in non-TTY mode)'))
|
|
148
150
|
.option('-o, --output [path]', z
|
|
149
151
|
.string()
|
|
150
152
|
.default('egaki-output.png')
|
|
@@ -187,10 +189,7 @@ cli
|
|
|
187
189
|
.action(async (prompt, options) => {
|
|
188
190
|
// Inject stored API keys as env vars before calling the AI SDK.
|
|
189
191
|
injectCredentialsToEnv();
|
|
190
|
-
|
|
191
|
-
// options.model / options.output / options.count as `T | undefined` even
|
|
192
|
-
// though the runtime always resolves them. Apply the same defaults here.
|
|
193
|
-
const model = options.model ?? DEFAULT_MODEL;
|
|
192
|
+
const model = options.model ?? await resolveImageModel();
|
|
194
193
|
const outputPath = options.output ?? 'egaki-output.png';
|
|
195
194
|
const count = options.count ?? 1;
|
|
196
195
|
const config = getModelConfig(model);
|
|
@@ -261,7 +260,7 @@ cli
|
|
|
261
260
|
Agent note: video generation can be slow. When invoking this command from
|
|
262
261
|
automation, use a command timeout of at least 5 minutes.
|
|
263
262
|
`)
|
|
264
|
-
.option('-m, --model [model]', z.enum(VIDEO_MODELS).
|
|
263
|
+
.option('-m, --model [model]', z.enum(VIDEO_MODELS).describe('Video model ID for generation. If omitted, shows an interactive picker (or uses default in non-TTY mode)'))
|
|
265
264
|
.option('-o, --output [path]', z
|
|
266
265
|
.string()
|
|
267
266
|
.default('egaki-output.mp4')
|
|
@@ -291,8 +290,7 @@ cli
|
|
|
291
290
|
.example('egaki video "waves crashing on cliffs" -n 2 -o waves.mp4')
|
|
292
291
|
.action(async (prompt, options) => {
|
|
293
292
|
injectCredentialsToEnv();
|
|
294
|
-
|
|
295
|
-
const model = options.model ?? DEFAULT_VIDEO_MODEL;
|
|
293
|
+
const model = options.model ?? await resolveVideoModel();
|
|
296
294
|
const outputPath = options.output ?? 'egaki-output.mp4';
|
|
297
295
|
const count = options.count ?? 1;
|
|
298
296
|
const config = getModelConfig(model);
|
|
@@ -336,7 +334,6 @@ cli
|
|
|
336
334
|
.describe('Filter by model type: image (image+text-image), video, or all'))
|
|
337
335
|
.option('--json', 'Output as JSON instead of YAML')
|
|
338
336
|
.action(async (options) => {
|
|
339
|
-
const { CATALOG } = await import('./model-catalog.js');
|
|
340
337
|
const yaml = await import('js-yaml');
|
|
341
338
|
const providerStatuses = Object.fromEntries(Object.keys(PROVIDERS).map((provider) => [provider, getKeyStatus(provider)]));
|
|
342
339
|
let models = options.type === 'video'
|
|
@@ -392,6 +389,84 @@ cli
|
|
|
392
389
|
cli.help();
|
|
393
390
|
cli.version(pkg.version);
|
|
394
391
|
cli.parse();
|
|
392
|
+
// ─── interactive model pickers ──────────────────────────────────────────────
|
|
393
|
+
/** Curated image models shown in the interactive picker, ordered by recommendation. */
|
|
394
|
+
const FEATURED_IMAGE_MODELS = [
|
|
395
|
+
'imagen-4.0-generate-001',
|
|
396
|
+
'imagen-4.0-ultra-generate-001',
|
|
397
|
+
'imagen-4.0-fast-generate-001',
|
|
398
|
+
'gemini-2.5-flash-image',
|
|
399
|
+
'gemini-3.1-flash-image-preview',
|
|
400
|
+
'nano-banana-pro-preview',
|
|
401
|
+
'gpt-image-2',
|
|
402
|
+
'gpt-image-1.5',
|
|
403
|
+
'chatgpt-image-latest',
|
|
404
|
+
'flux-kontext-pro',
|
|
405
|
+
'flux-kontext-max',
|
|
406
|
+
'flux-pro-1.1-ultra',
|
|
407
|
+
'recraft-v4',
|
|
408
|
+
'dall-e-3',
|
|
409
|
+
'grok-imagine-image-pro',
|
|
410
|
+
];
|
|
411
|
+
/**
|
|
412
|
+
* Resolve the image model: show interactive picker in TTY, use default otherwise.
|
|
413
|
+
*/
|
|
414
|
+
async function resolveImageModel() {
|
|
415
|
+
const isTTY = process.stdin.isTTY && process.stderr.isTTY;
|
|
416
|
+
if (!isTTY)
|
|
417
|
+
return DEFAULT_MODEL;
|
|
418
|
+
const options = FEATURED_IMAGE_MODELS
|
|
419
|
+
.map((id) => CATALOG.find((m) => m.id === id))
|
|
420
|
+
.filter(Boolean)
|
|
421
|
+
.map((m) => {
|
|
422
|
+
const cost = m.cost.type === 'per-image'
|
|
423
|
+
? pc.dim(`$${m.cost.perImage}/img`)
|
|
424
|
+
: m.cost.type === 'per-token'
|
|
425
|
+
? pc.dim(`$${m.cost.outputPerM}/M out`)
|
|
426
|
+
: '';
|
|
427
|
+
return {
|
|
428
|
+
value: m.id,
|
|
429
|
+
label: `${m.name} ${cost}`,
|
|
430
|
+
hint: m.id,
|
|
431
|
+
};
|
|
432
|
+
});
|
|
433
|
+
const selected = await select({
|
|
434
|
+
message: 'Select an image model',
|
|
435
|
+
options,
|
|
436
|
+
});
|
|
437
|
+
if (isCancel(selected)) {
|
|
438
|
+
cancel('Cancelled.');
|
|
439
|
+
process.exit(0);
|
|
440
|
+
}
|
|
441
|
+
return selected;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Resolve the video model: show interactive picker in TTY, use default otherwise.
|
|
445
|
+
*/
|
|
446
|
+
async function resolveVideoModel() {
|
|
447
|
+
const isTTY = process.stdin.isTTY && process.stderr.isTTY;
|
|
448
|
+
if (!isTTY)
|
|
449
|
+
return DEFAULT_VIDEO_MODEL;
|
|
450
|
+
const options = VIDEO_CATALOG.map((m) => {
|
|
451
|
+
const dur = m.features.durationRangeSec
|
|
452
|
+
? pc.dim(`${m.features.durationRangeSec.min}-${m.features.durationRangeSec.max}s`)
|
|
453
|
+
: '';
|
|
454
|
+
return {
|
|
455
|
+
value: m.id,
|
|
456
|
+
label: `${m.name} ${dur}`,
|
|
457
|
+
hint: m.id,
|
|
458
|
+
};
|
|
459
|
+
});
|
|
460
|
+
const selected = await select({
|
|
461
|
+
message: 'Select a video model',
|
|
462
|
+
options,
|
|
463
|
+
});
|
|
464
|
+
if (isCancel(selected)) {
|
|
465
|
+
cancel('Cancelled.');
|
|
466
|
+
process.exit(0);
|
|
467
|
+
}
|
|
468
|
+
return selected;
|
|
469
|
+
}
|
|
395
470
|
function isUrl(input) {
|
|
396
471
|
return /^https?:\/\//i.test(input);
|
|
397
472
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-catalog.d.ts","sourceRoot":"","sources":["../src/model-catalog.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,WAAW,CAAA;IACjB,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,WAAW,CAAA;IACjB,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAA;IACjB,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,YAAY,GAAG,YAAY,CAAA;AAEnD,MAAM,MAAM,aAAa,GAAG;IAC1B,wCAAwC;IACxC,OAAO,EAAE,OAAO,CAAA;IAChB,mCAAmC;IACnC,UAAU,EAAE,OAAO,CAAA;IACnB,8BAA8B;IAC9B,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,kCAAkC;IAClC,IAAI,EAAE,OAAO,CAAA;IACb,uCAAuC;IACvC,cAAc,EAAE,OAAO,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAA;IAC1B,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,SAAS,CAAA;IACf,QAAQ,EAAE,aAAa,CAAA;CACxB,CAAA;AA+ED,eAAO,MAAM,OAAO,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"model-catalog.d.ts","sourceRoot":"","sources":["../src/model-catalog.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,WAAW,CAAA;IACjB,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,WAAW,CAAA;IACjB,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAA;IACjB,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,YAAY,GAAG,YAAY,CAAA;AAEnD,MAAM,MAAM,aAAa,GAAG;IAC1B,wCAAwC;IACxC,OAAO,EAAE,OAAO,CAAA;IAChB,mCAAmC;IACnC,UAAU,EAAE,OAAO,CAAA;IACnB,8BAA8B;IAC9B,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,kCAAkC;IAClC,IAAI,EAAE,OAAO,CAAA;IACb,uCAAuC;IACvC,cAAc,EAAE,OAAO,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAA;IAC1B,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,SAAS,CAAA;IACf,QAAQ,EAAE,aAAa,CAAA;CACxB,CAAA;AA+ED,eAAO,MAAM,OAAO,EAAE,UAAU,EAy6B/B,CAAA;AAMD,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAE5D"}
|
package/dist/model-catalog.js
CHANGED
|
@@ -437,14 +437,33 @@ export const CATALOG = [
|
|
|
437
437
|
multipleImages: true,
|
|
438
438
|
},
|
|
439
439
|
},
|
|
440
|
+
{
|
|
441
|
+
id: 'gpt-image-2',
|
|
442
|
+
name: 'GPT Image 2',
|
|
443
|
+
description: 'State-of-the-art image generation model with token-based pricing. ' +
|
|
444
|
+
'Cost varies by quality and size; $0.053 is the medium-quality 1024×1024 estimate.',
|
|
445
|
+
released: '2026-04-21',
|
|
446
|
+
...openaiImage,
|
|
447
|
+
// Token-based pricing: $30/1M output image tokens. Medium quality 1024×1024 ≈ $0.053.
|
|
448
|
+
cost: { type: 'per-image', perImage: 0.053 },
|
|
449
|
+
features: {
|
|
450
|
+
editing: true,
|
|
451
|
+
inpainting: true,
|
|
452
|
+
aspectRatios: [],
|
|
453
|
+
sizes: ['1024x1024', '1536x1024', '1024x1536'],
|
|
454
|
+
seed: false,
|
|
455
|
+
multipleImages: true,
|
|
456
|
+
},
|
|
457
|
+
},
|
|
440
458
|
{
|
|
441
459
|
id: 'chatgpt-image-latest',
|
|
442
460
|
name: 'ChatGPT Image',
|
|
443
461
|
description: 'Rolling-latest alias that tracks the newest ChatGPT image model. Currently points ' +
|
|
444
|
-
'to gpt-image-
|
|
462
|
+
'to gpt-image-2. Pricing may shift when the alias retargets to a newer model.',
|
|
445
463
|
released: '2026-01',
|
|
446
464
|
...openaiImage,
|
|
447
|
-
|
|
465
|
+
// Matches gpt-image-2 medium quality 1024×1024 estimate.
|
|
466
|
+
cost: { type: 'per-image', perImage: 0.053 },
|
|
448
467
|
features: {
|
|
449
468
|
editing: true,
|
|
450
469
|
inpainting: true,
|
package/package.json
CHANGED
package/skills/egaki/SKILL.md
CHANGED
|
@@ -45,6 +45,15 @@ the key with `egaki login --provider egaki --key egaki_...`.
|
|
|
45
45
|
When login requires a URL flow, run login in the background and send the login URL
|
|
46
46
|
to the user so they can complete auth interactively.
|
|
47
47
|
|
|
48
|
+
## Model selection
|
|
49
|
+
|
|
50
|
+
The `--model` / `-m` flag is **optional** on both `egaki image` and `egaki video`.
|
|
51
|
+
|
|
52
|
+
- **Interactive (TTY):** omitting `--model` shows a picker with popular models
|
|
53
|
+
- **Non-interactive (piped/scripted):** omitting `--model` uses a sensible default
|
|
54
|
+
|
|
55
|
+
Agents should always pass `-m` explicitly to avoid the interactive picker.
|
|
56
|
+
|
|
48
57
|
## Example commands
|
|
49
58
|
|
|
50
59
|
```bash
|
|
@@ -60,7 +69,7 @@ egaki subscribe
|
|
|
60
69
|
# check subscription usage
|
|
61
70
|
egaki usage
|
|
62
71
|
|
|
63
|
-
# generate an image
|
|
72
|
+
# generate an image (interactive model picker if TTY)
|
|
64
73
|
egaki image "a watercolor fox reading a map" -o fox.png
|
|
65
74
|
|
|
66
75
|
# select a model explicitly
|
package/src/cli.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
generateText,
|
|
16
16
|
experimental_generateVideo as aiGenerateVideo,
|
|
17
17
|
} from 'ai'
|
|
18
|
+
import { select, isCancel, cancel } from '@clack/prompts'
|
|
18
19
|
import fs from 'node:fs'
|
|
19
20
|
import path from 'node:path'
|
|
20
21
|
import pc from 'picocolors'
|
|
@@ -38,6 +39,7 @@ import {
|
|
|
38
39
|
createVideoModel,
|
|
39
40
|
shouldUseResponsesApi,
|
|
40
41
|
} from './models.js'
|
|
42
|
+
import { CATALOG } from './model-catalog.js'
|
|
41
43
|
import { VIDEO_CATALOG } from './video-model-catalog.js'
|
|
42
44
|
import {
|
|
43
45
|
loginInteractive,
|
|
@@ -225,7 +227,7 @@ cli
|
|
|
225
227
|
)
|
|
226
228
|
.option(
|
|
227
229
|
'-m, --model [model]',
|
|
228
|
-
z.enum(IMAGE_MODELS).
|
|
230
|
+
z.enum(IMAGE_MODELS).describe('Model ID for generation. If omitted, shows an interactive picker (or uses default in non-TTY mode)'),
|
|
229
231
|
)
|
|
230
232
|
.option(
|
|
231
233
|
'-o, --output [path]',
|
|
@@ -308,10 +310,7 @@ cli
|
|
|
308
310
|
// Inject stored API keys as env vars before calling the AI SDK.
|
|
309
311
|
injectCredentialsToEnv()
|
|
310
312
|
|
|
311
|
-
|
|
312
|
-
// options.model / options.output / options.count as `T | undefined` even
|
|
313
|
-
// though the runtime always resolves them. Apply the same defaults here.
|
|
314
|
-
const model = options.model ?? DEFAULT_MODEL
|
|
313
|
+
const model = options.model ?? await resolveImageModel()
|
|
315
314
|
const outputPath = options.output ?? 'egaki-output.png'
|
|
316
315
|
const count = options.count ?? 1
|
|
317
316
|
const config = getModelConfig(model)
|
|
@@ -391,7 +390,7 @@ cli
|
|
|
391
390
|
)
|
|
392
391
|
.option(
|
|
393
392
|
'-m, --model [model]',
|
|
394
|
-
z.enum(VIDEO_MODELS).
|
|
393
|
+
z.enum(VIDEO_MODELS).describe('Video model ID for generation. If omitted, shows an interactive picker (or uses default in non-TTY mode)'),
|
|
395
394
|
)
|
|
396
395
|
.option(
|
|
397
396
|
'-o, --output [path]',
|
|
@@ -455,8 +454,7 @@ cli
|
|
|
455
454
|
.action(async (prompt, options) => {
|
|
456
455
|
injectCredentialsToEnv()
|
|
457
456
|
|
|
458
|
-
|
|
459
|
-
const model = options.model ?? DEFAULT_VIDEO_MODEL
|
|
457
|
+
const model = options.model ?? await resolveVideoModel()
|
|
460
458
|
const outputPath = options.output ?? 'egaki-output.mp4'
|
|
461
459
|
const count = options.count ?? 1
|
|
462
460
|
const config = getModelConfig(model)
|
|
@@ -515,7 +513,6 @@ cli
|
|
|
515
513
|
)
|
|
516
514
|
.option('--json', 'Output as JSON instead of YAML')
|
|
517
515
|
.action(async (options) => {
|
|
518
|
-
const { CATALOG } = await import('./model-catalog.js')
|
|
519
516
|
const yaml = await import('js-yaml')
|
|
520
517
|
const providerStatuses = Object.fromEntries(
|
|
521
518
|
Object.keys(PROVIDERS).map((provider) => [provider, getKeyStatus(provider)]),
|
|
@@ -579,6 +576,94 @@ cli.help()
|
|
|
579
576
|
cli.version(pkg.version)
|
|
580
577
|
cli.parse()
|
|
581
578
|
|
|
579
|
+
// ─── interactive model pickers ──────────────────────────────────────────────
|
|
580
|
+
|
|
581
|
+
/** Curated image models shown in the interactive picker, ordered by recommendation. */
|
|
582
|
+
const FEATURED_IMAGE_MODELS = [
|
|
583
|
+
'imagen-4.0-generate-001',
|
|
584
|
+
'imagen-4.0-ultra-generate-001',
|
|
585
|
+
'imagen-4.0-fast-generate-001',
|
|
586
|
+
'gemini-2.5-flash-image',
|
|
587
|
+
'gemini-3.1-flash-image-preview',
|
|
588
|
+
'nano-banana-pro-preview',
|
|
589
|
+
'gpt-image-2',
|
|
590
|
+
'gpt-image-1.5',
|
|
591
|
+
'chatgpt-image-latest',
|
|
592
|
+
'flux-kontext-pro',
|
|
593
|
+
'flux-kontext-max',
|
|
594
|
+
'flux-pro-1.1-ultra',
|
|
595
|
+
'recraft-v4',
|
|
596
|
+
'dall-e-3',
|
|
597
|
+
'grok-imagine-image-pro',
|
|
598
|
+
]
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Resolve the image model: show interactive picker in TTY, use default otherwise.
|
|
602
|
+
*/
|
|
603
|
+
async function resolveImageModel(): Promise<string> {
|
|
604
|
+
const isTTY = process.stdin.isTTY && process.stderr.isTTY
|
|
605
|
+
if (!isTTY) return DEFAULT_MODEL
|
|
606
|
+
|
|
607
|
+
const options = FEATURED_IMAGE_MODELS
|
|
608
|
+
.map((id) => CATALOG.find((m) => m.id === id))
|
|
609
|
+
.filter(Boolean)
|
|
610
|
+
.map((m) => {
|
|
611
|
+
const cost = m!.cost.type === 'per-image'
|
|
612
|
+
? pc.dim(`$${m!.cost.perImage}/img`)
|
|
613
|
+
: m!.cost.type === 'per-token'
|
|
614
|
+
? pc.dim(`$${m!.cost.outputPerM}/M out`)
|
|
615
|
+
: ''
|
|
616
|
+
return {
|
|
617
|
+
value: m!.id,
|
|
618
|
+
label: `${m!.name} ${cost}`,
|
|
619
|
+
hint: m!.id,
|
|
620
|
+
}
|
|
621
|
+
})
|
|
622
|
+
|
|
623
|
+
const selected = await select({
|
|
624
|
+
message: 'Select an image model',
|
|
625
|
+
options,
|
|
626
|
+
})
|
|
627
|
+
|
|
628
|
+
if (isCancel(selected)) {
|
|
629
|
+
cancel('Cancelled.')
|
|
630
|
+
process.exit(0)
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
return selected
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Resolve the video model: show interactive picker in TTY, use default otherwise.
|
|
638
|
+
*/
|
|
639
|
+
async function resolveVideoModel(): Promise<string> {
|
|
640
|
+
const isTTY = process.stdin.isTTY && process.stderr.isTTY
|
|
641
|
+
if (!isTTY) return DEFAULT_VIDEO_MODEL
|
|
642
|
+
|
|
643
|
+
const options = VIDEO_CATALOG.map((m) => {
|
|
644
|
+
const dur = m.features.durationRangeSec
|
|
645
|
+
? pc.dim(`${m.features.durationRangeSec.min}-${m.features.durationRangeSec.max}s`)
|
|
646
|
+
: ''
|
|
647
|
+
return {
|
|
648
|
+
value: m.id,
|
|
649
|
+
label: `${m.name} ${dur}`,
|
|
650
|
+
hint: m.id,
|
|
651
|
+
}
|
|
652
|
+
})
|
|
653
|
+
|
|
654
|
+
const selected = await select({
|
|
655
|
+
message: 'Select a video model',
|
|
656
|
+
options,
|
|
657
|
+
})
|
|
658
|
+
|
|
659
|
+
if (isCancel(selected)) {
|
|
660
|
+
cancel('Cancelled.')
|
|
661
|
+
process.exit(0)
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
return selected
|
|
665
|
+
}
|
|
666
|
+
|
|
582
667
|
function isUrl(input: string): boolean {
|
|
583
668
|
return /^https?:\/\//i.test(input)
|
|
584
669
|
}
|
package/src/model-catalog.ts
CHANGED
|
@@ -506,15 +506,35 @@ export const CATALOG: ModelEntry[] = [
|
|
|
506
506
|
multipleImages: true,
|
|
507
507
|
},
|
|
508
508
|
},
|
|
509
|
+
{
|
|
510
|
+
id: 'gpt-image-2',
|
|
511
|
+
name: 'GPT Image 2',
|
|
512
|
+
description:
|
|
513
|
+
'State-of-the-art image generation model with token-based pricing. ' +
|
|
514
|
+
'Cost varies by quality and size; $0.053 is the medium-quality 1024×1024 estimate.',
|
|
515
|
+
released: '2026-04-21',
|
|
516
|
+
...openaiImage,
|
|
517
|
+
// Token-based pricing: $30/1M output image tokens. Medium quality 1024×1024 ≈ $0.053.
|
|
518
|
+
cost: { type: 'per-image', perImage: 0.053 },
|
|
519
|
+
features: {
|
|
520
|
+
editing: true,
|
|
521
|
+
inpainting: true,
|
|
522
|
+
aspectRatios: [],
|
|
523
|
+
sizes: ['1024x1024', '1536x1024', '1024x1536'],
|
|
524
|
+
seed: false,
|
|
525
|
+
multipleImages: true,
|
|
526
|
+
},
|
|
527
|
+
},
|
|
509
528
|
{
|
|
510
529
|
id: 'chatgpt-image-latest',
|
|
511
530
|
name: 'ChatGPT Image',
|
|
512
531
|
description:
|
|
513
532
|
'Rolling-latest alias that tracks the newest ChatGPT image model. Currently points ' +
|
|
514
|
-
'to gpt-image-
|
|
533
|
+
'to gpt-image-2. Pricing may shift when the alias retargets to a newer model.',
|
|
515
534
|
released: '2026-01',
|
|
516
535
|
...openaiImage,
|
|
517
|
-
|
|
536
|
+
// Matches gpt-image-2 medium quality 1024×1024 estimate.
|
|
537
|
+
cost: { type: 'per-image', perImage: 0.053 },
|
|
518
538
|
features: {
|
|
519
539
|
editing: true,
|
|
520
540
|
inpainting: true,
|