egaki 0.10.0 → 0.12.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/dist/cli/chatgpt-auth.d.ts +30 -5
  3. package/dist/cli/chatgpt-auth.d.ts.map +1 -1
  4. package/dist/cli/chatgpt-auth.js +257 -79
  5. package/dist/cli/cli.d.ts.map +1 -1
  6. package/dist/cli/cli.js +97 -30
  7. package/dist/cli/generate.d.ts +10 -2
  8. package/dist/cli/generate.d.ts.map +1 -1
  9. package/dist/cli/generate.js +172 -59
  10. package/dist/cli/generate.test.js +50 -1
  11. package/dist/cli/login.d.ts +38 -3
  12. package/dist/cli/login.d.ts.map +1 -1
  13. package/dist/cli/login.js +44 -17
  14. package/dist/cli/login.test.d.ts +2 -0
  15. package/dist/cli/login.test.d.ts.map +1 -0
  16. package/dist/cli/login.test.js +158 -0
  17. package/dist/cli/model-catalog.d.ts.map +1 -1
  18. package/dist/cli/model-catalog.js +121 -5
  19. package/dist/cli/open-browser.d.ts +3 -1
  20. package/dist/cli/open-browser.d.ts.map +1 -1
  21. package/dist/cli/open-browser.js +2 -2
  22. package/dist/cli/xai-auth.d.ts +3 -1
  23. package/dist/cli/xai-auth.d.ts.map +1 -1
  24. package/dist/cli/xai-auth.js +13 -3
  25. package/dist/vite/generated-media-client.d.ts.map +1 -1
  26. package/dist/vite/generated-media-client.js +2 -2
  27. package/dist/vite/vite-plugin.d.ts.map +1 -1
  28. package/dist/vite/vite-plugin.js +20 -1
  29. package/package.json +7 -7
  30. package/skills/egaki/SKILL.md +14 -0
  31. package/src/cli/chatgpt-auth.ts +379 -89
  32. package/src/cli/cli.ts +115 -29
  33. package/src/cli/generate.test.ts +57 -0
  34. package/src/cli/generate.ts +198 -67
  35. package/src/cli/login.test.ts +185 -0
  36. package/src/cli/login.ts +68 -16
  37. package/src/cli/model-catalog.ts +128 -5
  38. package/src/cli/open-browser.ts +7 -6
  39. package/src/cli/xai-auth.ts +15 -3
  40. package/src/vite/generated-media-client.tsx +6 -1
  41. package/src/vite/vite-plugin.ts +20 -1
package/dist/cli/cli.js CHANGED
@@ -7,19 +7,18 @@
7
7
  // - imagen-* models → generateImage() with google.image()
8
8
  // - all other models → generateText() with responseModalities: ['IMAGE']
9
9
  // The CLI auto-detects which path to use based on model ID prefix.
10
- import { goke } from 'goke';
10
+ import { colors as pc, goke, isAgent, } from 'goke';
11
11
  import { z } from 'zod';
12
12
  import dedent from 'string-dedent';
13
13
  import { APICallError, NoImageGeneratedError, NoVideoGeneratedError, RetryError, } from 'ai';
14
14
  import { select, isCancel, cancel } from '@clack/prompts';
15
15
  import fs from 'node:fs';
16
16
  import path from 'node:path';
17
- import pc from 'picocolors';
18
17
  import pkg from '../../package.json' with { type: 'json' };
19
18
  import { injectCredentialsToEnv, PROVIDERS, getKeyStatus, } from './credentials.js';
20
19
  import { DEFAULT_MODEL, DEFAULT_VIDEO_MODEL, getModelConfig, } from './models.js';
21
20
  import { CATALOG, VIDEO_CATALOG, describeProviderValues, } from './model-catalog.js';
22
- import { loginInteractive, loginNonInteractive, showLoginStatus, removeLogin, readKeyFromStdin, } from './login.js';
21
+ import { loginInteractive, loginNonInteractive, resolveLoginAction, showLoginStatus, removeLogin, readKeyFromStdin, } from './login.js';
23
22
  import { subscribeInteractive, subscribeNonInteractive, unsubscribe, showUsage, } from './subscription.js';
24
23
  import { generateImageUncached as generateImage, generateVideoUncached as generateVideo, calculateCost, ValidationError, } from './generate.js';
25
24
  import { generateSpeechUncached as generateSpeech, } from './speech-generate.js';
@@ -47,8 +46,8 @@ cli
47
46
  .command('login', dedent `
48
47
  Configure API keys for image generation providers.
49
48
  Interactive mode: shows a provider picker and secure key input.
50
- Non-interactive mode: pass --provider and --key flags, or pipe key via stdin.
51
- Keys are saved to ~/.config/egaki/credentials.json (mode 0600).
49
+ Non-interactive mode: pass \`--provider\` and \`--key\`, or pipe the key via stdin.
50
+ Browser OAuth runs in the background for AI agents and non-TTY shells.
52
51
  `)
