@agi-cli/sdk 0.1.148 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agi-cli/sdk",
3
- "version": "0.1.148",
3
+ "version": "0.1.150",
4
4
  "description": "AI agent SDK for building intelligent assistants - tree-shakable and comprehensive",
5
5
  "author": "nitishxyz",
6
6
  "license": "MIT",
@@ -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';
@@ -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><title>AGI - Authentication Successful</title></head>
127
- <body style="font-family: system-ui; text-align: center; padding: 50px;">
128
- <h1>✅ Authentication Successful</h1>
129
- <p>You can close this window and return to the terminal.</p>
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
 
@@ -40,4 +40,5 @@ export type AGIConfig = {
40
40
  defaults: DefaultConfig;
41
41
  providers: Record<ProviderId, ProviderConfig>;
42
42
  paths: PathConfig;
43
+ onboardingComplete?: boolean;
43
44
  };