egaki 0.0.1 → 0.1.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 +54 -0
- package/README.md +239 -0
- package/dist/antigravity-auth.d.ts +15 -0
- package/dist/antigravity-auth.d.ts.map +1 -0
- package/dist/antigravity-auth.js +544 -0
- package/dist/antigravity-image.d.ts +11 -0
- package/dist/antigravity-image.d.ts.map +1 -0
- package/dist/antigravity-image.js +285 -0
- package/dist/chatgpt-auth.js +294 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +666 -0
- package/dist/credentials.d.ts +18 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +107 -0
- package/dist/gateway-model-candidates.generated.d.ts +11 -0
- package/dist/gateway-model-candidates.generated.d.ts.map +1 -0
- package/dist/gateway-model-candidates.generated.js +64 -0
- package/dist/login.d.ts +9 -0
- package/dist/login.d.ts.map +1 -0
- package/dist/login.js +112 -0
- package/dist/model-catalog.d.ts +42 -0
- package/dist/model-catalog.d.ts.map +1 -0
- package/dist/model-catalog.js +898 -0
- package/dist/models.d.ts +15 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +196 -0
- package/dist/open-browser.d.ts +2 -0
- package/dist/open-browser.d.ts.map +1 -0
- package/dist/open-browser.js +37 -0
- package/dist/subscription.d.ts +5 -0
- package/dist/subscription.d.ts.map +1 -0
- package/dist/subscription.js +216 -0
- package/dist/video-model-catalog.d.ts +52 -0
- package/dist/video-model-catalog.d.ts.map +1 -0
- package/dist/video-model-catalog.js +245 -0
- package/package.json +68 -5
- package/src/cli.ts +998 -0
- package/src/credentials.ts +126 -0
- package/src/login.ts +163 -0
- package/src/model-catalog.ts +968 -0
- package/src/models.ts +258 -0
- package/src/open-browser.ts +48 -0
- package/src/subscription.ts +298 -0
- package/src/video-model-catalog.ts +302 -0
- package/index.js +0 -1
package/dist/models.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ImageModel, LanguageModel } from 'ai';
|
|
2
|
+
import type { ModelEntry } from './model-catalog.js';
|
|
3
|
+
import type { VideoModelEntry } from './video-model-catalog.js';
|
|
4
|
+
export type AnyModelEntry = ModelEntry | VideoModelEntry;
|
|
5
|
+
export type { ModelEntry, VideoModelEntry };
|
|
6
|
+
export declare const IMAGE_MODELS: [string, ...string[]];
|
|
7
|
+
export declare const VIDEO_MODELS: [string, ...string[]];
|
|
8
|
+
export declare const DEFAULT_MODEL = "nano-banana-pro-preview";
|
|
9
|
+
export declare const DEFAULT_VIDEO_MODEL = "veo-3.1-fast-generate-001";
|
|
10
|
+
export declare function getModelConfig(modelId: string): AnyModelEntry;
|
|
11
|
+
export declare function ensureProviderKey(providerName: string): void;
|
|
12
|
+
export declare function createImageModel(modelId: string): Promise<ImageModel>;
|
|
13
|
+
export declare function createTextModel(modelId: string): Promise<LanguageModel>;
|
|
14
|
+
export declare function createVideoModel(modelId: string): Promise<any>;
|
|
15
|
+
//# sourceMappingURL=models.d.ts.map
|
|
@@ -0,0 +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"}
|
package/dist/models.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import pc from 'picocolors';
|
|
2
|
+
import { PROVIDERS, EGAKI_GATEWAY_URL } from './credentials.js';
|
|
3
|
+
import { CATALOG, findModel } from './model-catalog.js';
|
|
4
|
+
import { VIDEO_CATALOG, findVideoModel } from './video-model-catalog.js';
|
|
5
|
+
export const IMAGE_MODELS = CATALOG.map((m) => m.id);
|
|
6
|
+
export const VIDEO_MODELS = VIDEO_CATALOG.map((m) => m.id);
|
|
7
|
+
export const DEFAULT_MODEL = 'nano-banana-pro-preview';
|
|
8
|
+
export const DEFAULT_VIDEO_MODEL = 'veo-3.1-fast-generate-001';
|
|
9
|
+
export function getModelConfig(modelId) {
|
|
10
|
+
const entry = findModel(modelId) ?? findVideoModel(modelId);
|
|
11
|
+
if (!entry) {
|
|
12
|
+
console.error(pc.red(`Unknown model: ${modelId}`));
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
return entry;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Check if the egaki gateway API key is available (from env or stored credentials).
|
|
19
|
+
*/
|
|
20
|
+
function hasEgakiKey() {
|
|
21
|
+
const info = PROVIDERS['egaki'];
|
|
22
|
+
if (!info)
|
|
23
|
+
return false;
|
|
24
|
+
return Boolean(process.env[info.envVar]);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Check if a direct provider key is available for the given provider.
|
|
28
|
+
*/
|
|
29
|
+
function hasDirectProviderKey(providerName) {
|
|
30
|
+
const info = PROVIDERS[providerName];
|
|
31
|
+
if (!info)
|
|
32
|
+
return false;
|
|
33
|
+
return Boolean(process.env[info.envVar]);
|
|
34
|
+
}
|
|
35
|
+
// Check that the provider's API key is available before making API calls.
|
|
36
|
+
// Prints a user-friendly error with instructions on how to configure it.
|
|
37
|
+
// Prioritizes egaki subscription over individual provider keys.
|
|
38
|
+
export function ensureProviderKey(providerName) {
|
|
39
|
+
// If direct provider key exists, we're good
|
|
40
|
+
if (hasDirectProviderKey(providerName))
|
|
41
|
+
return;
|
|
42
|
+
// If egaki key exists, we'll route through the gateway
|
|
43
|
+
if (hasEgakiKey())
|
|
44
|
+
return;
|
|
45
|
+
const info = PROVIDERS[providerName];
|
|
46
|
+
console.error('');
|
|
47
|
+
console.error(pc.red(pc.bold(`Missing API key for ${info?.label || providerName}`)));
|
|
48
|
+
console.error('');
|
|
49
|
+
console.error(` ${pc.bold('Recommended:')} Use Egaki subscription (all models, one key)`);
|
|
50
|
+
console.error('');
|
|
51
|
+
console.error(` ${pc.cyan('egaki subscribe')} get started in 30 seconds`);
|
|
52
|
+
console.error('');
|
|
53
|
+
console.error(` ${pc.dim('Or configure a provider key directly:')}`);
|
|
54
|
+
console.error('');
|
|
55
|
+
console.error(` ${pc.cyan('egaki login')} interactive setup`);
|
|
56
|
+
if (info) {
|
|
57
|
+
console.error(` ${pc.cyan(`${info.envVar}=...`)} egaki image ... inline env var`);
|
|
58
|
+
console.error(` ${pc.cyan(`egaki login --provider ${providerName} --key <key>`)}`);
|
|
59
|
+
console.error('');
|
|
60
|
+
console.error(` ${pc.dim(info.hint)}`);
|
|
61
|
+
}
|
|
62
|
+
console.error('');
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Create a gateway-backed image model using createGateway from the AI SDK.
|
|
67
|
+
* The model ID is sent as provider/model format to the gateway.
|
|
68
|
+
*/
|
|
69
|
+
async function createGatewayImageModel(modelId, provider) {
|
|
70
|
+
const { createGateway } = await import('ai');
|
|
71
|
+
const gateway = createGateway({
|
|
72
|
+
apiKey: process.env['EGAKI_API_KEY'],
|
|
73
|
+
baseURL: EGAKI_GATEWAY_URL,
|
|
74
|
+
});
|
|
75
|
+
// Gateway expects provider/model format for routing
|
|
76
|
+
const gatewayModelId = `${provider}/${modelId}`;
|
|
77
|
+
return gateway.image(gatewayModelId);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Create a gateway-backed text model using createGateway from the AI SDK.
|
|
81
|
+
*/
|
|
82
|
+
async function createGatewayTextModel(modelId, provider) {
|
|
83
|
+
const { createGateway } = await import('ai');
|
|
84
|
+
const gateway = createGateway({
|
|
85
|
+
apiKey: process.env['EGAKI_API_KEY'],
|
|
86
|
+
baseURL: EGAKI_GATEWAY_URL,
|
|
87
|
+
});
|
|
88
|
+
const gatewayModelId = `${provider}/${modelId}`;
|
|
89
|
+
return gateway(gatewayModelId);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Create a gateway-backed video model using createGateway from the AI SDK.
|
|
93
|
+
*/
|
|
94
|
+
async function createGatewayVideoModel(modelId, provider) {
|
|
95
|
+
const { createGateway } = await import('ai');
|
|
96
|
+
const gateway = createGateway({
|
|
97
|
+
apiKey: process.env['EGAKI_API_KEY'],
|
|
98
|
+
baseURL: EGAKI_GATEWAY_URL,
|
|
99
|
+
});
|
|
100
|
+
const gatewayModelId = `${provider}/${modelId}`;
|
|
101
|
+
return gateway.video(gatewayModelId);
|
|
102
|
+
}
|
|
103
|
+
// Lazily import the provider and create the right model instance.
|
|
104
|
+
// This avoids loading all provider SDKs upfront — only the one needed
|
|
105
|
+
// for the selected model gets imported.
|
|
106
|
+
//
|
|
107
|
+
// Priority: direct provider key > egaki gateway > error
|
|
108
|
+
export async function createImageModel(modelId) {
|
|
109
|
+
const config = getModelConfig(modelId);
|
|
110
|
+
ensureProviderKey(config.provider);
|
|
111
|
+
// If the user has a direct provider key, use the provider SDK directly
|
|
112
|
+
if (hasDirectProviderKey(config.provider)) {
|
|
113
|
+
switch (config.provider) {
|
|
114
|
+
case 'google': {
|
|
115
|
+
const { google } = await import('@ai-sdk/google');
|
|
116
|
+
return google.image(modelId);
|
|
117
|
+
}
|
|
118
|
+
case 'openai': {
|
|
119
|
+
const { openai } = await import('@ai-sdk/openai');
|
|
120
|
+
return openai.image(modelId);
|
|
121
|
+
}
|
|
122
|
+
case 'replicate': {
|
|
123
|
+
const { replicate } = await import('@ai-sdk/replicate');
|
|
124
|
+
return replicate.image(modelId);
|
|
125
|
+
}
|
|
126
|
+
case 'fal': {
|
|
127
|
+
const { fal } = await import('@ai-sdk/fal');
|
|
128
|
+
return fal.image(modelId);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Fall back to egaki gateway
|
|
133
|
+
if (hasEgakiKey()) {
|
|
134
|
+
return createGatewayImageModel(modelId, config.provider);
|
|
135
|
+
}
|
|
136
|
+
// Should not reach here — ensureProviderKey would have exited
|
|
137
|
+
console.error(pc.red(`No API key available for provider: ${config.provider}`));
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
export async function createTextModel(modelId) {
|
|
141
|
+
const config = getModelConfig(modelId);
|
|
142
|
+
if (config.strategy !== 'text') {
|
|
143
|
+
console.error(pc.red(`Model ${modelId} is not a text model`));
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
ensureProviderKey(config.provider);
|
|
147
|
+
// Direct provider key takes priority
|
|
148
|
+
if (hasDirectProviderKey(config.provider)) {
|
|
149
|
+
switch (config.provider) {
|
|
150
|
+
case 'google': {
|
|
151
|
+
const { google } = await import('@ai-sdk/google');
|
|
152
|
+
return google(modelId);
|
|
153
|
+
}
|
|
154
|
+
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}`));
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Fall back to egaki gateway
|
|
161
|
+
if (hasEgakiKey()) {
|
|
162
|
+
return createGatewayTextModel(modelId, config.provider);
|
|
163
|
+
}
|
|
164
|
+
console.error(pc.red(`No API key available for provider: ${config.provider}`));
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
export async function createVideoModel(modelId) {
|
|
168
|
+
const config = getModelConfig(modelId);
|
|
169
|
+
if (config.strategy !== 'video') {
|
|
170
|
+
console.error(pc.red(`Model ${modelId} is not a video model`));
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
ensureProviderKey(config.provider);
|
|
174
|
+
// Direct provider key takes priority
|
|
175
|
+
if (hasDirectProviderKey(config.provider)) {
|
|
176
|
+
switch (config.provider) {
|
|
177
|
+
case 'google': {
|
|
178
|
+
const { google } = await import('@ai-sdk/google');
|
|
179
|
+
return google.video(modelId);
|
|
180
|
+
}
|
|
181
|
+
case 'fal': {
|
|
182
|
+
const { fal } = await import('@ai-sdk/fal');
|
|
183
|
+
return fal.video(modelId);
|
|
184
|
+
}
|
|
185
|
+
default:
|
|
186
|
+
console.error(pc.red(`Direct video generation is only supported for Google and Fal keys, got provider: ${config.provider}`));
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// Fall back to egaki gateway
|
|
191
|
+
if (hasEgakiKey()) {
|
|
192
|
+
return createGatewayVideoModel(modelId, config.provider);
|
|
193
|
+
}
|
|
194
|
+
console.error(pc.red(`No API key available for provider: ${config.provider}`));
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-browser.d.ts","sourceRoot":"","sources":["../src/open-browser.ts"],"names":[],"mappings":"AAyBA,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAsBrD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Cross-platform browser opener for URLs used by CLI flows.
|
|
2
|
+
// Never throws: missing OS commands are handled gracefully.
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
function getOpenCommands(url) {
|
|
5
|
+
if (process.platform === 'darwin') {
|
|
6
|
+
return [{ command: 'open', args: [url] }];
|
|
7
|
+
}
|
|
8
|
+
if (process.platform === 'win32') {
|
|
9
|
+
// `start` is a cmd builtin, so invoke through cmd.exe.
|
|
10
|
+
return [{ command: 'cmd', args: ['/c', 'start', '', url] }];
|
|
11
|
+
}
|
|
12
|
+
return [
|
|
13
|
+
{ command: 'xdg-open', args: [url] },
|
|
14
|
+
{ command: 'gio', args: ['open', url] },
|
|
15
|
+
];
|
|
16
|
+
}
|
|
17
|
+
export function openUrlInBrowser(url) {
|
|
18
|
+
// Avoid launching browser commands in non-interactive contexts (CI/pipes).
|
|
19
|
+
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
for (const candidate of getOpenCommands(url)) {
|
|
23
|
+
try {
|
|
24
|
+
const result = spawnSync(candidate.command, candidate.args, {
|
|
25
|
+
stdio: 'ignore',
|
|
26
|
+
windowsHide: true,
|
|
27
|
+
});
|
|
28
|
+
if (!result.error && result.status === 0) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// Keep trying fallback commands. This helper must never crash the CLI.
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function subscribeInteractive(): Promise<void>;
|
|
2
|
+
export declare function subscribeNonInteractive(email?: string, plan?: string): void;
|
|
3
|
+
export declare function unsubscribe(): Promise<void>;
|
|
4
|
+
export declare function showUsage(): Promise<void>;
|
|
5
|
+
//# sourceMappingURL=subscription.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscription.d.ts","sourceRoot":"","sources":["../src/subscription.ts"],"names":[],"mappings":"AAsDA,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CA8H1D;AAED,wBAAgB,uBAAuB,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CA6B3E;AAID,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAgCjD;AAID,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CA8C/C"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
// Subscription management for egaki.
|
|
2
|
+
// Handles the subscribe, unsubscribe, and usage flows.
|
|
3
|
+
// Communicates with the egaki gateway API at egaki.org.
|
|
4
|
+
import pc from 'picocolors';
|
|
5
|
+
import { intro, outro, text, select, confirm, isCancel, cancel, log, note, spinner, } from '@clack/prompts';
|
|
6
|
+
import { EGAKI_GATEWAY_URL, saveProviderKey, getProviderKey } from './credentials.js';
|
|
7
|
+
import { openUrlInBrowser } from './open-browser.js';
|
|
8
|
+
// Strip /vN/ai suffix to get the base URL for non-proxy endpoints (/buy, /api/*)
|
|
9
|
+
const GATEWAY_BASE = EGAKI_GATEWAY_URL.replace(/\/v\d+\/ai$/, '');
|
|
10
|
+
// ── Subscribe ─────────────────────────────────────────────────────────────
|
|
11
|
+
export async function subscribeInteractive() {
|
|
12
|
+
intro(pc.bold('egaki subscribe'));
|
|
13
|
+
// Fetch available plans
|
|
14
|
+
const s = spinner();
|
|
15
|
+
s.start('Fetching plans...');
|
|
16
|
+
let plans;
|
|
17
|
+
try {
|
|
18
|
+
const res = await fetch(`${GATEWAY_BASE}/api/plans`);
|
|
19
|
+
if (!res.ok)
|
|
20
|
+
throw new Error(`HTTP ${res.status}`);
|
|
21
|
+
const data = (await res.json());
|
|
22
|
+
plans = data.plans;
|
|
23
|
+
s.stop('Plans loaded');
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
s.stop('Failed to fetch plans');
|
|
27
|
+
log.error('Could not connect to egaki.org. Check your internet connection.');
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
// Select plan
|
|
31
|
+
const planId = await select({
|
|
32
|
+
message: 'Choose a plan',
|
|
33
|
+
options: plans.map((p) => ({
|
|
34
|
+
value: p.id,
|
|
35
|
+
label: `${p.name} — ${p.priceFormatted}`,
|
|
36
|
+
hint: `$${p.spendingCap} spending cap, all models`,
|
|
37
|
+
})),
|
|
38
|
+
});
|
|
39
|
+
if (isCancel(planId)) {
|
|
40
|
+
cancel('Subscribe cancelled.');
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
// Optional email prefill for checkout
|
|
44
|
+
const prefillEmail = await confirm({
|
|
45
|
+
message: 'Prefill your email in checkout URL? (optional)',
|
|
46
|
+
});
|
|
47
|
+
if (isCancel(prefillEmail)) {
|
|
48
|
+
cancel('Subscribe cancelled.');
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}
|
|
51
|
+
let email;
|
|
52
|
+
if (prefillEmail) {
|
|
53
|
+
const enteredEmail = await text({
|
|
54
|
+
message: 'Email for the subscription',
|
|
55
|
+
placeholder: 'you@example.com',
|
|
56
|
+
validate: (value) => {
|
|
57
|
+
if (!value || !value.includes('@')) {
|
|
58
|
+
return 'Please enter a valid email';
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
if (isCancel(enteredEmail)) {
|
|
63
|
+
cancel('Subscribe cancelled.');
|
|
64
|
+
process.exit(0);
|
|
65
|
+
}
|
|
66
|
+
email = enteredEmail;
|
|
67
|
+
}
|
|
68
|
+
// Show checkout URL
|
|
69
|
+
const params = new URLSearchParams({ plan: planId });
|
|
70
|
+
if (email) {
|
|
71
|
+
params.set('email', email);
|
|
72
|
+
}
|
|
73
|
+
const checkoutUrl = `${GATEWAY_BASE}/buy?${params.toString()}`;
|
|
74
|
+
log.info('');
|
|
75
|
+
note([
|
|
76
|
+
`Open this URL to complete payment:`,
|
|
77
|
+
'',
|
|
78
|
+
pc.cyan(checkoutUrl),
|
|
79
|
+
'',
|
|
80
|
+
`After payment, you'll receive your API key via email.`,
|
|
81
|
+
`Then run:`,
|
|
82
|
+
'',
|
|
83
|
+
` ${pc.cyan('egaki login --provider egaki --key egaki_...')}`,
|
|
84
|
+
].join('\n'), 'Checkout');
|
|
85
|
+
const opened = openUrlInBrowser(checkoutUrl);
|
|
86
|
+
if (opened) {
|
|
87
|
+
log.success('Opened checkout URL in your browser');
|
|
88
|
+
}
|
|
89
|
+
else if (process.stdout.isTTY && process.stdin.isTTY) {
|
|
90
|
+
log.info('Could not auto-open a browser. Open the URL above manually.');
|
|
91
|
+
}
|
|
92
|
+
// Ask if they want to enter the key now
|
|
93
|
+
const hasKey = await confirm({
|
|
94
|
+
message: 'Do you already have your API key?',
|
|
95
|
+
});
|
|
96
|
+
if (isCancel(hasKey)) {
|
|
97
|
+
outro('You can enter your key later with: egaki login --provider egaki --key <key>');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (hasKey) {
|
|
101
|
+
const key = await text({
|
|
102
|
+
message: 'Paste your egaki API key',
|
|
103
|
+
placeholder: 'egaki_...',
|
|
104
|
+
validate: (value) => {
|
|
105
|
+
if (!value || !value.startsWith('egaki_')) {
|
|
106
|
+
return 'API key should start with egaki_';
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
if (isCancel(key)) {
|
|
111
|
+
outro('You can enter your key later with: egaki login --provider egaki --key <key>');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
saveProviderKey('egaki', key.trim());
|
|
115
|
+
log.success('API key saved');
|
|
116
|
+
outro('You can now generate images with any model: egaki image "your prompt"');
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
outro('After payment, run: egaki login --provider egaki --key <your-key>');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
export function subscribeNonInteractive(email, plan) {
|
|
123
|
+
const emailParam = email ? `&email=${encodeURIComponent(email)}` : '';
|
|
124
|
+
if (plan) {
|
|
125
|
+
// Specific plan requested
|
|
126
|
+
const checkoutUrl = `${GATEWAY_BASE}/buy?plan=${plan}${emailParam}`;
|
|
127
|
+
console.log(pc.bold('Egaki Subscription'));
|
|
128
|
+
console.log('');
|
|
129
|
+
console.log('Open this URL to complete payment:');
|
|
130
|
+
console.log('');
|
|
131
|
+
console.log(pc.cyan(checkoutUrl));
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
// No plan specified — show all options
|
|
135
|
+
console.log(pc.bold('Egaki Subscription'));
|
|
136
|
+
console.log('');
|
|
137
|
+
console.log('Choose a plan:');
|
|
138
|
+
console.log('');
|
|
139
|
+
console.log(` ${pc.bold('Plus')} $29/mo ${pc.cyan(`${GATEWAY_BASE}/buy?plan=plus${emailParam}`)}`);
|
|
140
|
+
console.log(` ${pc.bold('Pro')} $99/mo ${pc.cyan(`${GATEWAY_BASE}/buy?plan=pro${emailParam}`)}`);
|
|
141
|
+
}
|
|
142
|
+
console.log('');
|
|
143
|
+
console.log('After payment, your API key is shown on the success page.');
|
|
144
|
+
console.log('If an email was provided to Stripe checkout, a copy is also sent by email.');
|
|
145
|
+
console.log(`Then run: ${pc.cyan('egaki login --provider egaki --key egaki_...')}`);
|
|
146
|
+
console.log('');
|
|
147
|
+
console.log('Tip: egaki supports both BYOK and subscription mode.');
|
|
148
|
+
console.log('BYOK = add provider keys (google/openai/replicate/fal) with egaki login.');
|
|
149
|
+
console.log('Subscription = one egaki key to access all models without per-provider key setup.');
|
|
150
|
+
}
|
|
151
|
+
// ── Unsubscribe ───────────────────────────────────────────────────────────
|
|
152
|
+
export async function unsubscribe() {
|
|
153
|
+
const apiKey = getProviderKey('egaki') || process.env['EGAKI_API_KEY'];
|
|
154
|
+
if (!apiKey) {
|
|
155
|
+
console.error(pc.red('No egaki API key found.'));
|
|
156
|
+
console.error(`If you have a key, set it first: ${pc.cyan('egaki login --provider egaki --key <key>')}`);
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
console.error(pc.dim('Canceling subscription...'));
|
|
160
|
+
try {
|
|
161
|
+
const res = await fetch(`${GATEWAY_BASE}/api/cancel`, {
|
|
162
|
+
method: 'POST',
|
|
163
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
164
|
+
});
|
|
165
|
+
const data = (await res.json());
|
|
166
|
+
if (!res.ok) {
|
|
167
|
+
console.error(pc.red(data.error || 'Failed to cancel subscription'));
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
console.log(pc.green(data.message || 'Subscription canceled.'));
|
|
171
|
+
console.log(pc.dim('You can resubscribe anytime with: egaki subscribe'));
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
console.error(pc.red('Could not connect to egaki.org. Check your internet connection.'));
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// ── Usage ─────────────────────────────────────────────────────────────────
|
|
179
|
+
export async function showUsage() {
|
|
180
|
+
const apiKey = getProviderKey('egaki') || process.env['EGAKI_API_KEY'];
|
|
181
|
+
if (!apiKey) {
|
|
182
|
+
console.error(pc.red('No egaki API key found.'));
|
|
183
|
+
console.error(`Set your key first: ${pc.cyan('egaki login --provider egaki --key <key>')}`);
|
|
184
|
+
console.error(`Or subscribe: ${pc.cyan('egaki subscribe')}`);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
const res = await fetch(`${GATEWAY_BASE}/api/usage`, {
|
|
189
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
190
|
+
});
|
|
191
|
+
if (!res.ok) {
|
|
192
|
+
const data = (await res.json());
|
|
193
|
+
console.error(pc.red(data.error || 'Failed to fetch usage'));
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
const usage = (await res.json());
|
|
197
|
+
const pct = usage.spendingCap > 0
|
|
198
|
+
? Math.round((usage.dollarsUsed / usage.spendingCap) * 100)
|
|
199
|
+
: 0;
|
|
200
|
+
const remaining = Math.max(0, usage.spendingCap - usage.dollarsUsed);
|
|
201
|
+
console.log(pc.bold(`Egaki ${usage.planName} Plan\n`));
|
|
202
|
+
console.log(` Status: ${usage.status === 'active' ? pc.green('active') : pc.red(usage.status)}`);
|
|
203
|
+
console.log(` Spent: $${usage.dollarsUsed.toFixed(2)} / $${usage.spendingCap.toFixed(2)} (${pct}%)`);
|
|
204
|
+
console.log(` Remaining: $${remaining.toFixed(2)}`);
|
|
205
|
+
console.log(` Period: started ${new Date(usage.periodStart).toLocaleDateString()}`);
|
|
206
|
+
if (pct >= 90) {
|
|
207
|
+
console.log('');
|
|
208
|
+
console.log(pc.yellow(' Running low on budget! Consider upgrading your plan.'));
|
|
209
|
+
console.log(pc.dim(` Visit: https://egaki.org/buy`));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
console.error(pc.red('Could not connect to egaki.org. Check your internet connection.'));
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export type VideoDurationPricingTier = {
|
|
2
|
+
/** USD per second for this variant */
|
|
3
|
+
costPerSecond: number;
|
|
4
|
+
/** Optional resolution discriminator (e.g. 720p, 1080p, 4k) */
|
|
5
|
+
resolution?: string;
|
|
6
|
+
/** Optional mode discriminator (e.g. std, pro) */
|
|
7
|
+
mode?: string;
|
|
8
|
+
/** Optional audio discriminator */
|
|
9
|
+
audio?: boolean;
|
|
10
|
+
};
|
|
11
|
+
export type PerVideoSecondCost = {
|
|
12
|
+
type: 'per-video-second';
|
|
13
|
+
/** Fallback duration when request does not specify one (seconds) */
|
|
14
|
+
defaultDurationSec: number;
|
|
15
|
+
tiers: VideoDurationPricingTier[];
|
|
16
|
+
};
|
|
17
|
+
export type UnknownVideoCost = {
|
|
18
|
+
type: 'unknown';
|
|
19
|
+
};
|
|
20
|
+
export type VideoModelCost = PerVideoSecondCost | UnknownVideoCost;
|
|
21
|
+
export type VideoModelFeatures = {
|
|
22
|
+
/** Supports text prompt to video generation */
|
|
23
|
+
textToVideo: boolean;
|
|
24
|
+
/** Supports image-to-video prompt object ({ image, text }) */
|
|
25
|
+
imageToVideo: boolean;
|
|
26
|
+
/** Optional capabilities exposed by provider */
|
|
27
|
+
capabilities: Array<'t2v' | 'i2v' | 'r2v' | 'motion-control' | 'editing'>;
|
|
28
|
+
/** Optional supported aspect ratios */
|
|
29
|
+
aspectRatios?: string[];
|
|
30
|
+
/** Optional supported resolutions */
|
|
31
|
+
resolutions?: string[];
|
|
32
|
+
/** Optional duration range in seconds */
|
|
33
|
+
durationRangeSec?: {
|
|
34
|
+
min: number;
|
|
35
|
+
max: number;
|
|
36
|
+
};
|
|
37
|
+
seed: boolean;
|
|
38
|
+
multipleVideos: boolean;
|
|
39
|
+
};
|
|
40
|
+
export type VideoModelEntry = {
|
|
41
|
+
id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
description?: string;
|
|
44
|
+
provider: string;
|
|
45
|
+
strategy: 'video';
|
|
46
|
+
released: string;
|
|
47
|
+
cost: VideoModelCost;
|
|
48
|
+
features: VideoModelFeatures;
|
|
49
|
+
};
|
|
50
|
+
export declare const VIDEO_CATALOG: VideoModelEntry[];
|
|
51
|
+
export declare function findVideoModel(id: string): VideoModelEntry | undefined;
|
|
52
|
+
//# sourceMappingURL=video-model-catalog.d.ts.map
|
|
@@ -0,0 +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"}
|