egaki 0.2.0 → 0.4.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/dist/login.js CHANGED
@@ -1,9 +1,11 @@
1
1
  // Login command — interactive and non-interactive API key management.
2
2
  // Interactive mode uses clack prompts for provider selection and key input.
3
3
  // Non-interactive mode supports --provider + --key flags and stdin piping.
4
+ // ChatGPT OAuth uses a browser flow instead of pasting a key.
4
5
  import { intro, outro, select, password, isCancel, cancel, log, note, } from '@clack/prompts';
5
6
  import pc from 'picocolors';
6
- import { PROVIDERS, saveProviderKey, removeProviderKey, getKeyStatus, readCredentials, } from './credentials.js';
7
+ import { PROVIDERS, saveProviderKey, removeProviderKey, getKeyStatus, getChatGptAuth, saveChatGptAuth, } from './credentials.js';
8
+ import { chatGptOAuthLogin, extractPlanType } from './chatgpt-auth.js';
7
9
  export async function loginInteractive() {
8
10
  intro(pc.bold('egaki login'));
9
11
  const providerOptions = Object.entries(PROVIDERS).map(([key, info]) => {
@@ -15,6 +17,11 @@ export async function loginInteractive() {
15
17
  if (status.source === 'stored') {
16
18
  return pc.green('(saved)');
17
19
  }
20
+ if (status.source === 'oauth') {
21
+ const auth = getChatGptAuth();
22
+ const email = auth?.email;
23
+ return pc.green(`(signed in${email ? ` as ${email}` : ''})`);
24
+ }
18
25
  return pc.dim('(not configured)');
19
26
  })();
20
27
  // Highlight the egaki option as recommended
@@ -40,6 +47,13 @@ export async function loginInteractive() {
40
47
  log.error(`Unknown provider: ${provider}`);
41
48
  process.exit(1);
42
49
  }
50
+ // ChatGPT uses browser OAuth instead of key paste
51
+ if (provider === 'chatgpt') {
52
+ const auth = await chatGptOAuthLogin();
53
+ saveChatGptAuth(auth);
54
+ outro('Done — ChatGPT OAuth saved');
55
+ return;
56
+ }
43
57
  log.info(`${pc.dim('Env var:')} ${info.envVar} ${pc.dim('(also used by the CLI when set)')}`);
44
58
  log.info(`${pc.dim('Get key:')} ${info.hint}`);
45
59
  const key = await password({
@@ -59,13 +73,20 @@ export async function loginInteractive() {
59
73
  note(`You can also set ${pc.bold(info.envVar)} as an env var.\nThe CLI reads it automatically, so you can pass it inline too:\n ${pc.dim(`${info.envVar}=... egaki generate "prompt"`)}`, 'Tip');
60
74
  outro('Done');
61
75
  }
62
- export function loginNonInteractive({ provider, key, }) {
76
+ export async function loginNonInteractive({ provider, key, }) {
63
77
  const info = PROVIDERS[provider];
64
78
  if (!info) {
65
79
  const available = Object.keys(PROVIDERS).join(', ');
66
80
  console.error(pc.red(`Unknown provider: ${provider}. Available: ${available}`));
67
81
  process.exit(1);
68
82
  }
83
+ // ChatGPT uses browser OAuth — key flag is not needed
84
+ if (provider === 'chatgpt') {
85
+ const auth = await chatGptOAuthLogin();
86
+ saveChatGptAuth(auth);
87
+ console.log(pc.green('ChatGPT OAuth saved'));
88
+ return;
89
+ }
69
90
  if (!key || key.trim().length === 0) {
70
91
  console.error(pc.red('API key cannot be empty'));
71
92
  process.exit(1);
@@ -77,6 +98,25 @@ export function showLoginStatus() {
77
98
  console.log(pc.bold('Configured providers:\n'));
78
99
  for (const [key, info] of Object.entries(PROVIDERS)) {
79
100
  const status = getKeyStatus(key);
101
+ if (key === 'chatgpt') {
102
+ const auth = getChatGptAuth();
103
+ if (auth) {
104
+ const plan = extractPlanType(auth);
105
+ const email = auth.email ?? auth.accountId ?? 'unknown';
106
+ const expired = auth.expires < Date.now();
107
+ const expiryLabel = expired
108
+ ? pc.yellow('(token expired, will auto-refresh)')
109
+ : pc.dim(`(token valid)`);
110
+ console.log(`${pc.green('*')} ${info.label} ${pc.green('(signed in)')}`);
111
+ console.log(pc.dim(` account: ${email}${plan ? `, plan: ${plan}` : ''}`));
112
+ console.log(` ${expiryLabel}`);
113
+ }
114
+ else {
115
+ console.log(`${pc.dim('-')} ${info.label} ${pc.dim('(not signed in)')}`);
116
+ console.log(pc.dim(` run: egaki login → select ChatGPT`));
117
+ }
118
+ continue;
119
+ }
80
120
  const icon = status.available ? pc.green('*') : pc.dim('-');
81
121
  const source = (() => {
82
122
  if (status.source === 'env') {
@@ -100,7 +140,8 @@ export function removeLogin(provider) {
100
140
  process.exit(1);
101
141
  }
102
142
  removeProviderKey(provider);
103
- console.log(pc.green(`${info.label} key removed`));
143
+ const label = provider === 'chatgpt' ? 'ChatGPT OAuth' : info.label;
144
+ console.log(pc.green(`${label} key removed`));
104
145
  }
105
146
  // Read API key from stdin (for piping: echo "sk-xxx" | egaki login --provider google)
106
147
  export async function readKeyFromStdin() {
@@ -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+DD,eAAO,MAAM,OAAO,EAAE,UAAU,EA40B/B,CAAA;AAMD,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAE5D"}
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,EAq5B/B,CAAA;AAMD,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAE5D"}
@@ -53,6 +53,20 @@ const xaiImage = {
53
53
  provider: 'xai',
54
54
  strategy: 'image',
55
55
  };
56
+ const vertexImagen = {
57
+ provider: 'vertex',
58
+ strategy: 'image',
59
+ features: {
60
+ ...googleImagen.features,
61
+ },
62
+ };
63
+ const vertexText = {
64
+ provider: 'vertex',
65
+ strategy: 'text',
66
+ features: {
67
+ ...googleText.features,
68
+ },
69
+ };
56
70
  const fluxAspectRatios = ['1:1', '3:4', '4:3', '9:16', '16:9', '9:21', '21:9'];
57
71
  // ─── catalog ─────────────────────────────────────────────────────────────────
58
72
  export const CATALOG = [
@@ -134,6 +148,62 @@ export const CATALOG = [
134
148
  },
135
149
  cost: { type: 'per-token', inputPerM: 0.5, outputPerM: 3.0 },
136
150
  },
151
+ // ── Vertex: Imagen ──────────────────────────────────────────────────────
152
+ // Same models as Google AI Studio but routed through Vertex AI / Google Cloud billing.
153
+ // Use `egaki login --provider vertex --key <key>` to configure.
154
+ {
155
+ id: 'vertex/imagen-4.0-generate-001',
156
+ name: 'Imagen 4 (Vertex)',
157
+ released: '2025-08-15',
158
+ ...vertexImagen,
159
+ cost: { type: 'per-image', perImage: 0.04 },
160
+ },
161
+ {
162
+ id: 'vertex/imagen-4.0-ultra-generate-001',
163
+ name: 'Imagen 4 Ultra (Vertex)',
164
+ released: '2025-08-15',
165
+ ...vertexImagen,
166
+ cost: { type: 'per-image', perImage: 0.06 },
167
+ },
168
+ {
169
+ id: 'vertex/imagen-4.0-fast-generate-001',
170
+ name: 'Imagen 4 Fast (Vertex)',
171
+ released: '2025-08-15',
172
+ ...vertexImagen,
173
+ cost: { type: 'per-image', perImage: 0.02 },
174
+ },
175
+ // ── Vertex: Gemini text+image ─────────────────────────────────────────
176
+ {
177
+ id: 'vertex/gemini-2.5-flash-image',
178
+ name: 'Gemini 2.5 Flash Image (Vertex)',
179
+ released: '2025-08-26',
180
+ ...vertexText,
181
+ cost: { type: 'per-token', inputPerM: 0.3, outputPerM: 30 },
182
+ },
183
+ {
184
+ id: 'vertex/gemini-3-pro-image-preview',
185
+ name: 'Gemini 3 Pro Image (Vertex)',
186
+ released: '2025-11-20',
187
+ ...vertexText,
188
+ cost: { type: 'per-token', inputPerM: 1.25, outputPerM: 10 },
189
+ },
190
+ {
191
+ id: 'vertex/gemini-3.1-flash-image-preview',
192
+ name: 'Gemini 3.1 Flash Image (Vertex)',
193
+ released: '2026-02-26',
194
+ ...vertexText,
195
+ features: {
196
+ ...vertexText.features,
197
+ aspectRatios: [
198
+ ...vertexText.features.aspectRatios,
199
+ '4:1',
200
+ '1:4',
201
+ '8:1',
202
+ '1:8',
203
+ ],
204
+ },
205
+ cost: { type: 'per-token', inputPerM: 0.5, outputPerM: 3.0 },
206
+ },
137
207
  // ── BFL (AI Gateway) ───────────────────────────────────────────────────
138
208
  {
139
209
  id: 'flux-kontext-max',
@@ -367,9 +437,23 @@ export const CATALOG = [
367
437
  multipleImages: true,
368
438
  },
369
439
  },
370
- // NOTE: chatgpt-image-latest exists in OpenAI's .d.ts types but it's a moving
371
- // alias used by ChatGPT/Responses API, not a stable image endpoint model.
372
- // Omitted until OpenAI stabilizes it as a first-class image generation target.
440
+ {
441
+ id: 'chatgpt-image-latest',
442
+ name: 'ChatGPT Image',
443
+ description: 'Rolling-latest alias that tracks the newest ChatGPT image model. Currently points ' +
444
+ 'to gpt-image-1.5. Pricing may shift when the alias retargets to a newer model.',
445
+ released: '2026-01',
446
+ ...openaiImage,
447
+ cost: { type: 'per-image', perImage: 0.034 },
448
+ features: {
449
+ editing: true,
450
+ inpainting: true,
451
+ aspectRatios: [],
452
+ sizes: ['1024x1024', '1536x1024', '1024x1536'],
453
+ seed: false,
454
+ multipleImages: true,
455
+ },
456
+ },
373
457
  // ── Replicate ──────────────────────────────────────────────────────────
374
458
  {
375
459
  id: 'black-forest-labs/flux-1.1-pro',
package/dist/models.d.ts CHANGED
@@ -9,6 +9,11 @@ export declare const DEFAULT_MODEL = "nano-banana-pro-preview";
9
9
  export declare const DEFAULT_VIDEO_MODEL = "veo-3.1-fast-generate-001";
10
10
  export declare function getModelConfig(modelId: string): AnyModelEntry;
11
11
  export declare function ensureProviderKey(providerName: string): void;
12
+ /**
13
+ * ChatGPT OAuth image generation goes through the Codex backend instead of the
14
+ * OpenAI Image API, so OpenAI image models need the custom responses path.
15
+ */
16
+ export declare function shouldUseResponsesApi(modelId: string): boolean;
12
17
  export declare function createImageModel(modelId: string): Promise<ImageModel>;
13
18
  export declare function createTextModel(modelId: string): Promise<LanguageModel>;
14
19
  export declare function createVideoModel(modelId: string): Promise<any>;
@@ -1 +1 @@
1
- {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,IAAI,CAAA;AAInD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAEpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAE/D,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,eAAe,CAAA;AACxD,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,CAAA;AAE3C,eAAO,MAAM,YAAY,EAA+B,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAA;AAC7E,eAAO,MAAM,YAAY,EAAqC,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAA;AAEnF,eAAO,MAAM,aAAa,4BAA4B,CAAA;AACtD,eAAO,MAAM,mBAAmB,8BAA8B,CAAA;AAE9D,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAO7D;AAuBD,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAmC5D;AAgDD,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAkC3E;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,aAAa,CAAC,CAiCxB;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAqCpE"}
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,IAAI,CAAA;AAInD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAEpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAE/D,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,eAAe,CAAA;AACxD,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,CAAA;AAE3C,eAAO,MAAM,YAAY,EAA+B,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAA;AAC7E,eAAO,MAAM,YAAY,EAAqC,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAA;AAYnF,eAAO,MAAM,aAAa,4BAA4B,CAAA;AACtD,eAAO,MAAM,mBAAmB,8BAA8B,CAAA;AAE9D,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAO7D;AAuBD,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAiD5D;AAgDD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAI9D;AAOD,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAsC3E;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,aAAa,CAAC,CAqCxB;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAyCpE"}
package/dist/models.js CHANGED
@@ -1,9 +1,19 @@
1
1
  import pc from 'picocolors';
2
- import { PROVIDERS, EGAKI_GATEWAY_URL } from './credentials.js';
2
+ import { PROVIDERS, EGAKI_GATEWAY_URL, shouldUseChatGptBackend } from './credentials.js';
3
3
  import { CATALOG, findModel } from './model-catalog.js';
4
4
  import { VIDEO_CATALOG, findVideoModel } from './video-model-catalog.js';
5
5
  export const IMAGE_MODELS = CATALOG.map((m) => m.id);
6
6
  export const VIDEO_MODELS = VIDEO_CATALOG.map((m) => m.id);
7
+ /**
8
+ * Strip provider prefix from a model ID (e.g. "vertex/imagen-4.0-generate-001" → "imagen-4.0-generate-001").
9
+ * Provider SDKs expect bare model IDs, but the catalog uses prefixed IDs for routing.
10
+ */
11
+ function stripProviderPrefix(modelId) {
12
+ const slash = modelId.indexOf('/');
13
+ if (slash === -1)
14
+ return modelId;
15
+ return modelId.slice(slash + 1);
16
+ }
7
17
  export const DEFAULT_MODEL = 'nano-banana-pro-preview';
8
18
  export const DEFAULT_VIDEO_MODEL = 'veo-3.1-fast-generate-001';
9
19
  export function getModelConfig(modelId) {
@@ -39,6 +49,19 @@ export function ensureProviderKey(providerName) {
39
49
  // If direct provider key exists, we're good
40
50
  if (hasDirectProviderKey(providerName))
41
51
  return;
52
+ // Vertex models require a direct key — the upstream Vercel AI Gateway
53
+ // does not support vertex/ routing, so egaki gateway can't proxy them.
54
+ if (providerName === 'vertex') {
55
+ console.error('');
56
+ console.error(pc.red(pc.bold('Vertex models require a direct Google Cloud API key')));
57
+ console.error('');
58
+ console.error(` ${pc.cyan('egaki login --provider vertex --key <key>')}`);
59
+ console.error('');
60
+ console.error(pc.dim('Get a key at https://console.cloud.google.com/apis/credentials'));
61
+ console.error(pc.dim('Egaki subscription does not cover Vertex routing yet.'));
62
+ console.error('');
63
+ process.exit(1);
64
+ }
42
65
  // If egaki key exists, we'll route through the gateway
43
66
  if (hasEgakiKey())
44
67
  return;
@@ -72,8 +95,11 @@ async function createGatewayImageModel(modelId, provider) {
72
95
  apiKey: process.env['EGAKI_API_KEY'],
73
96
  baseURL: EGAKI_GATEWAY_URL,
74
97
  });
75
- // Gateway expects provider/model format for routing
76
- const gatewayModelId = `${provider}/${modelId}`;
98
+ // Gateway expects provider/model format for routing.
99
+ // For models that already have a prefix (e.g. vertex/imagen-4.0), strip it
100
+ // and use the catalog provider. For bare IDs, prepend the provider.
101
+ const bareId = stripProviderPrefix(modelId);
102
+ const gatewayModelId = `${provider}/${bareId}`;
77
103
  return gateway.image(gatewayModelId);
78
104
  }
79
105
  /**
@@ -85,7 +111,8 @@ async function createGatewayTextModel(modelId, provider) {
85
111
  apiKey: process.env['EGAKI_API_KEY'],
86
112
  baseURL: EGAKI_GATEWAY_URL,
87
113
  });
88
- const gatewayModelId = `${provider}/${modelId}`;
114
+ const bareId = stripProviderPrefix(modelId);
115
+ const gatewayModelId = `${provider}/${bareId}`;
89
116
  return gateway(gatewayModelId);
90
117
  }
91
118
  /**
@@ -97,9 +124,20 @@ async function createGatewayVideoModel(modelId, provider) {
97
124
  apiKey: process.env['EGAKI_API_KEY'],
98
125
  baseURL: EGAKI_GATEWAY_URL,
99
126
  });
100
- const gatewayModelId = `${provider}/${modelId}`;
127
+ const bareId = stripProviderPrefix(modelId);
128
+ const gatewayModelId = `${provider}/${bareId}`;
101
129
  return gateway.video(gatewayModelId);
102
130
  }
131
+ /**
132
+ * ChatGPT OAuth image generation goes through the Codex backend instead of the
133
+ * OpenAI Image API, so OpenAI image models need the custom responses path.
134
+ */
135
+ export function shouldUseResponsesApi(modelId) {
136
+ const config = findModel(modelId);
137
+ if (!config)
138
+ return false;
139
+ return config.provider === 'openai' && config.strategy === 'image' && shouldUseChatGptBackend();
140
+ }
103
141
  // Lazily import the provider and create the right model instance.
104
142
  // This avoids loading all provider SDKs upfront — only the one needed
105
143
  // for the selected model gets imported.
@@ -115,6 +153,10 @@ export async function createImageModel(modelId) {
115
153
  const { google } = await import('@ai-sdk/google');
116
154
  return google.image(modelId);
117
155
  }
156
+ case 'vertex': {
157
+ const { vertex } = await import('@ai-sdk/google-vertex');
158
+ return vertex.image(stripProviderPrefix(modelId));
159
+ }
118
160
  case 'openai': {
119
161
  const { openai } = await import('@ai-sdk/openai');
120
162
  return openai.image(modelId);
@@ -151,9 +193,13 @@ export async function createTextModel(modelId) {
151
193
  const { google } = await import('@ai-sdk/google');
152
194
  return google(modelId);
153
195
  }
196
+ case 'vertex': {
197
+ const { vertex } = await import('@ai-sdk/google-vertex');
198
+ return vertex(stripProviderPrefix(modelId));
199
+ }
154
200
  default:
155
- // Only Google supports generateText with responseModalities for images
156
- console.error(pc.red(`Text+image generation is only supported for Google models, got provider: ${config.provider}`));
201
+ // Only Google/Vertex support generateText with responseModalities for images
202
+ console.error(pc.red(`Text+image generation is only supported for Google/Vertex models, got provider: ${config.provider}`));
157
203
  process.exit(1);
158
204
  }
159
205
  }
@@ -178,12 +224,16 @@ export async function createVideoModel(modelId) {
178
224
  const { google } = await import('@ai-sdk/google');
179
225
  return google.video(modelId);
180
226
  }
227
+ case 'vertex': {
228
+ const { vertex } = await import('@ai-sdk/google-vertex');
229
+ return vertex.video(stripProviderPrefix(modelId));
230
+ }
181
231
  case 'fal': {
182
232
  const { fal } = await import('@ai-sdk/fal');
183
233
  return fal.video(modelId);
184
234
  }
185
235
  default:
186
- console.error(pc.red(`Direct video generation is only supported for Google and Fal keys, got provider: ${config.provider}`));
236
+ console.error(pc.red(`Direct video generation is only supported for Google, Vertex, and Fal keys, got provider: ${config.provider}`));
187
237
  process.exit(1);
188
238
  }
189
239
  }
@@ -1 +1 @@
1
- {"version":3,"file":"video-model-catalog.d.ts","sourceRoot":"","sources":["../src/video-model-catalog.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,wBAAwB,GAAG;IACrC,sCAAsC;IACtC,aAAa,EAAE,MAAM,CAAA;IACrB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,mCAAmC;IACnC,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,kBAAkB,CAAA;IACxB,oEAAoE;IACpE,kBAAkB,EAAE,MAAM,CAAA;IAC1B,KAAK,EAAE,wBAAwB,EAAE,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,SAAS,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,cAAc,GAAG,kBAAkB,GAAG,gBAAgB,CAAA;AAElE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,+CAA+C;IAC/C,WAAW,EAAE,OAAO,CAAA;IACpB,8DAA8D;IAC9D,YAAY,EAAE,OAAO,CAAA;IACrB,gDAAgD;IAChD,YAAY,EAAE,KAAK,CAAC,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,gBAAgB,GAAG,SAAS,CAAC,CAAA;IACzE,uCAAuC;IACvC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,yCAAyC;IACzC,gBAAgB,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IAC/C,IAAI,EAAE,OAAO,CAAA;IACb,cAAc,EAAE,OAAO,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,cAAc,CAAA;IACpB,QAAQ,EAAE,kBAAkB,CAAA;CAC7B,CAAA;AAID,eAAO,MAAM,aAAa,EAAE,eAAe,EA6O1C,CAAA;AAID,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAEtE"}
1
+ {"version":3,"file":"video-model-catalog.d.ts","sourceRoot":"","sources":["../src/video-model-catalog.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,wBAAwB,GAAG;IACrC,sCAAsC;IACtC,aAAa,EAAE,MAAM,CAAA;IACrB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,mCAAmC;IACnC,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,kBAAkB,CAAA;IACxB,oEAAoE;IACpE,kBAAkB,EAAE,MAAM,CAAA;IAC1B,KAAK,EAAE,wBAAwB,EAAE,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,SAAS,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,cAAc,GAAG,kBAAkB,GAAG,gBAAgB,CAAA;AAElE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,+CAA+C;IAC/C,WAAW,EAAE,OAAO,CAAA;IACpB,8DAA8D;IAC9D,YAAY,EAAE,OAAO,CAAA;IACrB,gDAAgD;IAChD,YAAY,EAAE,KAAK,CAAC,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,gBAAgB,GAAG,SAAS,CAAC,CAAA;IACzE,uCAAuC;IACvC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,yCAAyC;IACzC,gBAAgB,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IAC/C,IAAI,EAAE,OAAO,CAAA;IACb,cAAc,EAAE,OAAO,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,cAAc,CAAA;IACpB,QAAQ,EAAE,kBAAkB,CAAA;CAC7B,CAAA;AAID,eAAO,MAAM,aAAa,EAAE,eAAe,EA0S1C,CAAA;AAID,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAEtE"}
@@ -115,6 +115,67 @@ export const VIDEO_CATALOG = [
115
115
  multipleVideos: true,
116
116
  },
117
117
  },
118
+ // ── Vertex: Veo ──────────────────────────────────────────────────────
119
+ // Same Veo models routed through Vertex AI / Google Cloud billing.
120
+ // Use `egaki login --provider vertex --key <key>` to configure.
121
+ {
122
+ id: 'vertex/veo-3.1-generate-001',
123
+ name: 'Veo 3.1 (Vertex)',
124
+ provider: 'vertex',
125
+ strategy: 'video',
126
+ released: '2026-01',
127
+ cost: {
128
+ type: 'per-video-second',
129
+ defaultDurationSec: 8,
130
+ tiers: [
131
+ { resolution: '720p', audio: false, costPerSecond: 0.2 },
132
+ { resolution: '720p', audio: true, costPerSecond: 0.4 },
133
+ { resolution: '1080p', audio: false, costPerSecond: 0.2 },
134
+ { resolution: '1080p', audio: true, costPerSecond: 0.4 },
135
+ { resolution: '4k', audio: false, costPerSecond: 0.4 },
136
+ { resolution: '4k', audio: true, costPerSecond: 0.6 },
137
+ ],
138
+ },
139
+ features: {
140
+ textToVideo: true,
141
+ imageToVideo: false,
142
+ capabilities: ['t2v'],
143
+ aspectRatios: ['16:9', '9:16'],
144
+ resolutions: ['720p', '1080p', '4k'],
145
+ durationRangeSec: { min: 4, max: 8 },
146
+ seed: true,
147
+ multipleVideos: true,
148
+ },
149
+ },
150
+ {
151
+ id: 'vertex/veo-3.1-fast-generate-001',
152
+ name: 'Veo 3.1 Fast (Vertex)',
153
+ provider: 'vertex',
154
+ strategy: 'video',
155
+ released: '2026-01',
156
+ cost: {
157
+ type: 'per-video-second',
158
+ defaultDurationSec: 8,
159
+ tiers: [
160
+ { resolution: '720p', audio: false, costPerSecond: 0.1 },
161
+ { resolution: '720p', audio: true, costPerSecond: 0.15 },
162
+ { resolution: '1080p', audio: false, costPerSecond: 0.1 },
163
+ { resolution: '1080p', audio: true, costPerSecond: 0.15 },
164
+ { resolution: '4k', audio: false, costPerSecond: 0.3 },
165
+ { resolution: '4k', audio: true, costPerSecond: 0.35 },
166
+ ],
167
+ },
168
+ features: {
169
+ textToVideo: true,
170
+ imageToVideo: false,
171
+ capabilities: ['t2v'],
172
+ aspectRatios: ['16:9', '9:16'],
173
+ resolutions: ['720p', '1080p', '4k'],
174
+ durationRangeSec: { min: 4, max: 8 },
175
+ seed: true,
176
+ multipleVideos: true,
177
+ },
178
+ },
118
179
  {
119
180
  id: 'grok-imagine-video',
120
181
  name: 'Grok Imagine Video',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "egaki",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "main": "./dist/cli.js",
6
6
  "types": "./dist/cli.d.ts",
@@ -33,6 +33,7 @@
33
33
  "files": [
34
34
  "src",
35
35
  "dist",
36
+ "skills",
36
37
  "README.md",
37
38
  "CHANGELOG.md"
38
39
  ],
@@ -48,11 +49,13 @@
48
49
  "dependencies": {
49
50
  "@ai-sdk/fal": "2.0.21",
50
51
  "@ai-sdk/google": "3.0.33",
52
+ "@ai-sdk/google-vertex": "^4.0.95",
51
53
  "@ai-sdk/openai": "3.0.36",
52
54
  "@ai-sdk/replicate": "2.0.20",
53
55
  "@clack/prompts": "^1.0.1",
54
56
  "ai": "6.0.103",
55
- "goke": "^6.1.3",
57
+ "eventsource-parser": "^3.0.6",
58
+ "goke": "^6.6.1",
56
59
  "js-yaml": "^4.1.1",
57
60
  "picocolors": "^1.1.1",
58
61
  "string-dedent": "^3.0.2",
@@ -0,0 +1,100 @@
1
+ ---
2
+ name: egaki
3
+ description: >
4
+ AI image and video generation CLI. Use this skill to install egaki, configure
5
+ auth, run help commands, and generate images or videos with provider keys or
6
+ an Egaki subscription.
7
+ ---
8
+
9
+ # egaki
10
+
11
+ Generate AI images and videos from the terminal.
12
+ Use this for text-to-image, image editing, mask-based edits, text-to-video,
13
+ image-to-video, and model discovery.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ pnpm add -g egaki
19
+ ```
20
+
21
+ ## Always check help first
22
+
23
+ Run the full help output before using commands:
24
+
25
+ ```bash
26
+ egaki --help
27
+ ```
28
+
29
+ Do not truncate help output with `head`.
30
+
31
+ For subcommand details: `egaki <command> --help` (e.g. `egaki image --help`, `egaki video --help`, `egaki login --help`)
32
+
33
+ ## Auth options
34
+
35
+ You can authenticate in two ways:
36
+
37
+ 1. Egaki subscription key (recommended — all models, one key)
38
+ 2. Provider API keys (Google, OpenAI, Fal, Replicate) via `egaki login`
39
+
40
+ If using Egaki subscription, set it up first with `egaki subscribe`, then store
41
+ the key with `egaki login --provider egaki --key egaki_...`.
42
+
43
+ ## Login behavior for remote agents
44
+
45
+ When login requires a URL flow, run login in the background and send the login URL
46
+ to the user so they can complete auth interactively.
47
+
48
+ ## Example commands
49
+
50
+ ```bash
51
+ # configure key interactively
52
+ egaki login
53
+
54
+ # show login status
55
+ egaki login --show
56
+
57
+ # subscribe to Egaki for all supported models
58
+ egaki subscribe
59
+
60
+ # check subscription usage
61
+ egaki usage
62
+
63
+ # generate an image
64
+ egaki image "a watercolor fox reading a map" -o fox.png
65
+
66
+ # select a model explicitly
67
+ egaki image "isometric floating city, soft colors" -m imagen-4.0-generate-001 -o city.png
68
+
69
+ # edit an existing image (local file or URL)
70
+ egaki image "add a red scarf and make it winter" --input portrait.jpg -o portrait-winter.png
71
+ egaki image "turn this into a manga panel" --input https://example.com/photo.jpg -o manga.png
72
+
73
+ # inpainting with a mask
74
+ egaki image "replace the sky with a dramatic sunset" --input scene.png --mask mask.png -o scene-sunset.png
75
+
76
+ # generate a video — use a 5 minute timeout, video generation is slow
77
+ egaki video "a paper boat drifting on a calm lake at sunrise" -o boat.mp4
78
+
79
+ # generate a video with a specific model
80
+ egaki video "timelapse of a stormy sea, cinematic" -m google/veo-3.1-fast-generate-001 --duration 6 -o storm.mp4
81
+
82
+ # cheap video model
83
+ egaki video "a cat walking on a rooftop at night" -m klingai/kling-v2.5-turbo-t2v --duration 5 -o cat.mp4
84
+
85
+ # image-to-video (model must support i2v)
86
+ egaki video "slowly animate the clouds" --input photo.jpg -m klingai/kling-v2.6-i2v -o animated.mp4
87
+
88
+ # discover all models (image + video)
89
+ egaki models
90
+
91
+ # filter by type
92
+ egaki models --type video
93
+ egaki models --type image
94
+ ```
95
+
96
+ ## Video generation note for agents
97
+
98
+ Video generation can be very slow — some models take 1–3 minutes per request.
99
+ Always use a command timeout of **at least 5 minutes** when invoking `egaki video`
100
+ from automation or agent workflows.