53
52
  .option('-p, --provider [name]', z
54
53
  .string()
@@ -67,29 +66,84 @@ cli
67
66
  .example('egaki login --show')
68
67
  .example('# Remove a stored key')
69
68
  .example('egaki login --remove google')
70
- .action(async (options) => {
71
- if (options.show) {
72
- showLoginStatus();
69
+ .action(async (options, ctx) => {
70
+ const action = resolveLoginAction({
71
+ options,
72
+ daemonProvider: ctx.daemon.isDaemon
73
+ ? ctx.process.env.EGAKI_LOGIN_PROVIDER
74
+ : undefined,
75
+ isAgent: isAgent || !process.stdin.isTTY,
76
+ });
77
+ if (action instanceof Error) {
78
+ ctx.console.error(action.message);
79
+ ctx.process.exit(1);
73
80
  return;
74
81
  }
75
- if (options.remove) {
76
- removeLogin(options.remove);
82
+ if (action.type === 'show') {
83
+ showLoginStatus({ loginRunning: await ctx.daemon.isRunning() });
77
84
  return;
78
85
  }
79
- // Non-interactive: --provider + --key or stdin
80
- if (options.provider) {
81
- // OAuth providers use browser flow — skip key reading
82
- if (options.provider === 'chatgpt' || options.provider === 'xai-oauth') {
83
- await loginNonInteractive({ provider: options.provider, key: '' });
84
- return;
85
- }
86
- const key = options.key || (await readKeyFromStdin());
87
- await loginNonInteractive({ provider: options.provider, key });
86
+ if (action.type === 'remove') {
87
+ removeLogin(action.provider);
88
+ return;
89
+ }
90
+ if (action.type === 'oauth-daemon') {
91
+ await loginNonInteractive({
92
+ provider: action.provider,
93
+ key: '',
94
+ background: true,
95
+ });
96
+ return;
97
+ }
98
+ if (action.type === 'oauth-client') {
99
+ await startOAuthLogin({ provider: action.provider, ctx });
88
100
  return;
89
101
  }
90
- // Interactive mode
91
- await loginInteractive();
102
+ if (action.type === 'api-key') {
103
+ await loginNonInteractive({
104
+ provider: action.provider,
105
+ key: action.key || (await readKeyFromStdin()),
106
+ });
107
+ return;
108
+ }
109
+ await loginInteractive({
110
+ loginOAuth: (provider) => startOAuthLogin({ provider, ctx }),
111
+ });
92
112
  });
113
+ cli
114
+ .command('login status <provider>', 'Check one provider login. Exits with status 1 when login is not complete.')
115
+ .example('egaki login status chatgpt')
116
+ .example('egaki login status xai-oauth')
117
+ .action(async (provider, _options, ctx) => {
118
+ const info = PROVIDERS[provider];
119
+ if (!info) {
120
+ ctx.console.error(`Unknown provider: ${provider}`);
121
+ ctx.process.exit(1);
122
+ return;
123
+ }
124
+ const status = getKeyStatus(provider);
125
+ if (status.available) {
126
+ ctx.console.log(`${info.label} is configured.`);
127
+ return;
128
+ }
129
+ const loginRunning = await ctx.daemon.forCommand('login').isRunning();
130
+ ctx.console.error(loginRunning
131
+ ? `An OAuth login is running, but ${info.label} is not configured yet.`
132
+ : `${info.label} is not configured.`);
133
+ ctx.process.exit(1);
134
+ });
135
+ async function startOAuthLogin({ provider, ctx, }) {
136
+ const runInBackground = isAgent || !process.stdin.isTTY;
137
+ await ctx.daemon.start({
138
+ attach: !runInBackground,
139
+ timeoutMs: 10 * 60 * 1000,
140
+ env: { EGAKI_LOGIN_PROVIDER: provider },
141
+ });
142
+ if (!runInBackground)
143
+ return;
144
+ ctx.console.log('Login is running in the background and the browser will open.');
145
+ ctx.console.log(`After approval, verify with: egaki login status ${provider}`);
146
+ }
93
147
  // ─── subscribe command ───────────────────────────────────────────────────────
94
148
  cli
95
149
  .command('subscribe', dedent `
@@ -97,17 +151,18 @@ cli
97
151
  You can also use your own provider keys (Google/OpenAI/Replicate/Fal)
98
152
  via 'egaki login --provider <name> --key <key>' if you prefer BYOK.
99
153
  Egaki subscription avoids managing one key per provider.
100
- Three plans: Starter ($9/mo, 100 credits), Pro ($29/mo, 500 credits),
101
- Unlimited ($99/mo, 2000 credits). One credit ≈ one standard image.
154
+ Two plans: Plus ($29/mo) and Pro ($99/mo). Each plan includes a
155
+ monthly dollar budget of generation usage equal to its price;
156
+ generations count their marked-up provider cost against the budget.
102
157
  Interactive mode: pick a plan and get a checkout URL (email prefill optional).
103
158
  Non-interactive: --email is optional and only pre-fills checkout.
104
159
  `)
105
160
  .option('-e, --email [email]', z.string().describe('Optional email prefill for checkout (skips interactive prompt)'))
106
- .option('--plan [plan]', z.string().describe('Plan ID: starter, pro, or unlimited (default: pro)'))
161
+ .option('--plan [plan]', z.enum(['plus', 'pro']).describe('Plan ID: plus ($29/mo) or pro ($99/mo)'))
107
162
  .example('# Interactive subscribe')
108
163
  .example('egaki subscribe')
109
164
  .example('# Non-interactive (for agents)')
110
- .example('egaki subscribe --email user@example.com --plan pro')
165
+ .example('egaki subscribe --email user@example.com --plan plus')
111
166
  .example('# Non-interactive without email prefill')
112
167
  .example('egaki subscribe --plan pro')
113
168
  .action(async (options) => {
@@ -131,8 +186,8 @@ cli
131
186
  // ─── usage command ───────────────────────────────────────────────────────────
132
187
  cli
133
188
  .command('usage', dedent `
134
- Show your current Egaki credit usage for this billing period.
135
- Displays plan, credits used, credits remaining, and period info.
189
+ Show your current Egaki usage for this billing period.
190
+ Displays plan, dollars spent, remaining budget, and period info.
136
191
  `)
137
192
  .example('egaki usage')
138
193
  .action(async () => {
@@ -172,6 +227,15 @@ cli
172
227
  console.log(` ${pc.dim('root:')} ${result.root}`);
173
228
  console.log(` ${pc.dim('url:')} ${pc.cyan(result.url)}`);
174
229
  console.log();
230
+ console.log(` ${pc.dim('SDK available in browser console as')} ${pc.yellow('window.egakiSDK')}`);
231
+ console.log(` ${pc.dim('.seekTo(frame)')} ${pc.dim('jump to a frame')}`);
232
+ console.log(` ${pc.dim('.screenshot()')} ${pc.dim('capture frame as data URL')}`);
233
+ console.log(` ${pc.dim('.filmstrip({...})')} ${pc.dim('grid of frames across scenes')}`);
234
+ console.log(` ${pc.dim('.export({...})')} ${pc.dim('render MP4 via WebCodecs')}`);
235
+ console.log(` ${pc.dim('.getInfo()')} ${pc.dim('composition metadata')}`);
236
+ console.log();
237
+ console.log(` ${pc.dim('Docs:')} ${pc.cyan('https://raw.githubusercontent.com/remorses/egaki/main/cli/skills/egaki/SKILL.md')}`);
238
+ console.log();
175
239
  if (options.open) {
176
240
  const { openUrlInBrowser } = await import('./open-browser.js');
177
241
  await openUrlInBrowser(result.url);
@@ -194,7 +258,7 @@ cli
194
258
  .option('-n, --count [n]', z.number().default(1).describe('Number of images to generate'))
195
259
  .option('--aspect-ratio [ratio]', z
196
260
  .string()
197
- .describe('Aspect ratio for the generated image. Imagen supports: 1:1, 3:4, 4:3, 9:16, 16:9. Gemini supports additional ratios: 2:3, 3:2, 4:5, 5:4, 21:9'))
261
+ .describe('Aspect ratio for the generated image. Imagen supports: 1:1, 3:4, 4:3, 9:16, 16:9. Gemini supports additional ratios: 2:3, 3:2, 4:5, 5:4, 21:9. OpenAI models (gpt-image-*, dall-e-*) have no native aspect ratio parameter, so the ratio is snapped to the closest supported size and stated in the prompt text'))
198
262
  .option('--seed [seed]', z.number().describe('Seed for reproducible generation. Same seed + same prompt = same image'))
199
263
  .option('--image-size [size]', z
200
264
  .enum(['1K', '2K', '4K'])
@@ -320,7 +384,7 @@ cli
320
384
  .describe(`Video operation mode. ${describeProviderValues('mode', [VIDEO_CATALOG])}`))
321
385
  .option('--reference-images [path]', z
322
386
  .array(z.string())
323
- .describe('Reference images for R2V generation, repeatable. xAI: 1-7 images. Seedance: 1-9 images (use @Image1 etc. in prompt)'))
387
+ .describe('Reference images for R2V generation, repeatable. xAI: 1-7 images (1-14 on grok-imagine-video-1.5). Seedance: 1-9 images (use @Image1 etc. in prompt)'))
324
388
  .option('--negative-prompt [text]', z
325
389
  .string()
326
390
  .describe('Describe what to avoid in the generated video (Fal models)'))
@@ -1012,6 +1076,7 @@ const UPLOAD_GATEWAY_BASE = 'https://egaki.org';
1012
1076
  /** Max upload size in bytes (must match gateway limit). */
1013
1077
  const MAX_UPLOAD_SIZE = 100 * 1024 * 1024;
1014
1078
  cli.help();
1079
+ cli.completions();
1015
1080
  cli.version(pkg.version);
1016
1081
  export { cli };
1017
1082
  async function runProviderCall(operation, call) {
@@ -1158,6 +1223,8 @@ const FEATURED_IMAGE_MODELS = [
1158
1223
  'gemini-2.5-flash-image',
1159
1224
  'gemini-3.1-flash-image-preview',
1160
1225
  'nano-banana-pro-preview',
1226
+ 'gpt-image-2.5-sunburst',
1227
+ 'gpt-image-2.5-flare',
1161
1228
  'gpt-image-2',
1162
1229
  'gpt-image-1.5',
1163
1230
  'chatgpt-image-latest',
@@ -1166,7 +1233,7 @@ const FEATURED_IMAGE_MODELS = [
1166
1233
  'flux-pro-1.1-ultra',
1167
1234
  'recraft-v4',
1168
1235
  'dall-e-3',
1169
- 'grok-imagine-image-quality',
1236
+ 'grok-imagine-image-2.0',
1170
1237
  ];
1171
1238
  /**
1172
1239
  * Resolve the image model: show interactive picker in TTY, use default otherwise.
@@ -1,9 +1,9 @@
1
1
  import { ValidationError } from './models.js';
2
2
  export { ValidationError };
3
3
  /** Image model IDs from the catalog. Accepts arbitrary strings too. */
4
- export type ImageModelId = 'imagen-4.0-generate-001' | 'imagen-4.0-ultra-generate-001' | 'imagen-4.0-fast-generate-001' | 'gemini-2.0-flash-exp-image-generation' | 'gemini-2.5-flash-image' | 'gemini-3-pro-image-preview' | 'gemini-3.1-flash-image-preview' | 'vertex/imagen-4.0-generate-001' | 'vertex/imagen-4.0-ultra-generate-001' | 'vertex/imagen-4.0-fast-generate-001' | 'vertex/gemini-2.5-flash-image' | 'vertex/gemini-3-pro-image-preview' | 'vertex/gemini-3.1-flash-image-preview' | 'flux-kontext-max' | 'flux-kontext-pro' | 'flux-pro-1.0-fill' | 'flux-pro-1.1' | 'flux-pro-1.1-ultra' | 'flux-2-pro' | 'flux-2-max' | 'flux-2-flex' | 'flux-2-klein-9b' | 'flux-2-klein-4b' | 'recraft-v2' | 'recraft-v3' | 'recraft-v4' | 'recraft-v4-pro' | 'recraft-v4.1' | 'recraft-v4.1-pro' | 'recraft-v4.1-utility' | 'recraft-v4.1-utility-pro' | 'seedream-5.0-lite' | 'seedream-4.5' | 'seedream-4.0' | 'grok-imagine-image' | 'grok-imagine-image-quality' | 'dall-e-2' | 'dall-e-3' | 'gpt-image-1' | 'gpt-image-1-mini' | 'gpt-image-1.5' | 'gpt-image-2' | 'chatgpt-image-latest' | (string & {});
4
+ export type ImageModelId = 'imagen-4.0-generate-001' | 'imagen-4.0-ultra-generate-001' | 'imagen-4.0-fast-generate-001' | 'gemini-2.0-flash-exp-image-generation' | 'gemini-2.5-flash-image' | 'gemini-3-pro-image-preview' | 'gemini-3.1-flash-image-preview' | 'vertex/imagen-4.0-generate-001' | 'vertex/imagen-4.0-ultra-generate-001' | 'vertex/imagen-4.0-fast-generate-001' | 'vertex/gemini-2.5-flash-image' | 'vertex/gemini-3-pro-image-preview' | 'vertex/gemini-3.1-flash-image-preview' | 'flux-kontext-max' | 'flux-kontext-pro' | 'flux-pro-1.0-fill' | 'flux-pro-1.1' | 'flux-pro-1.1-ultra' | 'flux-2-pro' | 'flux-2-max' | 'flux-2-flex' | 'flux-2-klein-9b' | 'flux-2-klein-4b' | 'recraft-v2' | 'recraft-v3' | 'recraft-v4' | 'recraft-v4-pro' | 'recraft-v4.1' | 'recraft-v4.1-pro' | 'recraft-v4.1-utility' | 'recraft-v4.1-utility-pro' | 'seedream-5.0-lite' | 'seedream-4.5' | 'seedream-4.0' | 'grok-imagine-image' | 'grok-imagine-image-2.0' | 'grok-imagine-image-quality' | 'dall-e-2' | 'dall-e-3' | 'gpt-image-1' | 'gpt-image-1-mini' | 'gpt-image-1.5' | 'gpt-image-2' | 'gpt-image-2.5-flare' | 'gpt-image-2.5-sunburst' | 'chatgpt-image-latest' | (string & {});
5
5
  /** Video model IDs from the catalog. Accepts arbitrary strings too. */
6
- export type VideoModelId = 'veo-3.1-generate-001' | 'veo-3.1-fast-generate-001' | 'veo-3.0-generate-001' | 'veo-3.0-fast-generate-001' | 'vertex/veo-3.1-generate-001' | 'vertex/veo-3.1-fast-generate-001' | 'grok-imagine-video' | 'grok-imagine-video-1.5' | 'kling-v3.0-t2v' | 'kling-v3.0-i2v' | 'kling-v3.0-motion-control' | 'kling-v2.6-t2v' | 'kling-v2.6-i2v' | 'kling-v2.6-motion-control' | 'kling-v2.5-turbo-t2v' | 'kling-v2.5-turbo-i2v' | 'wan-v2.6-t2v' | 'wan-v2.6-i2v' | 'wan-v2.6-i2v-flash' | 'wan-v2.6-r2v' | 'wan-v2.6-r2v-flash' | 'wan-v2.5-t2v-preview' | 'seedance-2.0' | 'seedance-2.0-fast' | 'seedance-v1.5-pro' | 'seedance-v1.0-pro' | 'seedance-v1.0-pro-fast' | 'seedance-v1.0-lite-t2v' | 'seedance-v1.0-lite-i2v' | 'luma-ray-2' | 'minimax-video' | 'hunyuan-video' | (string & {});
6
+ export type VideoModelId = 'veo-3.1-generate-001' | 'veo-3.1-fast-generate-001' | 'veo-3.0-generate-001' | 'veo-3.0-fast-generate-001' | 'vertex/veo-3.1-generate-001' | 'vertex/veo-3.1-fast-generate-001' | 'grok-imagine-video' | 'grok-imagine-video-1.5' | 'grok-imagine-video-1.5-lite' | 'kling-v3.0-t2v' | 'kling-v3.0-i2v' | 'kling-v3.0-motion-control' | 'kling-v2.6-t2v' | 'kling-v2.6-i2v' | 'kling-v2.6-motion-control' | 'kling-v2.5-turbo-t2v' | 'kling-v2.5-turbo-i2v' | 'wan-v2.6-t2v' | 'wan-v2.6-i2v' | 'wan-v2.6-i2v-flash' | 'wan-v2.6-r2v' | 'wan-v2.6-r2v-flash' | 'wan-v2.5-t2v-preview' | 'seedance-2.0' | 'seedance-2.0-fast' | 'seedance-v1.5-pro' | 'seedance-v1.0-pro' | 'seedance-v1.0-pro-fast' | 'seedance-v1.0-lite-t2v' | 'seedance-v1.0-lite-i2v' | 'luma-ray-2' | 'minimax-video' | 'hunyuan-video' | (string & {});
7
7
  /** Common aspect ratios across providers. */
8
8
  export type AspectRatio = '1:1' | '3:4' | '4:3' | '9:16' | '16:9' | '2:3' | '3:2' | '4:5' | '5:4' | '9:21' | '21:9' | (string & {});
9
9
  /** Image quality presets (varies by provider). */
@@ -114,6 +114,14 @@ export declare function calculateCost(cost: {
114
114
  resolution?: string;
115
115
  hasVideoInput?: boolean;
116
116
  }, count?: number): number | null;
117
+ /**
118
+ * Translate an aspect ratio request into a prompt prefix + concrete `size` for
119
+ * models that have no native aspect ratio parameter.
120
+ */
121
+ export declare function aspectRatioFallback(prompt: string, aspectRatio: `${number}:${number}` | undefined, sizes: readonly string[]): {
122
+ prompt: string;
123
+ size?: `${number}x${number}`;
124
+ };
117
125
  /**
118
126
  * Generate images from a text prompt. Auto-detects which generation strategy
119
127
  * to use (image API, text model, or ChatGPT Responses API) based on model ID.
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/cli/generate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/cli/generate.ts"],"names":[],"mappings":"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"}
@@ -19,7 +19,7 @@ import { createParser } from 'eventsource-parser';
19
19
  import { injectCredentialsToEnv, getChatGptAuth, saveChatGptAuth, } from './credentials.js';
20
20
  import { getModelConfig, createImageModel, createTextModel, createVideoModel, shouldUseResponsesApi, DEFAULT_MODEL, DEFAULT_VIDEO_MODEL, ValidationError, } from './models.js';
21
21
  export { ValidationError };
22
- import { getValidChatGptAuth } from './chatgpt-auth.js';
22
+ import { chatGptReLoginMessage, getValidChatGptAuth, isRevokedChatGptAccessToken, refreshChatGptToken, resolveChatGptResponsesModel, } from './chatgpt-auth.js';
23
23
  export function buildImageProviderOptions(provider, opts) {
24
24
  switch (provider) {
25
25
  case 'google': {
@@ -38,7 +38,8 @@ export function buildImageProviderOptions(provider, opts) {
38
38
  }
39
39
  case 'xai': {
40
40
  const xaiOpts = {
41
- ...(opts.quality ? { quality: opts.quality } : {}),
41
+ // 'auto' is xAI's server default; @ai-sdk/xai does not accept it, so omit the field.
42
+ ...(opts.quality && opts.quality !== 'auto' ? { quality: opts.quality } : {}),
42
43
  ...(opts.resolution ? { resolution: opts.resolution } : {}),
43
44
  ...(opts.outputFormat ? { output_format: opts.outputFormat } : {}),
44
45
  };
@@ -181,21 +182,70 @@ function imageBytesToDataUrl(image) {
181
182
  const mediaType = inferImageMediaType(image);
182
183
  return `data:${mediaType};base64,${Buffer.from(image).toString('base64')}`;
183
184
  }
184
- function chatGptImageSizeFromAspectRatio(aspectRatio) {
185
- switch (aspectRatio) {
186
- case '1:1':
187
- return '1024x1024';
188
- case '3:2':
189
- return '1536x1024';
190
- case '2:3':
191
- return '1024x1536';
192
- default:
193
- return undefined;
194
- }
195
- }
196
185
  function isAspectRatio(input) {
197
186
  return /^\d+:\d+$/.test(input);
198
187
  }
188
+ // ─── aspect ratio fallback for models without native support ─────────────────
189
+ //
190
+ // OpenAI image models (gpt-image-*, dall-e-*) reject `aspectRatio`: the AI SDK
191
+ // pushes an "unsupported: aspectRatio" warning and the request falls back to a
192
+ // square image. They accept a fixed `size` list instead, and they also honour a
193
+ // plain-language ratio statement in the prompt text when picking the framing.
194
+ // So for these models we do both: snap to the closest supported size AND state
195
+ // the ratio in the prompt. The prompt hint matters because the supported sizes
196
+ // are coarse (only 3:2 / 1:1 / 2:3), so a request like 16:9 has no exact match
197
+ // and the composition has to come from the prompt.
198
+ //
199
+ // Models with a non-empty `features.aspectRatios` keep the native flag.
200
+ const OPENAI_SIZES = ['1024x1024', '1536x1024', '1024x1536'];
201
+ function ratioValue(aspectRatio) {
202
+ const [w, h] = aspectRatio.split(':').map(Number);
203
+ if (!w || !h)
204
+ return undefined;
205
+ return w / h;
206
+ }
207
+ function orientationLabel(ratio) {
208
+ if (ratio > 1.02)
209
+ return 'landscape';
210
+ if (ratio < 0.98)
211
+ return 'portrait';
212
+ return 'square';
213
+ }
214
+ /** Pick the supported WIDTHxHEIGHT whose ratio is closest to the requested one. */
215
+ function closestSize(aspectRatio, sizes) {
216
+ const target = ratioValue(aspectRatio);
217
+ if (target === undefined || sizes.length === 0)
218
+ return undefined;
219
+ let best;
220
+ let bestDistance = Infinity;
221
+ for (const size of sizes) {
222
+ const [w, h] = size.split('x').map(Number);
223
+ if (!w || !h)
224
+ continue;
225
+ // Compare in log space so 2x-too-wide and 2x-too-tall are equally far off.
226
+ const distance = Math.abs(Math.log(w / h) - Math.log(target));
227
+ if (distance < bestDistance) {
228
+ bestDistance = distance;
229
+ best = size;
230
+ }
231
+ }
232
+ return best;
233
+ }
234
+ /**
235
+ * Translate an aspect ratio request into a prompt prefix + concrete `size` for
236
+ * models that have no native aspect ratio parameter.
237
+ */
238
+ export function aspectRatioFallback(prompt, aspectRatio, sizes) {
239
+ if (!aspectRatio)
240
+ return { prompt };
241
+ const ratio = ratioValue(aspectRatio);
242
+ if (ratio === undefined)
243
+ return { prompt };
244
+ return {
245
+ prompt: `Image aspect ratio: ${aspectRatio}, ${orientationLabel(ratio)} orientation. ${prompt}`,
246
+ size: closestSize(aspectRatio, sizes),
247
+ };
248
+ }
199
249
  /** Convert any thrown value into an Error instance. */
200
250
  function toError(err) {
201
251
  if (err instanceof Error)
@@ -336,12 +386,19 @@ export async function generateVideoUncached(opts) {
336
386
  }
337
387
  // ─── internal generation paths ───────────────────────────────────────────────
338
388
  async function generateWithImageModel(opts) {
339
- const imagePrompt = opts.inputImages.length > 0
340
- ? { text: opts.prompt, images: opts.inputImages, ...(opts.maskImage ? { mask: opts.maskImage } : {}) }
341
- : opts.prompt;
342
389
  const config = getModelConfig(opts.model);
343
390
  if (config instanceof Error)
344
391
  return config;
392
+ // Models with an empty `aspectRatios` list have no native aspect ratio
393
+ // parameter, so the request is expressed through `size` + the prompt text.
394
+ const features = config.strategy === 'video' ? undefined : config.features;
395
+ const supportsNativeAspectRatio = (features?.aspectRatios?.length ?? 0) > 0;
396
+ const fallback = supportsNativeAspectRatio
397
+ ? { prompt: opts.prompt, size: undefined }
398
+ : aspectRatioFallback(opts.prompt, opts.aspectRatio, features?.sizes ?? []);
399
+ const imagePrompt = opts.inputImages.length > 0
400
+ ? { text: fallback.prompt, images: opts.inputImages, ...(opts.maskImage ? { mask: opts.maskImage } : {}) }
401
+ : fallback.prompt;
345
402
  const imageModel = await createImageModel(opts.model);
346
403
  if (imageModel instanceof Error)
347
404
  return imageModel;
@@ -359,7 +416,8 @@ async function generateWithImageModel(opts) {
359
416
  model: imageModel,
360
417
  prompt: imagePrompt,
361
418
  n: opts.count,
362
- ...(opts.aspectRatio ? { aspectRatio: opts.aspectRatio } : {}),
419
+ ...(opts.aspectRatio && supportsNativeAspectRatio ? { aspectRatio: opts.aspectRatio } : {}),
420
+ ...(fallback.size ? { size: fallback.size } : {}),
363
421
  ...(opts.seed !== undefined ? { seed: opts.seed } : {}),
364
422
  providerOptions,
365
423
  });
@@ -453,10 +511,7 @@ async function generateWithResponsesApi(opts) {
453
511
  if (opts.maskImage) {
454
512
  return new ValidationError('ChatGPT image generation does not support mask yet.');
455
513
  }
456
- const size = opts.aspectRatio ? chatGptImageSizeFromAspectRatio(opts.aspectRatio) : undefined;
457
- if (opts.aspectRatio && !size) {
458
- return new ValidationError('ChatGPT image generation only supports aspect ratios 1:1, 3:2, or 2:3.');
459
- }
514
+ const { prompt, size } = aspectRatioFallback(opts.prompt, opts.aspectRatio, OPENAI_SIZES);
460
515
  const storedAuth = getChatGptAuth();
461
516
  if (!storedAuth?.accountId) {
462
517
  return new ValidationError('Missing ChatGPT account metadata. Please run `egaki login --provider chatgpt` again.');
@@ -465,49 +520,62 @@ async function generateWithResponsesApi(opts) {
465
520
  if (auth instanceof Error)
466
521
  return auth;
467
522
  const content = [
468
- { type: 'input_text', text: opts.prompt },
523
+ { type: 'input_text', text: prompt },
469
524
  ...opts.inputImages.map((image) => ({
470
525
  type: 'input_image',
471
526
  image_url: imageBytesToDataUrl(image),
472
527
  })),
473
528
  ];
529
+ const responsesModel = await resolveChatGptResponsesModel({ auth });
530
+ if (responsesModel instanceof Error)
531
+ return responsesModel;
532
+ const requestBodyFor = (model) => JSON.stringify({
533
+ model,
534
+ instructions: 'You are Codex.',
535
+ input: [
536
+ {
537
+ type: 'message',
538
+ role: 'user',
539
+ content,
540
+ },
541
+ ],
542
+ tools: [
543
+ {
544
+ type: 'image_generation',
545
+ model: opts.model,
546
+ quality: 'auto',
547
+ output_format: 'png',
548
+ output_compression: 100,
549
+ moderation: 'auto',
550
+ size: size ?? 'auto',
551
+ },
552
+ ],
553
+ tool_choice: 'auto',
554
+ parallel_tool_calls: true,
555
+ stream: true,
556
+ store: false,
557
+ include: [],
558
+ });
559
+ const post = ({ access, accountId, body, }) => {
560
+ const headers = new Headers({
561
+ Authorization: `Bearer ${access}`,
562
+ 'Content-Type': 'application/json',
563
+ });
564
+ if (accountId)
565
+ headers.set('ChatGPT-Account-ID', accountId);
566
+ return fetch('https://chatgpt.com/backend-api/codex/responses', {
567
+ method: 'POST',
568
+ headers,
569
+ body,
570
+ });
571
+ };
572
+ let activeModel = responsesModel;
474
573
  let response;
475
574
  try {
476
- response = await fetch('https://chatgpt.com/backend-api/codex/responses', {
477
- method: 'POST',
478
- headers: {
479
- Authorization: `Bearer ${auth.access}`,
480
- ...(auth.accountId && { 'ChatGPT-Account-ID': auth.accountId }),
481
- 'Content-Type': 'application/json',
482
- },
483
- body: JSON.stringify({
484
- model: 'gpt-5.4',
485
- instructions: 'You are Codex.',
486
- input: [
487
- {
488
- type: 'message',
489
- role: 'user',
490
- content,
491
- },
492
- ],
493
- tools: [
494
- {
495
- type: 'image_generation',
496
- model: opts.model,
497
- size: 'auto',
498
- quality: 'auto',
499
- output_format: 'png',
500
- output_compression: 100,
501
- moderation: 'auto',
502
- ...(size ? { size } : {}),
503
- },
504
- ],
505
- tool_choice: 'auto',
506
- parallel_tool_calls: true,
507
- stream: true,
508
- store: false,
509
- include: [],
510
- }),
575
+ response = await post({
576
+ access: auth.access,
577
+ accountId: auth.accountId,
578
+ body: requestBodyFor(activeModel),
511
579
  });
512
580
  }
513
581
  catch (err) {
@@ -515,7 +583,52 @@ async function generateWithResponsesApi(opts) {
515
583
  }
516
584
  if (!response.ok || !response.body) {
517
585
  const body = await response.text().catch(() => '');
518
- return new Error(`ChatGPT image generation failed: ${response.status}${body ? ` ${body}` : ''}`);
586
+ if (!(response.status === 400 && body.includes('not supported'))) {
587
+ if (!isRevokedChatGptAccessToken(response.status, body)) {
588
+ return new Error(`ChatGPT image generation failed: ${response.status}${body ? ` ${body}` : ''}`);
589
+ }
590
+ const refreshed = await refreshChatGptToken(auth);
591
+ if (refreshed instanceof Error)
592
+ return refreshed;
593
+ saveChatGptAuth(refreshed);
594
+ try {
595
+ response = await post({
596
+ access: refreshed.access,
597
+ accountId: refreshed.accountId,
598
+ body: requestBodyFor(activeModel),
599
+ });
600
+ }
601
+ catch (err) {
602
+ return toError(err);
603
+ }
604
+ }
605
+ else {
606
+ const nextModel = await resolveChatGptResponsesModel({
607
+ auth,
608
+ exclude: [activeModel],
609
+ });
610
+ if (nextModel instanceof Error) {
611
+ return new Error(`ChatGPT image generation failed: ${response.status} ${body}`);
612
+ }
613
+ activeModel = nextModel;
614
+ try {
615
+ response = await post({
616
+ access: auth.access,
617
+ accountId: auth.accountId,
618
+ body: requestBodyFor(activeModel),
619
+ });
620
+ }
621
+ catch (err) {
622
+ return toError(err);
623
+ }
624
+ }
625
+ if (!response.ok || !response.body) {
626
+ const retryBody = await response.text().catch(() => '');
627
+ if (isRevokedChatGptAccessToken(response.status, retryBody)) {
628
+ return new Error(chatGptReLoginMessage({ status: response.status, body: retryBody }));
629
+ }
630
+ return new Error(`ChatGPT image generation failed: ${response.status}${retryBody ? ` ${retryBody}` : ''}`);
631
+ }
519
632
  }
520
633
  let imageBase64;
521
634
  let revisedPrompt = null;
@@ -2,7 +2,7 @@
2
2
  // Tests the error-as-value pattern and validation logic without
3
3
  // making actual API calls (no credentials needed).
4
4
  import { describe, it, expect } from 'vitest';
5
- import { generateImage, generateVideo, calculateCost, buildImageProviderOptions, buildVideoProviderOptions, ValidationError, } from './generate.js';
5
+ import { generateImage, generateVideo, calculateCost, buildImageProviderOptions, buildVideoProviderOptions, aspectRatioFallback, ValidationError, } from './generate.js';
6
6
  describe('generateImage', () => {
7
7
  it('returns ValidationError for unknown model', async () => {
8
8
  const result = await generateImage({
@@ -110,6 +110,55 @@ describe('buildImageProviderOptions', () => {
110
110
  expect(opts).toEqual({});
111
111
  });
112
112
  });
113
+ describe('aspectRatioFallback', () => {
114
+ const openaiSizes = ['1024x1024', '1536x1024', '1024x1536'];
115
+ it('leaves the prompt untouched when no aspect ratio is requested', () => {
116
+ expect(aspectRatioFallback('a sunset', undefined, openaiSizes)).toMatchInlineSnapshot(`
117
+ {
118
+ "prompt": "a sunset",
119
+ }
120
+ `);
121
+ });
122
+ it('prepends the ratio and picks the closest landscape size', () => {
123
+ expect(aspectRatioFallback('a sunset', '16:9', openaiSizes)).toMatchInlineSnapshot(`
124
+ {
125
+ "prompt": "Image aspect ratio: 16:9, landscape orientation. a sunset",
126
+ "size": "1536x1024",
127
+ }
128
+ `);
129
+ });
130
+ it('picks the closest portrait size', () => {
131
+ expect(aspectRatioFallback('a sunset', '9:16', openaiSizes)).toMatchInlineSnapshot(`
132
+ {
133
+ "prompt": "Image aspect ratio: 9:16, portrait orientation. a sunset",
134
+ "size": "1024x1536",
135
+ }
136
+ `);
137
+ });
138
+ it('picks the square size for 1:1', () => {
139
+ expect(aspectRatioFallback('a sunset', '1:1', openaiSizes)).toMatchInlineSnapshot(`
140
+ {
141
+ "prompt": "Image aspect ratio: 1:1, square orientation. a sunset",
142
+ "size": "1024x1024",
143
+ }
144
+ `);
145
+ });
146
+ it('still prepends the ratio when the model has no size list', () => {
147
+ expect(aspectRatioFallback('a sunset', '21:9', [])).toMatchInlineSnapshot(`
148
+ {
149
+ "prompt": "Image aspect ratio: 21:9, landscape orientation. a sunset",
150
+ "size": undefined,
151
+ }
152
+ `);
153
+ });
154
+ it('ignores malformed ratios', () => {
155
+ expect(aspectRatioFallback('a sunset', '0:0', openaiSizes)).toMatchInlineSnapshot(`
156
+ {
157
+ "prompt": "a sunset",
158
+ }
159
+ `);
160
+ });
161
+ });
113
162
  describe('buildVideoProviderOptions', () => {
114
163
  it('builds xai edit-video options', () => {
115
164
  const opts = buildVideoProviderOptions('xai', {