egaki 0.2.0 → 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/CHANGELOG.md CHANGED
@@ -2,6 +2,38 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## 0.3.0
6
+
7
+ 1. **Google Vertex AI provider** — use Google Cloud billing instead of AI Studio by
8
+ prefixing model IDs with `vertex/`:
9
+
10
+ ```bash
11
+ egaki login --provider vertex --key AIza...
12
+ egaki image "product shot on marble" -m vertex/imagen-4.0-generate-001 -o product.png
13
+ egaki image "editorial portrait" -m vertex/gemini-3.1-flash-image-preview --aspect-ratio 4:5
14
+ egaki video "storm over mountains" -m vertex/veo-3.1-fast-generate-001 --duration 6 -o storm.mp4
15
+ ```
16
+
17
+ Bare model IDs (e.g. `imagen-4.0-generate-001`) continue to route through Google AI
18
+ Studio as before. `vertex/` prefix routes through `@ai-sdk/google-vertex` using your
19
+ `GOOGLE_VERTEX_API_KEY`. The two providers are fully independent — having one key does
20
+ not affect the other.
21
+
22
+ Supported Vertex models:
23
+ - `vertex/imagen-4.0-generate-001`, `vertex/imagen-4.0-ultra-generate-001`, `vertex/imagen-4.0-fast-generate-001`
24
+ - `vertex/gemini-2.5-flash-image`, `vertex/gemini-3-pro-image-preview`, `vertex/gemini-3.1-flash-image-preview`
25
+ - `vertex/veo-3.1-generate-001`, `vertex/veo-3.1-fast-generate-001`
26
+
27
+ 2. **`egaki models --provider vertex`** — list only Vertex models:
28
+
29
+ ```bash
30
+ egaki models --provider vertex
31
+ ```
32
+
33
+ 3. **Fixed confusing error for Vertex without a key** — if you attempt a `vertex/` model
34
+ without a `GOOGLE_VERTEX_API_KEY` configured, you now get a clear error pointing you
35
+ to set one up instead of a cryptic upstream failure.
36
+
5
37
  ## 0.2.0
6
38
 
7
39
  1. **New `egaki video` command** — generate videos from text prompts or still images.
package/README.md CHANGED
@@ -55,6 +55,14 @@ egaki image "cyberpunk alley at night" --aspect-ratio 16:9
55
55
  egaki image "polaroid-style travel photo" --aspect-ratio 4:5
56
56
  ```
57
57
 
58
+ ### Use Google Cloud billing via Vertex AI
59
+
60
+ ```bash
61
+ egaki login --provider vertex --key AIza...
62
+ egaki image "editorial sneaker photo on white seamless" -m vertex/imagen-4.0-generate-001 -o sneaker.png
63
+ egaki video "storm over mountains" -m vertex/veo-3.1-fast-generate-001 --duration 6 -o storm.mp4
64
+ ```
65
+
58
66
  ### Pipe image output to other tools
59
67
 
60
68
  ```bash
@@ -67,7 +75,7 @@ egaki image "flat icon of a fox" --stdout | magick - -resize 512x512 fox-icon.pn
67
75
 
68
76
  ```bash
69
77
  egaki video "a paper boat drifting on a calm lake at sunrise" -o boat.mp4
70
- egaki video "timelapse of a stormy sea, cinematic" -m google/veo-3.1-generate-001 --duration 8 -o storm.mp4
78
+ egaki video "timelapse of a stormy sea, cinematic" -m veo-3.1-generate-001 --duration 8 -o storm.mp4
71
79
  ```
72
80
 
73
81
  ### Generate with a cheap model
@@ -88,7 +96,7 @@ egaki video "slowly animate the clouds" --input photo.jpg -m klingai/kling-v2.6-
88
96
 
