egaki 0.5.0 → 0.6.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 +57 -0
- package/README.md +107 -3
- package/dist/cli.js +459 -124
- package/dist/credentials.d.ts +11 -2
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +68 -5
- package/dist/login.d.ts.map +1 -1
- package/dist/login.js +45 -4
- package/dist/model-catalog.d.ts +96 -3
- package/dist/model-catalog.d.ts.map +1 -1
- package/dist/model-catalog.js +427 -3
- package/dist/models.d.ts +4 -4
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +162 -96
- package/dist/video-model-catalog.d.ts +1 -51
- package/dist/video-model-catalog.d.ts.map +1 -1
- package/dist/video-model-catalog.js +5 -306
- package/dist/xai-auth.d.ts +29 -0
- package/dist/xai-auth.d.ts.map +1 -0
- package/dist/xai-auth.js +325 -0
- package/package.json +5 -4
- package/src/cli.ts +621 -133
- package/src/credentials.ts +70 -7
- package/src/login.ts +48 -3
- package/src/model-catalog.ts +537 -7
- package/src/models.ts +239 -119
- package/src/video-model-catalog.ts +14 -363
- package/src/xai-auth.ts +396 -0
package/src/credentials.ts
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
// Reads and writes API keys from ~/.config/egaki/credentials.json.
|
|
3
3
|
// Keys are stored per-provider and injected as env vars at runtime.
|
|
4
4
|
//
|
|
5
|
-
// Most providers store a plain API key string.
|
|
6
|
-
//
|
|
5
|
+
// Most providers store a plain API key string. OAuth providers store structured
|
|
6
|
+
// token objects for their custom backend flows.
|
|
7
7
|
import fs from 'node:fs'
|
|
8
8
|
import path from 'node:path'
|
|
9
9
|
import os from 'node:os'
|
|
10
10
|
import type { ChatGptAuth } from './chatgpt-auth.js'
|
|
11
|
+
import type { XaiAuth } from './xai-auth.js'
|
|
11
12
|
|
|
12
13
|
export type ProviderInfo = {
|
|
13
14
|
envVar: string
|
|
@@ -57,6 +58,17 @@ export const PROVIDERS: Record<string, ProviderInfo> = {
|
|
|
57
58
|
label: 'fal.ai (Flux, SDXL, Recraft)',
|
|
58
59
|
hint: 'Get your key at https://fal.ai/dashboard/keys',
|
|
59
60
|
},
|
|
61
|
+
xai: {
|
|
62
|
+
envVar: 'XAI_API_KEY',
|
|
63
|
+
label: 'xAI (API key)',
|
|
64
|
+
hint: 'Get your key at https://console.x.ai/team/default/api-keys',
|
|
65
|
+
},
|
|
66
|
+
'xai-oauth': {
|
|
67
|
+
envVar: 'XAI_API_KEY',
|
|
68
|
+
label: 'xAI Grok Build (use your subscription)',
|
|
69
|
+
hint: 'Sign in with your xAI / Grok account via browser',
|
|
70
|
+
oauth: true,
|
|
71
|
+
},
|
|
60
72
|
}
|
|
61
73
|
|
|
62
74
|
export const EGAKI_GATEWAY_URL = 'https://egaki.org/v3/ai'
|
|
@@ -72,8 +84,8 @@ function getCredentialsPath(): string {
|
|
|
72
84
|
return path.join(getConfigDir(), 'credentials.json')
|
|
73
85
|
}
|
|
74
86
|
|
|
75
|
-
/** A credential value is
|
|
76
|
-
export type CredentialValue = string | ChatGptAuth
|
|
87
|
+
/** A credential value is a plain API key string, a ChatGPT OAuth object, or an xAI OAuth object. */
|
|
88
|
+
export type CredentialValue = string | ChatGptAuth | XaiAuth
|
|
77
89
|
export type Credentials = Record<string, CredentialValue>
|
|
78
90
|
|
|
79
91
|
export function readCredentials(): Credentials {
|
|
@@ -115,6 +127,19 @@ export function getChatGptAuth(): ChatGptAuth | undefined {
|
|
|
115
127
|
return undefined
|
|
116
128
|
}
|
|
117
129
|
|
|
130
|
+
export function saveXaiAuth(auth: XaiAuth): void {
|
|
131
|
+
const creds = readCredentials()
|
|
132
|
+
creds['xai-oauth'] = auth
|
|
133
|
+
writeCredentials(creds)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function getXaiAuth(): XaiAuth | undefined {
|
|
137
|
+
const creds = readCredentials()
|
|
138
|
+
const val = creds['xai-oauth']
|
|
139
|
+
if (!val || typeof val !== 'object') return undefined
|
|
140
|
+
return val
|
|
141
|
+
}
|
|
142
|
+
|
|
118
143
|
export function removeProviderKey(provider: string): void {
|
|
119
144
|
const creds = readCredentials()
|
|
120
145
|
delete creds[provider]
|
|
@@ -127,17 +152,26 @@ export function getProviderKey(provider: string): string | undefined {
|
|
|
127
152
|
return typeof val === 'string' ? val : undefined
|
|
128
153
|
}
|
|
129
154
|
|
|
130
|
-
// Inject all stored non-
|
|
155
|
+
// Inject all stored non-OAuth credentials as env vars so the AI SDK
|
|
131
156
|
// providers pick them up automatically. Env vars already set by the user take
|
|
132
157
|
// precedence (we don't overwrite them).
|
|
158
|
+
//
|
|
159
|
+
// Special case: if xAI OAuth tokens exist, skip injecting the stored xai API
|
|
160
|
+
// key so OAuth takes priority. Only an explicit XAI_API_KEY env var (set by
|
|
161
|
+
// the user before egaki runs) will override OAuth.
|
|
133
162
|
export function injectCredentialsToEnv(): void {
|
|
134
163
|
const creds = readCredentials()
|
|
164
|
+
const hasXaiOAuth = Boolean(getXaiAuth())
|
|
135
165
|
for (const [provider, value] of Object.entries(creds)) {
|
|
136
166
|
const info = PROVIDERS[provider]
|
|
137
167
|
if (!info) continue
|
|
138
168
|
|
|
139
|
-
// Skip
|
|
169
|
+
// Skip OAuth entries — handled separately via custom fetch
|
|
140
170
|
if (provider === 'chatgpt') continue
|
|
171
|
+
if (provider === 'xai-oauth') continue
|
|
172
|
+
|
|
173
|
+
// Skip stored xai key when OAuth tokens exist (OAuth takes priority)
|
|
174
|
+
if (provider === 'xai' && hasXaiOAuth && !process.env[info.envVar]) continue
|
|
141
175
|
|
|
142
176
|
if (typeof value === 'string' && !process.env[info.envVar]) {
|
|
143
177
|
process.env[info.envVar] = value
|
|
@@ -153,6 +187,16 @@ export function shouldUseChatGptBackend(): boolean {
|
|
|
153
187
|
return !process.env['OPENAI_API_KEY'] && Boolean(getChatGptAuth())
|
|
154
188
|
}
|
|
155
189
|
|
|
190
|
+
/**
|
|
191
|
+
* Returns true when xAI models should use the OAuth flow instead of a direct
|
|
192
|
+
* XAI_API_KEY. Priority: explicit env var > xai-oauth tokens > stored xai key.
|
|
193
|
+
* Only an explicit XAI_API_KEY (set before egaki runs) overrides OAuth.
|
|
194
|
+
*/
|
|
195
|
+
export function shouldUseXaiOAuth(): boolean {
|
|
196
|
+
if (process.env['XAI_API_KEY']) return false
|
|
197
|
+
return Boolean(getXaiAuth())
|
|
198
|
+
}
|
|
199
|
+
|
|
156
200
|
// Check if a provider has a key available (from env or stored credentials).
|
|
157
201
|
// Returns the source of the key for display purposes.
|
|
158
202
|
export function getKeyStatus(provider: string): {
|
|
@@ -164,7 +208,7 @@ export function getKeyStatus(provider: string): {
|
|
|
164
208
|
return { available: false, source: 'none' }
|
|
165
209
|
}
|
|
166
210
|
|
|
167
|
-
//
|
|
211
|
+
// OAuth providers are special cases
|
|
168
212
|
if (provider === 'chatgpt') {
|
|
169
213
|
const auth = getChatGptAuth()
|
|
170
214
|
if (auth) {
|
|
@@ -173,6 +217,25 @@ export function getKeyStatus(provider: string): {
|
|
|
173
217
|
return { available: false, source: 'none' }
|
|
174
218
|
}
|
|
175
219
|
|
|
220
|
+
if (provider === 'xai-oauth') {
|
|
221
|
+
const auth = getXaiAuth()
|
|
222
|
+
if (auth) {
|
|
223
|
+
return { available: true, source: 'oauth' }
|
|
224
|
+
}
|
|
225
|
+
return { available: false, source: 'none' }
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// For xai provider, report the same priority used by generation:
|
|
229
|
+
// explicit env var > xai-oauth tokens > stored xai key.
|
|
230
|
+
if (provider === 'xai') {
|
|
231
|
+
if (process.env[info.envVar]) return { available: true, source: 'env' }
|
|
232
|
+
const oauthAuth = getXaiAuth()
|
|
233
|
+
if (oauthAuth) return { available: true, source: 'oauth' }
|
|
234
|
+
const stored = getProviderKey(provider)
|
|
235
|
+
if (stored) return { available: true, source: 'stored' }
|
|
236
|
+
return { available: false, source: 'none' }
|
|
237
|
+
}
|
|
238
|
+
|
|
176
239
|
if (process.env[info.envVar]) {
|
|
177
240
|
return { available: true, source: 'env' }
|
|
178
241
|
}
|
package/src/login.ts
CHANGED
|
@@ -20,8 +20,11 @@ import {
|
|
|
20
20
|
getKeyStatus,
|
|
21
21
|
getChatGptAuth,
|
|
22
22
|
saveChatGptAuth,
|
|
23
|
+
getXaiAuth,
|
|
24
|
+
saveXaiAuth,
|
|
23
25
|
} from './credentials.js'
|
|
24
26
|
import { chatGptOAuthLogin, extractPlanType } from './chatgpt-auth.js'
|
|
27
|
+
import { xaiOAuthLogin } from './xai-auth.js'
|
|
25
28
|
|
|
26
29
|
export async function loginInteractive(): Promise<void> {
|
|
27
30
|
intro(pc.bold('egaki login'))
|
|
@@ -36,9 +39,17 @@ export async function loginInteractive(): Promise<void> {
|
|
|
36
39
|
return pc.green('(saved)')
|
|
37
40
|
}
|
|
38
41
|
if (status.source === 'oauth') {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
if (key === 'chatgpt') {
|
|
43
|
+
const auth = getChatGptAuth()
|
|
44
|
+
const email = auth?.email
|
|
45
|
+
return pc.green(`(signed in${email ? ` as ${email}` : ''})`)
|
|
46
|
+
}
|
|
47
|
+
if (key === 'xai-oauth') {
|
|
48
|
+
const auth = getXaiAuth()
|
|
49
|
+
const email = auth?.email
|
|
50
|
+
return pc.green(`(signed in${email ? ` as ${email}` : ''})`)
|
|
51
|
+
}
|
|
52
|
+
return pc.green('(signed in)')
|
|
42
53
|
}
|
|
43
54
|
return pc.dim('(not configured)')
|
|
44
55
|
})()
|
|
@@ -77,6 +88,14 @@ export async function loginInteractive(): Promise<void> {
|
|
|
77
88
|
return
|
|
78
89
|
}
|
|
79
90
|
|
|
91
|
+
// xAI Grok Build uses browser OAuth
|
|
92
|
+
if (provider === 'xai-oauth') {
|
|
93
|
+
const auth = await xaiOAuthLogin()
|
|
94
|
+
saveXaiAuth(auth)
|
|
95
|
+
outro('Done — xAI OAuth saved')
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
80
99
|
log.info(
|
|
81
100
|
`${pc.dim('Env var:')} ${info.envVar} ${pc.dim('(also used by the CLI when set)')}`,
|
|
82
101
|
)
|
|
@@ -131,6 +150,14 @@ export async function loginNonInteractive({
|
|
|
131
150
|
return
|
|
132
151
|
}
|
|
133
152
|
|
|
153
|
+
// xAI Grok Build uses browser OAuth — key flag is not needed
|
|
154
|
+
if (provider === 'xai-oauth') {
|
|
155
|
+
const auth = await xaiOAuthLogin()
|
|
156
|
+
saveXaiAuth(auth)
|
|
157
|
+
console.log(pc.green('xAI OAuth saved'))
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
|
|
134
161
|
if (!key || key.trim().length === 0) {
|
|
135
162
|
console.error(pc.red('API key cannot be empty'))
|
|
136
163
|
process.exit(1)
|
|
@@ -165,6 +192,24 @@ export function showLoginStatus(): void {
|
|
|
165
192
|
continue
|
|
166
193
|
}
|
|
167
194
|
|
|
195
|
+
if (key === 'xai-oauth') {
|
|
196
|
+
const auth = getXaiAuth()
|
|
197
|
+
if (auth) {
|
|
198
|
+
const email = auth.email ?? 'unknown'
|
|
199
|
+
const expired = auth.expires < Date.now()
|
|
200
|
+
const expiryLabel = expired
|
|
201
|
+
? pc.yellow('(token expired, will auto-refresh)')
|
|
202
|
+
: pc.dim('(token valid)')
|
|
203
|
+
console.log(`${pc.green('*')} ${info.label} ${pc.green('(signed in)')}`)
|
|
204
|
+
console.log(pc.dim(` account: ${email}`))
|
|
205
|
+
console.log(` ${expiryLabel}`)
|
|
206
|
+
} else {
|
|
207
|
+
console.log(`${pc.dim('-')} ${info.label} ${pc.dim('(not signed in)')}`)
|
|
208
|
+
console.log(pc.dim(` run: egaki login → select xAI Grok Build`))
|
|
209
|
+
}
|
|
210
|
+
continue
|
|
211
|
+
}
|
|
212
|
+
|
|
168
213
|
const icon = status.available ? pc.green('*') : pc.dim('-')
|
|
169
214
|
const source = (() => {
|
|
170
215
|
if (status.source === 'env') {
|