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/CHANGELOG.md +70 -0
- package/README.md +32 -5
- package/dist/chatgpt-auth.d.ts +25 -0
- package/dist/chatgpt-auth.d.ts.map +1 -0
- package/dist/chatgpt-auth.js +18 -11
- package/dist/cli.js +246 -19
- package/dist/credentials.d.ts +17 -4
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +55 -10
- package/dist/login.d.ts +1 -1
- package/dist/login.d.ts.map +1 -1
- package/dist/login.js +44 -3
- package/dist/model-catalog.d.ts.map +1 -1
- package/dist/model-catalog.js +87 -3
- package/dist/models.d.ts +5 -0
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +58 -8
- package/dist/video-model-catalog.d.ts.map +1 -1
- package/dist/video-model-catalog.js +61 -0
- package/package.json +5 -2
- package/skills/egaki/SKILL.md +100 -0
- package/src/chatgpt-auth.ts +387 -0
- package/src/cli.ts +317 -22
- package/src/credentials.ts +75 -17
- package/src/login.ts +49 -4
- package/src/model-catalog.ts +92 -3
- package/src/models.ts +59 -8
- package/src/video-model-catalog.ts +61 -0
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,
|
|
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
|
-
|
|
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+
|
|
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"}
|
package/dist/model-catalog.js
CHANGED
|
@@ -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
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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>;
|
package/dist/models.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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.
|
|
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
|
-
"
|
|
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.
|