89
97
  ```bash
90
98
  egaki video "aerial drone shot over a city grid" \
91
- -m google/veo-3.1-fast-generate-001 \
99
+ -m veo-3.1-fast-generate-001 \
92
100
  --aspect-ratio 16:9 \
93
101
  --resolution 1080p \
94
102
  --duration 6 \
@@ -175,8 +183,9 @@ egaki image "mascot variations, flat vector look" \
175
183
 
176
184
  | Model ID | Best for | Example command |
177
185
  | --- | --- | --- |
178
- | `google/veo-3.1-generate-001` | Highest quality video with audio, up to 4K | `egaki video "rainy Tokyo street at night" -m google/veo-3.1-generate-001 --duration 8 -o tokyo.mp4` |
179
- | `google/veo-3.1-fast-generate-001` | Fast Veo with 720p–4K, good for iteration | `egaki video "abstract paint patterns" -m google/veo-3.1-fast-generate-001 --duration 5 -o paint.mp4` |
186
+ | `veo-3.1-generate-001` | Highest quality video with audio, up to 4K | `egaki video "rainy Tokyo street at night" -m veo-3.1-generate-001 --duration 8 -o tokyo.mp4` |
187
+ | `veo-3.1-fast-generate-001` | Fast Veo with 720p–4K, good for iteration | `egaki video "abstract paint patterns" -m veo-3.1-fast-generate-001 --duration 5 -o paint.mp4` |
188
+ | `vertex/veo-3.1-generate-001` | Same as above, routed through Vertex AI | `egaki video "rainy Tokyo street" -m vertex/veo-3.1-generate-001 --duration 8 -o tokyo.mp4` |
180
189
  | `klingai/kling-v2.5-turbo-t2v` | Cheap, fast Kling text-to-video | `egaki video "a paper boat on a pond" -m klingai/kling-v2.5-turbo-t2v --duration 5 -o boat.mp4` |
181
190
  | `bytedance/seedance-v1.5-pro` | Bytedance, audio support, three resolutions | `egaki video "timelapse of clouds above mountains" -m bytedance/seedance-v1.5-pro -o clouds.mp4` |
182
191
  | `xai/grok-imagine-video` | xAI video generation, cheap for short clips | `egaki video "a dog catching a frisbee" -m xai/grok-imagine-video --duration 3 -o dog.mp4` |
@@ -189,6 +198,7 @@ egaki image "mascot variations, flat vector look" \
189
198
  - **BFL image models (`flux-*`)**: Kontext/Pro variants via AI Gateway subscription
190
199
  - **Recraft models (`recraft-*`)**: v2/v3/v4 families available via AI Gateway subscription
191
200
  - **xAI image models (`grok-imagine-*`)**: Grok image generation via AI Gateway subscription
201
+ - **Vertex models (`vertex/*`)**: same models as Google AI Studio, routed through Vertex AI / Google Cloud billing
192
202
  - **Google Veo video models**: up to 4K, audio optional, duration 4–8s
193
203
  - **Kling video models**: mode (std/pro), audio on v2.6+, image-to-video support
194
204
  - **Bytedance Seedance**: 480p–1080p, audio support on v1.5-pro
@@ -200,6 +210,7 @@ egaki supports **both** authentication modes:
200
210
 
201
211
  - **BYOK (bring your own keys):** add provider keys with `egaki login` per provider.
202
212
  - **Egaki subscription:** use one `egaki_...` key to access all supported models without managing keys for each provider.
213
+ - **Google vs Vertex:** bare model IDs (e.g. `imagen-4.0-generate-001`) use Google AI Studio. Prefix with `vertex/` (e.g. `vertex/imagen-4.0-generate-001`) to route through Vertex AI / Google Cloud billing.
203
214
 
204
215
  ```bash
205
216
  # Subscribe and get a checkout URL
@@ -211,8 +222,9 @@ egaki subscribe --plan pro
211
222
  # Save your Egaki key after checkout
212
223
  egaki login --provider egaki --key egaki_...
213
224
 
214
- # BYOK example (direct provider key)
225
+ # BYOK examples (direct provider keys)
215
226
  egaki login --provider google --key AIza...
227
+ egaki login --provider vertex --key AIza...
216
228
 
217
229
  # Check subscription usage / cancel
218
230
  egaki usage
package/dist/cli.js CHANGED
@@ -55,6 +55,7 @@ cli
55
55
  .example('egaki login')
56
56
  .example('# Non-interactive login with flags')
57
57
  .example('egaki login --provider google --key AIza...')
58
+ .example('egaki login --provider vertex --key AIza...')
58
59
  .example('# Pipe key from stdin (useful in CI/scripts)')
59
60
  .example('echo "AIza..." | egaki login --provider google')
60
61
  .example('# Show configured providers')
@@ -170,6 +171,8 @@ cli
170
171
  .example('egaki image "fill with flowers" --input photo.jpg --mask mask.png')
171
172
  .example('# Generate with Gemini multimodal at 4K')
172
173
  .example('egaki image "dreamy landscape" -m gemini-2.5-flash-image --image-size 4K')
174
+ .example('# Route through Vertex AI (Google Cloud billing)')
175
+ .example('egaki image "product photo on marble" -m vertex/imagen-4.0-generate-001')
173
176
  .example('# Generate multiple images')
174
177
  .example('egaki image "abstract art" -n 4 -o art.png')
175
178
  .example('# Pipe to another tool')
@@ -250,6 +253,8 @@ cli
250
253
  .example('egaki video "A paper airplane gliding through clouds" -o airplane.mp4')
251
254
  .example('# Generate with Veo model + duration')
252
255
  .example('egaki video "cinematic rainy street at night" -m veo-3.1-fast-generate-001 --duration 6')
256
+ .example('# Route through Vertex AI (Google Cloud billing)')
257
+ .example('egaki video "storm over mountains" -m vertex/veo-3.1-fast-generate-001 --duration 6')
253
258
  .example('# Image-to-video (model support required)')
254
259
  .example('egaki video "animate subtle camera pan" --model luma-ray-2 --input frame.png -o animated.mp4')
255
260
  .example('# Generate multiple videos')
@@ -385,7 +390,10 @@ async function generateWithImageModel({ prompt, model, outputPath, count, aspect
385
390
  const imagePrompt = inputImages.length > 0
386
391
  ? { text: prompt, images: inputImages, ...(maskImage ? { mask: maskImage } : {}) }
387
392
  : prompt;
393
+ const config = getModelConfig(model);
388
394
  const imageModel = await createImageModel(model);
395
+ // Use the correct providerOptions key based on provider (google vs vertex)
396
+ const providerOptionsKey = config.provider === 'vertex' ? 'vertex' : 'google';
389
397
  if (!stdout) {
390
398
  console.error(pc.cyan('Generating...'));
391
399
  }
@@ -396,7 +404,7 @@ async function generateWithImageModel({ prompt, model, outputPath, count, aspect
396
404
  ...(aspectRatio ? { aspectRatio } : {}),
397
405
  ...(seed !== undefined ? { seed } : {}),
398
406
  providerOptions: {
399
- google: {
407
+ [providerOptionsKey]: {
400
408
  ...(allowPeople ? { personGeneration: 'allow_all' } : {}),
401
409
  ...(aspectRatio ? { aspectRatio } : {}),
402
410
  },
@@ -421,7 +429,6 @@ async function generateWithImageModel({ prompt, model, outputPath, count, aspect
421
429
  console.error(pc.green(`Saved: ${filePath}`));
422
430
  savedFiles.push(filePath);
423
431
  }
424
- const config = getModelConfig(model);
425
432
  const cost = calculateCost(config.cost, result.usage, result.images.length);
426
433
  if (cost != null) {
427
434
  console.error(pc.dim(`Cost: ${formatCost(cost)}`));
@@ -441,6 +448,8 @@ async function generateWithImageModel({ prompt, model, outputPath, count, aspect
441
448
  // Generate using generateText with responseModalities (Gemini multimodal models)
442
449
  async function generateWithTextModel({ prompt, model, outputPath, inputImages, imageSize, aspectRatio, json, stdout, }) {
443
450
  const textModel = await createTextModel(model);
451
+ const config = getModelConfig(model);
452
+ const providerOptionsKey = config.provider === 'vertex' ? 'vertex' : 'google';
444
453
  if (!stdout) {
445
454
  console.error(pc.cyan('Generating...'));
446
455
  }
@@ -463,7 +472,7 @@ async function generateWithTextModel({ prompt, model, outputPath, inputImages, i
463
472
  model: textModel,
464
473
  ...(messages ? { messages } : { prompt }),
465
474
  providerOptions: {
466
- google: {
475
+ [providerOptionsKey]: {
467
476
  responseModalities: ['TEXT', 'IMAGE'],
468
477
  ...(imageSize || aspectRatio
469
478
  ? {
@@ -501,7 +510,6 @@ async function generateWithTextModel({ prompt, model, outputPath, inputImages, i
501
510
  console.error(pc.green(`Saved: ${filePath}`));
502
511
  savedFiles.push(filePath);
503
512
  }
504
- const config = getModelConfig(model);
505
513
  const cost = calculateCost(config.cost, result.usage, imageFiles.length);
506
514
  if (cost != null) {
507
515
  console.error(pc.dim(`Cost: ${formatCost(cost)}`));
@@ -1 +1 @@
1
- {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,SAAS,EAAE,MAAM,CAC5B,MAAM,EACN;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CA2BhD,CAAA;AAED,eAAO,MAAM,iBAAiB,4BAA4B,CAAA;AAa1D,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAEhD,wBAAgB,eAAe,IAAI,WAAW,CAO7C;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAOzD;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAInE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAIxD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGnE;AAKD,wBAAgB,sBAAsB,IAAI,IAAI,CAW7C;AAID,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG;IAC9C,SAAS,EAAE,OAAO,CAAA;IAClB,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAA;CAClC,CAaA"}
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,SAAS,EAAE,MAAM,CAC5B,MAAM,EACN;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAgChD,CAAA;AAED,eAAO,MAAM,iBAAiB,4BAA4B,CAAA;AAa1D,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAEhD,wBAAgB,eAAe,IAAI,WAAW,CAO7C;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAOzD;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAInE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAIxD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGnE;AAKD,wBAAgB,sBAAsB,IAAI,IAAI,CAW7C;AAID,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG;IAC9C,SAAS,EAAE,OAAO,CAAA;IAClB,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAA;CAClC,CAaA"}
@@ -18,6 +18,11 @@ export const PROVIDERS = {
18
18
  label: 'Google AI (Gemini, Imagen)',
19
19
  hint: 'Get your key at https://aistudio.google.com/apikey',
20
20
  },
21
+ vertex: {
22
+ envVar: 'GOOGLE_VERTEX_API_KEY',
23
+ label: 'Google Vertex AI (Gemini, Imagen, Veo via Google Cloud)',
24
+ hint: 'Get an API key at https://console.cloud.google.com/apis/credentials',
25
+ },
21
26
  openai: {
22
27
  envVar: 'OPENAI_API_KEY',
23
28
  label: 'OpenAI (DALL-E)',
@@ -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,EAs4B/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',
@@ -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;AAqDD,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
@@ -4,6 +4,16 @@ 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,7 +124,8 @@ 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
  }
103
131
  // Lazily import the provider and create the right model instance.
@@ -115,6 +143,10 @@ export async function createImageModel(modelId) {
115
143
  const { google } = await import('@ai-sdk/google');
116
144
  return google.image(modelId);
117
145
  }
146
+ case 'vertex': {
147
+ const { vertex } = await import('@ai-sdk/google-vertex');
148
+ return vertex.image(stripProviderPrefix(modelId));
149
+ }
118
150
  case 'openai': {
119
151
  const { openai } = await import('@ai-sdk/openai');
120
152
  return openai.image(modelId);
@@ -151,9 +183,13 @@ export async function createTextModel(modelId) {
151
183
  const { google } = await import('@ai-sdk/google');
152
184
  return google(modelId);
153
185
  }
186
+ case 'vertex': {
187
+ const { vertex } = await import('@ai-sdk/google-vertex');
188
+ return vertex(stripProviderPrefix(modelId));
189
+ }
154
190
  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}`));
191
+ // Only Google/Vertex support generateText with responseModalities for images
192
+ console.error(pc.red(`Text+image generation is only supported for Google/Vertex models, got provider: ${config.provider}`));
157
193
  process.exit(1);
158
194
  }
159
195
  }
@@ -178,12 +214,16 @@ export async function createVideoModel(modelId) {
178
214
  const { google } = await import('@ai-sdk/google');
179
215
  return google.video(modelId);
180
216
  }
217
+ case 'vertex': {
218
+ const { vertex } = await import('@ai-sdk/google-vertex');
219
+ return vertex.video(stripProviderPrefix(modelId));
220
+ }
181
221
  case 'fal': {
182
222
  const { fal } = await import('@ai-sdk/fal');
183
223
  return fal.video(modelId);
184
224
  }
185
225
  default:
186
- console.error(pc.red(`Direct video generation is only supported for Google and Fal keys, got provider: ${config.provider}`));
226
+ console.error(pc.red(`Direct video generation is only supported for Google, Vertex, and Fal keys, got provider: ${config.provider}`));
187
227
  process.exit(1);
188
228
  }
189
229
  }
@@ -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.3.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,6 +49,7 @@
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",
@@ -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.
package/src/cli.ts CHANGED
@@ -98,6 +98,7 @@ cli
98
98
  .example('egaki login')
99
99
  .example('# Non-interactive login with flags')
100
100
  .example('egaki login --provider google --key AIza...')
101
+ .example('egaki login --provider vertex --key AIza...')
101
102
  .example('# Pipe key from stdin (useful in CI/scripts)')
102
103
  .example('echo "AIza..." | egaki login --provider google')
103
104
  .example('# Show configured providers')
@@ -283,6 +284,8 @@ cli
283
284
  .example('egaki image "fill with flowers" --input photo.jpg --mask mask.png')
284
285
  .example('# Generate with Gemini multimodal at 4K')
285
286
  .example('egaki image "dreamy landscape" -m gemini-2.5-flash-image --image-size 4K')
287
+ .example('# Route through Vertex AI (Google Cloud billing)')
288
+ .example('egaki image "product photo on marble" -m vertex/imagen-4.0-generate-001')
286
289
  .example('# Generate multiple images')
287
290
  .example('egaki image "abstract art" -n 4 -o art.png')
288
291
  .example('# Pipe to another tool')
@@ -410,6 +413,8 @@ cli
410
413
  .example('egaki video "A paper airplane gliding through clouds" -o airplane.mp4')
411
414
  .example('# Generate with Veo model + duration')
412
415
  .example('egaki video "cinematic rainy street at night" -m veo-3.1-fast-generate-001 --duration 6')
416
+ .example('# Route through Vertex AI (Google Cloud billing)')
417
+ .example('egaki video "storm over mountains" -m vertex/veo-3.1-fast-generate-001 --duration 6')
413
418
  .example('# Image-to-video (model support required)')
414
419
  .example('egaki video "animate subtle camera pan" --model luma-ray-2 --input frame.png -o animated.mp4')
415
420
  .example('# Generate multiple videos')
@@ -597,8 +602,12 @@ async function generateWithImageModel({
597
602
  ? { text: prompt, images: inputImages, ...(maskImage ? { mask: maskImage } : {}) }
598
603
  : prompt
599
604
 
605
+ const config = getModelConfig(model)
600
606
  const imageModel = await createImageModel(model)
601
607
 
608
+ // Use the correct providerOptions key based on provider (google vs vertex)
609
+ const providerOptionsKey = config.provider === 'vertex' ? 'vertex' : 'google'
610
+
602
611
  if (!stdout) {
603
612
  console.error(pc.cyan('Generating...'))
604
613
  }
@@ -610,7 +619,7 @@ async function generateWithImageModel({
610
619
  ...(aspectRatio ? { aspectRatio } : {}),
611
620
  ...(seed !== undefined ? { seed } : {}),
612
621
  providerOptions: {
613
- google: {
622
+ [providerOptionsKey]: {
614
623
  ...(allowPeople ? { personGeneration: 'allow_all' } : {}),
615
624
  ...(aspectRatio ? { aspectRatio } : {}),
616
625
  },
@@ -640,7 +649,6 @@ async function generateWithImageModel({
640
649
  savedFiles.push(filePath)
641
650
  }
642
651
 
643
- const config = getModelConfig(model)
644
652
  const cost = calculateCost(config.cost, result.usage, result.images.length)
645
653
  if (cost != null) {
646
654
  console.error(pc.dim(`Cost: ${formatCost(cost)}`))
@@ -680,6 +688,8 @@ async function generateWithTextModel({
680
688
  stdout: boolean
681
689
  }) {
682
690
  const textModel = await createTextModel(model)
691
+ const config = getModelConfig(model)
692
+ const providerOptionsKey = config.provider === 'vertex' ? 'vertex' : 'google'
683
693
 
684
694
  if (!stdout) {
685
695
  console.error(pc.cyan('Generating...'))
@@ -705,7 +715,7 @@ async function generateWithTextModel({
705
715
  model: textModel,
706
716
  ...(messages ? { messages } : { prompt }),
707
717
  providerOptions: {
708
- google: {
718
+ [providerOptionsKey]: {
709
719
  responseModalities: ['TEXT', 'IMAGE'],
710
720
  ...(imageSize || aspectRatio
711
721
  ? {
@@ -750,7 +760,6 @@ async function generateWithTextModel({
750
760
  savedFiles.push(filePath)
751
761
  }
752
762
 
753
- const config = getModelConfig(model)
754
763
  const cost = calculateCost(config.cost, result.usage, imageFiles.length)
755
764
  if (cost != null) {
756
765
  console.error(pc.dim(`Cost: ${formatCost(cost)}`))
@@ -22,6 +22,11 @@ export const PROVIDERS: Record<
22
22
  label: 'Google AI (Gemini, Imagen)',
23
23
  hint: 'Get your key at https://aistudio.google.com/apikey',
24
24
  },
25
+ vertex: {
26
+ envVar: 'GOOGLE_VERTEX_API_KEY',
27
+ label: 'Google Vertex AI (Gemini, Imagen, Veo via Google Cloud)',
28
+ hint: 'Get an API key at https://console.cloud.google.com/apis/credentials',
29
+ },
25
30
  openai: {
26
31
  envVar: 'OPENAI_API_KEY',
27
32
  label: 'OpenAI (DALL-E)',
@@ -109,6 +109,22 @@ const xaiImage = {
109
109
  strategy: 'image' as const,
110
110
  }
111
111
 
112
+ const vertexImagen = {
113
+ provider: 'vertex',
114
+ strategy: 'image' as const,
115
+ features: {
116
+ ...googleImagen.features,
117
+ },
118
+ }
119
+
120
+ const vertexText = {
121
+ provider: 'vertex',
122
+ strategy: 'text' as const,
123
+ features: {
124
+ ...googleText.features,
125
+ },
126
+ }
127
+
112
128
  const fluxAspectRatios = ['1:1', '3:4', '4:3', '9:16', '16:9', '9:21', '21:9']
113
129
 
114
130
  // ─── catalog ─────────────────────────────────────────────────────────────────
@@ -196,6 +212,64 @@ export const CATALOG: ModelEntry[] = [
196
212
  cost: { type: 'per-token', inputPerM: 0.5, outputPerM: 3.0 },
197
213
  },
198
214
 
215
+ // ── Vertex: Imagen ──────────────────────────────────────────────────────
216
+ // Same models as Google AI Studio but routed through Vertex AI / Google Cloud billing.
217
+ // Use `egaki login --provider vertex --key <key>` to configure.
218
+ {
219
+ id: 'vertex/imagen-4.0-generate-001',
220
+ name: 'Imagen 4 (Vertex)',
221
+ released: '2025-08-15',
222
+ ...vertexImagen,
223
+ cost: { type: 'per-image', perImage: 0.04 },
224
+ },
225
+ {
226
+ id: 'vertex/imagen-4.0-ultra-generate-001',
227
+ name: 'Imagen 4 Ultra (Vertex)',
228
+ released: '2025-08-15',
229
+ ...vertexImagen,
230
+ cost: { type: 'per-image', perImage: 0.06 },
231
+ },
232
+ {
233
+ id: 'vertex/imagen-4.0-fast-generate-001',
234
+ name: 'Imagen 4 Fast (Vertex)',
235
+ released: '2025-08-15',
236
+ ...vertexImagen,
237
+ cost: { type: 'per-image', perImage: 0.02 },
238
+ },
239
+
240
+ // ── Vertex: Gemini text+image ─────────────────────────────────────────
241
+ {
242
+ id: 'vertex/gemini-2.5-flash-image',
243
+ name: 'Gemini 2.5 Flash Image (Vertex)',
244
+ released: '2025-08-26',
245
+ ...vertexText,
246
+ cost: { type: 'per-token', inputPerM: 0.3, outputPerM: 30 },
247
+ },
248
+ {
249
+ id: 'vertex/gemini-3-pro-image-preview',
250
+ name: 'Gemini 3 Pro Image (Vertex)',
251
+ released: '2025-11-20',
252
+ ...vertexText,
253
+ cost: { type: 'per-token', inputPerM: 1.25, outputPerM: 10 },
254
+ },
255
+ {
256
+ id: 'vertex/gemini-3.1-flash-image-preview',
257
+ name: 'Gemini 3.1 Flash Image (Vertex)',
258
+ released: '2026-02-26',
259
+ ...vertexText,
260
+ features: {
261
+ ...vertexText.features,
262
+ aspectRatios: [
263
+ ...vertexText.features.aspectRatios,
264
+ '4:1',
265
+ '1:4',
266
+ '8:1',
267
+ '1:8',
268
+ ],
269
+ },
270
+ cost: { type: 'per-token', inputPerM: 0.5, outputPerM: 3.0 },
271
+ },
272
+
199
273
  // ── BFL (AI Gateway) ───────────────────────────────────────────────────
200
274
  {
201
275
  id: 'flux-kontext-max',
package/src/models.ts CHANGED
@@ -29,6 +29,16 @@ export type { ModelEntry, VideoModelEntry }
29
29
  export const IMAGE_MODELS = CATALOG.map((m) => m.id) as [string, ...string[]]
30
30
  export const VIDEO_MODELS = VIDEO_CATALOG.map((m) => m.id) as [string, ...string[]]
31
31
 
32
+ /**
33
+ * Strip provider prefix from a model ID (e.g. "vertex/imagen-4.0-generate-001" → "imagen-4.0-generate-001").
34
+ * Provider SDKs expect bare model IDs, but the catalog uses prefixed IDs for routing.
35
+ */
36
+ function stripProviderPrefix(modelId: string): string {
37
+ const slash = modelId.indexOf('/')
38
+ if (slash === -1) return modelId
39
+ return modelId.slice(slash + 1)
40
+ }
41
+
32
42
  export const DEFAULT_MODEL = 'nano-banana-pro-preview'
