@agi-cli/sdk 0.1.149 → 0.1.150
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/package.json +1 -1
- package/src/auth/src/index.ts +12 -0
- package/src/auth/src/oauth.ts +62 -0
- package/src/auth/src/openai-oauth.ts +115 -4
- package/src/auth/src/wallet.ts +51 -0
- package/src/config/src/manager.ts +40 -0
- package/src/index.ts +13 -0
- package/src/types/src/config.ts +1 -0
package/package.json
CHANGED
package/src/auth/src/index.ts
CHANGED
|
@@ -67,6 +67,8 @@ export {
|
|
|
67
67
|
refreshToken,
|
|
68
68
|
openAuthUrl,
|
|
69
69
|
createApiKey,
|
|
70
|
+
authorizeWeb,
|
|
71
|
+
exchangeWeb,
|
|
70
72
|
} from './oauth.ts';
|
|
71
73
|
|
|
72
74
|
export {
|
|
@@ -75,5 +77,15 @@ export {
|
|
|
75
77
|
refreshOpenAIToken,
|
|
76
78
|
openOpenAIAuthUrl,
|
|
77
79
|
obtainOpenAIApiKey,
|
|
80
|
+
authorizeOpenAIWeb,
|
|
81
|
+
exchangeOpenAIWeb,
|
|
78
82
|
type OpenAIOAuthResult,
|
|
79
83
|
} from './openai-oauth.ts';
|
|
84
|
+
|
|
85
|
+
export {
|
|
86
|
+
generateWallet,
|
|
87
|
+
importWallet,
|
|
88
|
+
getSetuWallet,
|
|
89
|
+
ensureSetuWallet,
|
|
90
|
+
type WalletInfo,
|
|
91
|
+
} from './wallet.ts';
|
package/src/auth/src/oauth.ts
CHANGED
|
@@ -170,3 +170,65 @@ export async function createApiKey(accessToken: string) {
|
|
|
170
170
|
const json = (await result.json()) as { raw_key: string };
|
|
171
171
|
return json.raw_key;
|
|
172
172
|
}
|
|
173
|
+
|
|
174
|
+
export function authorizeWeb(mode: Mode, redirectUri: string) {
|
|
175
|
+
const pkce = generatePKCE();
|
|
176
|
+
|
|
177
|
+
const url = new URL(
|
|
178
|
+
`https://${mode === 'console' ? 'console.anthropic.com' : 'claude.ai'}/oauth/authorize`,
|
|
179
|
+
);
|
|
180
|
+
url.searchParams.set('code', 'true');
|
|
181
|
+
url.searchParams.set('client_id', CLIENT_ID);
|
|
182
|
+
url.searchParams.set('response_type', 'code');
|
|
183
|
+
url.searchParams.set('redirect_uri', redirectUri);
|
|
184
|
+
url.searchParams.set(
|
|
185
|
+
'scope',
|
|
186
|
+
'org:create_api_key user:profile user:inference',
|
|
187
|
+
);
|
|
188
|
+
url.searchParams.set('code_challenge', pkce.challenge);
|
|
189
|
+
url.searchParams.set('code_challenge_method', 'S256');
|
|
190
|
+
url.searchParams.set('state', pkce.verifier);
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
url: url.toString(),
|
|
194
|
+
verifier: pkce.verifier,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export async function exchangeWeb(
|
|
199
|
+
code: string,
|
|
200
|
+
verifier: string,
|
|
201
|
+
redirectUri: string,
|
|
202
|
+
) {
|
|
203
|
+
const splits = code.split('#');
|
|
204
|
+
const result = await fetch('https://console.anthropic.com/v1/oauth/token', {
|
|
205
|
+
method: 'POST',
|
|
206
|
+
headers: {
|
|
207
|
+
'Content-Type': 'application/json',
|
|
208
|
+
},
|
|
209
|
+
body: JSON.stringify({
|
|
210
|
+
code: splits[0],
|
|
211
|
+
state: splits[1],
|
|
212
|
+
grant_type: 'authorization_code',
|
|
213
|
+
client_id: CLIENT_ID,
|
|
214
|
+
redirect_uri: redirectUri,
|
|
215
|
+
code_verifier: verifier,
|
|
216
|
+
}),
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
if (!result.ok) {
|
|
220
|
+
const error = await result.text();
|
|
221
|
+
throw new Error(`Token exchange failed: ${error}`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const json = (await result.json()) as {
|
|
225
|
+
refresh_token: string;
|
|
226
|
+
access_token: string;
|
|
227
|
+
expires_in: number;
|
|
228
|
+
};
|
|
229
|
+
return {
|
|
230
|
+
refresh: json.refresh_token,
|
|
231
|
+
access: json.access_token,
|
|
232
|
+
expires: Date.now() + json.expires_in * 1000,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
@@ -123,10 +123,46 @@ export async function authorizeOpenAI(): Promise<OpenAIOAuthResult> {
|
|
|
123
123
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
124
124
|
res.end(`
|
|
125
125
|
<html>
|
|
126
|
-
<head
|
|
127
|
-
|
|
128
|
-
<
|
|
129
|
-
|
|
126
|
+
<head>
|
|
127
|
+
<title>AGI - Authentication Successful</title>
|
|
128
|
+
<style>
|
|
129
|
+
body {
|
|
130
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
131
|
+
display: flex;
|
|
132
|
+
justify-content: center;
|
|
133
|
+
align-items: center;
|
|
134
|
+
height: 100vh;
|
|
135
|
+
margin: 0;
|
|
136
|
+
background: linear-gradient(135deg, #10a37f 0%, #1a7f5a 100%);
|
|
137
|
+
color: white;
|
|
138
|
+
}
|
|
139
|
+
.container {
|
|
140
|
+
text-align: center;
|
|
141
|
+
padding: 2rem;
|
|
142
|
+
background: rgba(255,255,255,0.1);
|
|
143
|
+
border-radius: 16px;
|
|
144
|
+
backdrop-filter: blur(10px);
|
|
145
|
+
}
|
|
146
|
+
.checkmark { font-size: 4rem; margin-bottom: 1rem; }
|
|
147
|
+
h1 { margin: 0 0 0.5rem 0; }
|
|
148
|
+
p { margin: 0; opacity: 0.9; }
|
|
149
|
+
</style>
|
|
150
|
+
</head>
|
|
151
|
+
<body>
|
|
152
|
+
<div class="container">
|
|
153
|
+
<div class="checkmark">✓</div>
|
|
154
|
+
<h1>Connected!</h1>
|
|
155
|
+
<p>You can close this window.</p>
|
|
156
|
+
</div>
|
|
157
|
+
<script>
|
|
158
|
+
// Delay to allow server to complete token exchange
|
|
159
|
+
setTimeout(() => {
|
|
160
|
+
if (window.opener) {
|
|
161
|
+
window.opener.postMessage({ type: 'oauth-success', provider: 'openai' }, '*');
|
|
162
|
+
}
|
|
163
|
+
setTimeout(() => window.close(), 500);
|
|
164
|
+
}, 1500);
|
|
165
|
+
</script>
|
|
130
166
|
</body>
|
|
131
167
|
</html>
|
|
132
168
|
`);
|
|
@@ -281,3 +317,78 @@ export async function obtainOpenAIApiKey(idToken: string): Promise<string> {
|
|
|
281
317
|
|
|
282
318
|
return json.access_token;
|
|
283
319
|
}
|
|
320
|
+
|
|
321
|
+
export function authorizeOpenAIWeb(redirectUri: string): {
|
|
322
|
+
url: string;
|
|
323
|
+
verifier: string;
|
|
324
|
+
state: string;
|
|
325
|
+
} {
|
|
326
|
+
const pkce = generatePKCE();
|
|
327
|
+
const state = generateState();
|
|
328
|
+
|
|
329
|
+
const params = new URLSearchParams({
|
|
330
|
+
response_type: 'code',
|
|
331
|
+
client_id: OPENAI_CLIENT_ID,
|
|
332
|
+
redirect_uri: redirectUri,
|
|
333
|
+
scope: 'openid profile email offline_access',
|
|
334
|
+
code_challenge: pkce.challenge,
|
|
335
|
+
code_challenge_method: 'S256',
|
|
336
|
+
id_token_add_organizations: 'true',
|
|
337
|
+
codex_cli_simplified_flow: 'true',
|
|
338
|
+
state: state,
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
return {
|
|
342
|
+
url: `${OPENAI_ISSUER}/oauth/authorize?${params.toString()}`,
|
|
343
|
+
verifier: pkce.verifier,
|
|
344
|
+
state,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export async function exchangeOpenAIWeb(
|
|
349
|
+
code: string,
|
|
350
|
+
verifier: string,
|
|
351
|
+
redirectUri: string,
|
|
352
|
+
) {
|
|
353
|
+
const response = await fetch(`${OPENAI_ISSUER}/oauth/token`, {
|
|
354
|
+
method: 'POST',
|
|
355
|
+
headers: {
|
|
356
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
357
|
+
},
|
|
358
|
+
body: new URLSearchParams({
|
|
359
|
+
grant_type: 'authorization_code',
|
|
360
|
+
code,
|
|
361
|
+
redirect_uri: redirectUri,
|
|
362
|
+
client_id: OPENAI_CLIENT_ID,
|
|
363
|
+
code_verifier: verifier,
|
|
364
|
+
}).toString(),
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
if (!response.ok) {
|
|
368
|
+
const error = await response.text();
|
|
369
|
+
throw new Error(`Token exchange failed: ${error}`);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const json = (await response.json()) as {
|
|
373
|
+
id_token: string;
|
|
374
|
+
access_token: string;
|
|
375
|
+
refresh_token: string;
|
|
376
|
+
expires_in?: number;
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
let accountId: string | undefined;
|
|
380
|
+
try {
|
|
381
|
+
const payload = JSON.parse(
|
|
382
|
+
Buffer.from(json.access_token.split('.')[1], 'base64').toString(),
|
|
383
|
+
);
|
|
384
|
+
accountId = payload['https://api.openai.com/auth']?.chatgpt_account_id;
|
|
385
|
+
} catch {}
|
|
386
|
+
|
|
387
|
+
return {
|
|
388
|
+
idToken: json.id_token,
|
|
389
|
+
access: json.access_token,
|
|
390
|
+
refresh: json.refresh_token,
|
|
391
|
+
expires: Date.now() + (json.expires_in || 3600) * 1000,
|
|
392
|
+
accountId,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Keypair } from '@solana/web3.js';
|
|
2
|
+
import bs58 from 'bs58';
|
|
3
|
+
import { getAuth, setAuth } from './index.ts';
|
|
4
|
+
|
|
5
|
+
export interface WalletInfo {
|
|
6
|
+
publicKey: string;
|
|
7
|
+
privateKey: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function generateWallet(): WalletInfo {
|
|
11
|
+
const keypair = Keypair.generate();
|
|
12
|
+
return {
|
|
13
|
+
privateKey: bs58.encode(keypair.secretKey),
|
|
14
|
+
publicKey: keypair.publicKey.toBase58(),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function importWallet(privateKey: string): WalletInfo {
|
|
19
|
+
const privateKeyBytes = bs58.decode(privateKey);
|
|
20
|
+
const keypair = Keypair.fromSecretKey(privateKeyBytes);
|
|
21
|
+
return {
|
|
22
|
+
privateKey,
|
|
23
|
+
publicKey: keypair.publicKey.toBase58(),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function getSetuWallet(
|
|
28
|
+
projectRoot?: string,
|
|
29
|
+
): Promise<WalletInfo | null> {
|
|
30
|
+
const auth = await getAuth('setu', projectRoot);
|
|
31
|
+
if (auth?.type === 'wallet' && auth.secret) {
|
|
32
|
+
return importWallet(auth.secret);
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function ensureSetuWallet(
|
|
38
|
+
projectRoot?: string,
|
|
39
|
+
): Promise<WalletInfo> {
|
|
40
|
+
const existing = await getSetuWallet(projectRoot);
|
|
41
|
+
if (existing) return existing;
|
|
42
|
+
|
|
43
|
+
const wallet = generateWallet();
|
|
44
|
+
await setAuth(
|
|
45
|
+
'setu',
|
|
46
|
+
{ type: 'wallet', secret: wallet.privateKey },
|
|
47
|
+
projectRoot,
|
|
48
|
+
'global',
|
|
49
|
+
);
|
|
50
|
+
return wallet;
|
|
51
|
+
}
|
|
@@ -109,3 +109,43 @@ export async function removeAuth(
|
|
|
109
109
|
) {
|
|
110
110
|
await removeAuthFile(provider, projectRoot, scope);
|
|
111
111
|
}
|
|
112
|
+
|
|
113
|
+
export async function getOnboardingComplete(
|
|
114
|
+
_projectRoot?: string,
|
|
115
|
+
): Promise<boolean> {
|
|
116
|
+
const globalPath = getGlobalConfigPath();
|
|
117
|
+
const f = Bun.file(globalPath);
|
|
118
|
+
if (await f.exists()) {
|
|
119
|
+
try {
|
|
120
|
+
const data = await f.json();
|
|
121
|
+
return data?.onboardingComplete === true;
|
|
122
|
+
} catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export async function setOnboardingComplete(
|
|
130
|
+
_projectRoot?: string,
|
|
131
|
+
): Promise<void> {
|
|
132
|
+
const globalPath = getGlobalConfigPath();
|
|
133
|
+
const base = getGlobalConfigDir();
|
|
134
|
+
|
|
135
|
+
let existing: Record<string, unknown> = {};
|
|
136
|
+
const f = Bun.file(globalPath);
|
|
137
|
+
if (await f.exists()) {
|
|
138
|
+
try {
|
|
139
|
+
existing = (await f.json()) as Record<string, unknown>;
|
|
140
|
+
} catch {}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const next = { ...existing, onboardingComplete: true };
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
const { promises: fs } = await import('node:fs');
|
|
147
|
+
await fs.mkdir(base, { recursive: true }).catch(() => {});
|
|
148
|
+
} catch {}
|
|
149
|
+
|
|
150
|
+
await Bun.write(globalPath, JSON.stringify(next, null, 2));
|
|
151
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -120,6 +120,8 @@ export {
|
|
|
120
120
|
refreshToken,
|
|
121
121
|
openAuthUrl,
|
|
122
122
|
createApiKey,
|
|
123
|
+
authorizeWeb,
|
|
124
|
+
exchangeWeb,
|
|
123
125
|
} from './auth/src/index.ts';
|
|
124
126
|
export {
|
|
125
127
|
authorizeOpenAI,
|
|
@@ -127,8 +129,17 @@ export {
|
|
|
127
129
|
refreshOpenAIToken,
|
|
128
130
|
openOpenAIAuthUrl,
|
|
129
131
|
obtainOpenAIApiKey,
|
|
132
|
+
authorizeOpenAIWeb,
|
|
133
|
+
exchangeOpenAIWeb,
|
|
130
134
|
} from './auth/src/index.ts';
|
|
131
135
|
export type { OpenAIOAuthResult } from './auth/src/index.ts';
|
|
136
|
+
export {
|
|
137
|
+
generateWallet,
|
|
138
|
+
importWallet,
|
|
139
|
+
getSetuWallet,
|
|
140
|
+
ensureSetuWallet,
|
|
141
|
+
} from './auth/src/index.ts';
|
|
142
|
+
export type { WalletInfo } from './auth/src/index.ts';
|
|
132
143
|
|
|
133
144
|
// =======================
|
|
134
145
|
// Configuration (from internal config module)
|
|
@@ -152,6 +163,8 @@ export {
|
|
|
152
163
|
writeDefaults as setConfig,
|
|
153
164
|
writeAuth,
|
|
154
165
|
removeAuth as removeConfig,
|
|
166
|
+
getOnboardingComplete,
|
|
167
|
+
setOnboardingComplete,
|
|
155
168
|
} from './config/src/manager.ts';
|
|
156
169
|
export type { Scope } from './config/src/manager.ts';
|
|
157
170
|
|