33
43
  export const DEFAULT_VIDEO_MODEL = 'veo-3.1-fast-generate-001'
34
44
 
@@ -66,6 +76,20 @@ export function ensureProviderKey(providerName: string): void {
66
76
  // If direct provider key exists, we're good
67
77
  if (hasDirectProviderKey(providerName)) return
68
78
 
79
+ // Vertex models require a direct key — the upstream Vercel AI Gateway
80
+ // does not support vertex/ routing, so egaki gateway can't proxy them.
81
+ if (providerName === 'vertex') {
82
+ console.error('')
83
+ console.error(pc.red(pc.bold('Vertex models require a direct Google Cloud API key')))
84
+ console.error('')
85
+ console.error(` ${pc.cyan('egaki login --provider vertex --key <key>')}`)
86
+ console.error('')
87
+ console.error(pc.dim('Get a key at https://console.cloud.google.com/apis/credentials'))
88
+ console.error(pc.dim('Egaki subscription does not cover Vertex routing yet.'))
89
+ console.error('')
90
+ process.exit(1)
91
+ }
92
+
69
93
  // If egaki key exists, we'll route through the gateway
70
94
  if (hasEgakiKey()) return
71
95
 
@@ -109,8 +133,11 @@ async function createGatewayImageModel(modelId: string, provider: string): Promi
109
133
  apiKey: process.env['EGAKI_API_KEY']!,
110
134
  baseURL: EGAKI_GATEWAY_URL,
111
135
  })
112
- // Gateway expects provider/model format for routing
113
- const gatewayModelId = `${provider}/${modelId}`
136
+ // Gateway expects provider/model format for routing.
137
+ // For models that already have a prefix (e.g. vertex/imagen-4.0), strip it
138
+ // and use the catalog provider. For bare IDs, prepend the provider.
139
+ const bareId = stripProviderPrefix(modelId)
140
+ const gatewayModelId = `${provider}/${bareId}`
114
141
  return gateway.image(gatewayModelId)
115
142
  }
116
143
 
@@ -123,7 +150,8 @@ async function createGatewayTextModel(modelId: string, provider: string): Promis
123
150
  apiKey: process.env['EGAKI_API_KEY']!,
124
151
  baseURL: EGAKI_GATEWAY_URL,
125
152
  })
126
- const gatewayModelId = `${provider}/${modelId}`
153
+ const bareId = stripProviderPrefix(modelId)
154
+ const gatewayModelId = `${provider}/${bareId}`
127
155
  return gateway(gatewayModelId)
128
156
  }
129
157
 
@@ -136,7 +164,8 @@ async function createGatewayVideoModel(modelId: string, provider: string) {
136
164
  apiKey: process.env['EGAKI_API_KEY']!,
137
165
  baseURL: EGAKI_GATEWAY_URL,
138
166
  })
139
- const gatewayModelId = `${provider}/${modelId}`
167
+ const bareId = stripProviderPrefix(modelId)
168
+ const gatewayModelId = `${provider}/${bareId}`
140
169
  return gateway.video(gatewayModelId)
141
170
  }
142
171
 
@@ -156,6 +185,10 @@ export async function createImageModel(modelId: string): Promise<ImageModel> {
156
185
  const { google } = await import('@ai-sdk/google')
157
186
  return google.image(modelId)
158
187
  }
188
+ case 'vertex': {
189
+ const { vertex } = await import('@ai-sdk/google-vertex')
190
+ return vertex.image(stripProviderPrefix(modelId))
191
+ }
159
192
  case 'openai': {
160
193
  const { openai } = await import('@ai-sdk/openai')
161
194
  return openai.image(modelId)
@@ -198,11 +231,15 @@ export async function createTextModel(
198
231
  const { google } = await import('@ai-sdk/google')
199
232
  return google(modelId)
200
233
  }
234
+ case 'vertex': {
235
+ const { vertex } = await import('@ai-sdk/google-vertex')
236
+ return vertex(stripProviderPrefix(modelId))
237
+ }
201
238
  default:
202
- // Only Google supports generateText with responseModalities for images
239
+ // Only Google/Vertex support generateText with responseModalities for images
203
240
  console.error(
204
241
  pc.red(
205
- `Text+image generation is only supported for Google models, got provider: ${config.provider}`,
242
+ `Text+image generation is only supported for Google/Vertex models, got provider: ${config.provider}`,
206
243
  ),
207
244
  )
208
245
  process.exit(1)
@@ -234,6 +271,10 @@ export async function createVideoModel(modelId: string): Promise<any> {
234
271
  const { google } = await import('@ai-sdk/google')
235
272
  return google.video(modelId)
236
273
  }
274
+ case 'vertex': {
275
+ const { vertex } = await import('@ai-sdk/google-vertex')
276
+ return vertex.video(stripProviderPrefix(modelId))
277
+ }
237
278
  case 'fal': {
238
279
  const { fal } = await import('@ai-sdk/fal')
239
280
  return fal.video(modelId)
@@ -241,7 +282,7 @@ export async function createVideoModel(modelId: string): Promise<any> {
241
282
  default:
242
283
  console.error(
243
284
  pc.red(
244
- `Direct video generation is only supported for Google and Fal keys, got provider: ${config.provider}`,
285
+ `Direct video generation is only supported for Google, Vertex, and Fal keys, got provider: ${config.provider}`,
245
286
  ),
246
287
  )
247
288
  process.exit(1)
@@ -169,6 +169,67 @@ export const VIDEO_CATALOG: VideoModelEntry[] = [
169
169
  multipleVideos: true,
170
170
  },
171
171
  },
172
+ // ── Vertex: Veo ──────────────────────────────────────────────────────
173
+ // Same Veo models routed through Vertex AI / Google Cloud billing.
174
+ // Use `egaki login --provider vertex --key <key>` to configure.
175
+ {
176
+ id: 'vertex/veo-3.1-generate-001',
177
+ name: 'Veo 3.1 (Vertex)',
178
+ provider: 'vertex',
179
+ strategy: 'video',
180
+ released: '2026-01',
181
+ cost: {
182
+ type: 'per-video-second',
183
+ defaultDurationSec: 8,
184
+ tiers: [
185
+ { resolution: '720p', audio: false, costPerSecond: 0.2 },
186
+ { resolution: '720p', audio: true, costPerSecond: 0.4 },
187
+ { resolution: '1080p', audio: false, costPerSecond: 0.2 },
188
+ { resolution: '1080p', audio: true, costPerSecond: 0.4 },
189
+ { resolution: '4k', audio: false, costPerSecond: 0.4 },
190
+ { resolution: '4k', audio: true, costPerSecond: 0.6 },
191
+ ],
192
+ },
193
+ features: {
194
+ textToVideo: true,
195
+ imageToVideo: false,
196
+ capabilities: ['t2v'],
197
+ aspectRatios: ['16:9', '9:16'],
198
+ resolutions: ['720p', '1080p', '4k'],
199
+ durationRangeSec: { min: 4, max: 8 },
200
+ seed: true,
201
+ multipleVideos: true,
202
+ },
203
+ },
204
+ {
205
+ id: 'vertex/veo-3.1-fast-generate-001',
206
+ name: 'Veo 3.1 Fast (Vertex)',
207
+ provider: 'vertex',
208
+ strategy: 'video',
209
+ released: '2026-01',
210
+ cost: {
211
+ type: 'per-video-second',
212
+ defaultDurationSec: 8,
213
+ tiers: [
214
+ { resolution: '720p', audio: false, costPerSecond: 0.1 },
215
+ { resolution: '720p', audio: true, costPerSecond: 0.15 },
216
+ { resolution: '1080p', audio: false, costPerSecond: 0.1 },
217
+ { resolution: '1080p', audio: true, costPerSecond: 0.15 },
218
+ { resolution: '4k', audio: false, costPerSecond: 0.3 },
219
+ { resolution: '4k', audio: true, costPerSecond: 0.35 },
220
+ ],
221
+ },
222
+ features: {
223
+ textToVideo: true,
224
+ imageToVideo: false,
225
+ capabilities: ['t2v'],
226
+ aspectRatios: ['16:9', '9:16'],
227
+ resolutions: ['720p', '1080p', '4k'],
228
+ durationRangeSec: { min: 4, max: 8 },
229
+ seed: true,
230
+ multipleVideos: true,
231
+ },
232
+ },
172
233
  {
173
234
  id: 'grok-imagine-video',
174
235
  name: 'Grok Imagine Video',