@agi-cli/sdk 0.1.132 → 0.1.134
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/index.ts +2 -0
- package/src/providers/src/index.ts +2 -0
- package/src/providers/src/solforge-client.ts +222 -25
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -59,11 +59,13 @@ export {
|
|
|
59
59
|
createSolforgeModel,
|
|
60
60
|
fetchSolforgeBalance,
|
|
61
61
|
getPublicKeyFromPrivate,
|
|
62
|
+
fetchSolanaUsdcBalance,
|
|
62
63
|
} from './providers/src/index.ts';
|
|
63
64
|
export type {
|
|
64
65
|
SolforgeAuth,
|
|
65
66
|
SolforgeProviderOptions,
|
|
66
67
|
SolforgeBalanceResponse,
|
|
68
|
+
SolanaUsdcBalanceResponse,
|
|
67
69
|
} from './providers/src/index.ts';
|
|
68
70
|
export {
|
|
69
71
|
createOpenAIOAuthFetch,
|
|
@@ -22,12 +22,14 @@ export {
|
|
|
22
22
|
createSolforgeModel,
|
|
23
23
|
fetchSolforgeBalance,
|
|
24
24
|
getPublicKeyFromPrivate,
|
|
25
|
+
fetchSolanaUsdcBalance,
|
|
25
26
|
} from './solforge-client.ts';
|
|
26
27
|
export type {
|
|
27
28
|
SolforgeAuth,
|
|
28
29
|
SolforgeProviderOptions,
|
|
29
30
|
SolforgePaymentCallbacks,
|
|
30
31
|
SolforgeBalanceResponse,
|
|
32
|
+
SolanaUsdcBalanceResponse,
|
|
31
33
|
} from './solforge-client.ts';
|
|
32
34
|
export {
|
|
33
35
|
createOpenAIOAuthFetch,
|
|
@@ -67,6 +67,42 @@ type PaymentResponse = {
|
|
|
67
67
|
transaction?: string;
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
+
type PaymentQueueEntry = {
|
|
71
|
+
promise: Promise<void>;
|
|
72
|
+
resolve: () => void;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const paymentQueues = new Map<string, PaymentQueueEntry>();
|
|
76
|
+
const globalPaymentAttempts = new Map<string, number>();
|
|
77
|
+
|
|
78
|
+
async function acquirePaymentLock(walletAddress: string): Promise<() => void> {
|
|
79
|
+
const existing = paymentQueues.get(walletAddress);
|
|
80
|
+
|
|
81
|
+
let resolveFunc: () => void = () => {};
|
|
82
|
+
const newPromise = new Promise<void>((resolve) => {
|
|
83
|
+
resolveFunc = resolve;
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const entry: PaymentQueueEntry = {
|
|
87
|
+
promise: newPromise,
|
|
88
|
+
resolve: resolveFunc,
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
paymentQueues.set(walletAddress, entry);
|
|
92
|
+
|
|
93
|
+
if (existing) {
|
|
94
|
+
console.log('[Solforge] Waiting for pending payment to complete...');
|
|
95
|
+
await existing.promise;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return () => {
|
|
99
|
+
if (paymentQueues.get(walletAddress) === entry) {
|
|
100
|
+
paymentQueues.delete(walletAddress);
|
|
101
|
+
}
|
|
102
|
+
resolveFunc();
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
70
106
|
export function createSolforgeFetch(
|
|
71
107
|
auth: SolforgeAuth,
|
|
72
108
|
options: SolforgeProviderOptions = {},
|
|
@@ -84,7 +120,6 @@ export function createSolforgeFetch(
|
|
|
84
120
|
const promptCacheRetention = options.promptCacheRetention;
|
|
85
121
|
|
|
86
122
|
const baseFetch = globalThis.fetch.bind(globalThis);
|
|
87
|
-
let paymentAttempts = 0;
|
|
88
123
|
|
|
89
124
|
const buildWalletHeaders = () => {
|
|
90
125
|
const nonce = Date.now().toString();
|
|
@@ -105,19 +140,95 @@ export function createSolforgeFetch(
|
|
|
105
140
|
while (attempt < maxAttempts) {
|
|
106
141
|
attempt++;
|
|
107
142
|
let body = init?.body;
|
|
108
|
-
if (
|
|
109
|
-
body &&
|
|
110
|
-
typeof body === 'string' &&
|
|
111
|
-
(promptCacheKey || promptCacheRetention)
|
|
112
|
-
) {
|
|
143
|
+
if (body && typeof body === 'string') {
|
|
113
144
|
try {
|
|
114
145
|
const parsed = JSON.parse(body);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (promptCacheRetention) {
|
|
146
|
+
|
|
147
|
+
if (promptCacheKey) parsed.prompt_cache_key = promptCacheKey;
|
|
148
|
+
if (promptCacheRetention)
|
|
119
149
|
parsed.prompt_cache_retention = promptCacheRetention;
|
|
150
|
+
|
|
151
|
+
const MAX_SYSTEM_CACHE = 1;
|
|
152
|
+
const MAX_MESSAGE_CACHE = 1;
|
|
153
|
+
let systemCacheUsed = 0;
|
|
154
|
+
let messageCacheUsed = 0;
|
|
155
|
+
|
|
156
|
+
if (parsed.system && Array.isArray(parsed.system)) {
|
|
157
|
+
parsed.system = parsed.system.map(
|
|
158
|
+
(
|
|
159
|
+
block: { type: string; cache_control?: unknown },
|
|
160
|
+
index: number,
|
|
161
|
+
) => {
|
|
162
|
+
if (block.cache_control) return block;
|
|
163
|
+
if (
|
|
164
|
+
systemCacheUsed < MAX_SYSTEM_CACHE &&
|
|
165
|
+
index === 0 &&
|
|
166
|
+
block.type === 'text'
|
|
167
|
+
) {
|
|
168
|
+
systemCacheUsed++;
|
|
169
|
+
return { ...block, cache_control: { type: 'ephemeral' } };
|
|
170
|
+
}
|
|
171
|
+
return block;
|
|
172
|
+
},
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (parsed.messages && Array.isArray(parsed.messages)) {
|
|
177
|
+
const messageCount = parsed.messages.length;
|
|
178
|
+
parsed.messages = parsed.messages.map(
|
|
179
|
+
(
|
|
180
|
+
msg: {
|
|
181
|
+
role: string;
|
|
182
|
+
content: unknown;
|
|
183
|
+
[key: string]: unknown;
|
|
184
|
+
},
|
|
185
|
+
msgIndex: number,
|
|
186
|
+
) => {
|
|
187
|
+
const isLast = msgIndex === messageCount - 1;
|
|
188
|
+
|
|
189
|
+
if (Array.isArray(msg.content)) {
|
|
190
|
+
const blocks = msg.content as {
|
|
191
|
+
type: string;
|
|
192
|
+
cache_control?: unknown;
|
|
193
|
+
}[];
|
|
194
|
+
const content = blocks.map((block, blockIndex) => {
|
|
195
|
+
if (block.cache_control) return block;
|
|
196
|
+
if (
|
|
197
|
+
isLast &&
|
|
198
|
+
messageCacheUsed < MAX_MESSAGE_CACHE &&
|
|
199
|
+
blockIndex === blocks.length - 1
|
|
200
|
+
) {
|
|
201
|
+
messageCacheUsed++;
|
|
202
|
+
return { ...block, cache_control: { type: 'ephemeral' } };
|
|
203
|
+
}
|
|
204
|
+
return block;
|
|
205
|
+
});
|
|
206
|
+
return { ...msg, content };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (
|
|
210
|
+
isLast &&
|
|
211
|
+
messageCacheUsed < MAX_MESSAGE_CACHE &&
|
|
212
|
+
typeof msg.content === 'string'
|
|
213
|
+
) {
|
|
214
|
+
messageCacheUsed++;
|
|
215
|
+
return {
|
|
216
|
+
...msg,
|
|
217
|
+
content: [
|
|
218
|
+
{
|
|
219
|
+
type: 'text',
|
|
220
|
+
text: msg.content,
|
|
221
|
+
cache_control: { type: 'ephemeral' },
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return msg;
|
|
228
|
+
},
|
|
229
|
+
);
|
|
120
230
|
}
|
|
231
|
+
|
|
121
232
|
body = JSON.stringify(parsed);
|
|
122
233
|
} catch {}
|
|
123
234
|
}
|
|
@@ -143,7 +254,8 @@ export function createSolforgeFetch(
|
|
|
143
254
|
throw new Error('Solforge: payment failed after multiple attempts');
|
|
144
255
|
}
|
|
145
256
|
|
|
146
|
-
const
|
|
257
|
+
const currentAttempts = globalPaymentAttempts.get(walletAddress) ?? 0;
|
|
258
|
+
const remainingPayments = maxPaymentAttempts - currentAttempts;
|
|
147
259
|
if (remainingPayments <= 0) {
|
|
148
260
|
callbacks.onPaymentError?.('Maximum payment attempts exceeded');
|
|
149
261
|
throw new Error(
|
|
@@ -151,20 +263,29 @@ export function createSolforgeFetch(
|
|
|
151
263
|
);
|
|
152
264
|
}
|
|
153
265
|
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
266
|
+
const releaseLock = await acquirePaymentLock(walletAddress);
|
|
267
|
+
|
|
268
|
+
try {
|
|
269
|
+
const amountUsd =
|
|
270
|
+
parseInt(requirement.maxAmountRequired, 10) / 1_000_000;
|
|
271
|
+
callbacks.onPaymentRequired?.(amountUsd);
|
|
272
|
+
|
|
273
|
+
const outcome = await handlePayment({
|
|
274
|
+
requirement,
|
|
275
|
+
keypair,
|
|
276
|
+
rpcURL,
|
|
277
|
+
baseURL,
|
|
278
|
+
baseFetch,
|
|
279
|
+
buildWalletHeaders,
|
|
280
|
+
maxAttempts: remainingPayments,
|
|
281
|
+
callbacks,
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
const newTotal = currentAttempts + outcome.attemptsUsed;
|
|
285
|
+
globalPaymentAttempts.set(walletAddress, newTotal);
|
|
286
|
+
} finally {
|
|
287
|
+
releaseLock();
|
|
288
|
+
}
|
|
168
289
|
}
|
|
169
290
|
|
|
170
291
|
throw new Error('Solforge: max attempts exceeded');
|
|
@@ -442,3 +563,79 @@ export function getPublicKeyFromPrivate(privateKey: string): string | null {
|
|
|
442
563
|
return null;
|
|
443
564
|
}
|
|
444
565
|
}
|
|
566
|
+
|
|
567
|
+
const USDC_MINT_MAINNET = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
|
|
568
|
+
const USDC_MINT_DEVNET = '4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU';
|
|
569
|
+
|
|
570
|
+
export type SolanaUsdcBalanceResponse = {
|
|
571
|
+
walletAddress: string;
|
|
572
|
+
usdcBalance: number;
|
|
573
|
+
network: 'mainnet' | 'devnet';
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
export async function fetchSolanaUsdcBalance(
|
|
577
|
+
auth: SolforgeAuth,
|
|
578
|
+
network: 'mainnet' | 'devnet' = 'mainnet',
|
|
579
|
+
): Promise<SolanaUsdcBalanceResponse | null> {
|
|
580
|
+
try {
|
|
581
|
+
const privateKeyBytes = bs58.decode(auth.privateKey);
|
|
582
|
+
const keypair = Keypair.fromSecretKey(privateKeyBytes);
|
|
583
|
+
const walletAddress = keypair.publicKey.toBase58();
|
|
584
|
+
|
|
585
|
+
const rpcUrl =
|
|
586
|
+
network === 'devnet' ? 'https://api.devnet.solana.com' : DEFAULT_RPC_URL;
|
|
587
|
+
|
|
588
|
+
const usdcMint =
|
|
589
|
+
network === 'devnet' ? USDC_MINT_DEVNET : USDC_MINT_MAINNET;
|
|
590
|
+
|
|
591
|
+
const response = await fetch(rpcUrl, {
|
|
592
|
+
method: 'POST',
|
|
593
|
+
headers: { 'Content-Type': 'application/json' },
|
|
594
|
+
body: JSON.stringify({
|
|
595
|
+
jsonrpc: '2.0',
|
|
596
|
+
id: 1,
|
|
597
|
+
method: 'getTokenAccountsByOwner',
|
|
598
|
+
params: [walletAddress, { mint: usdcMint }, { encoding: 'jsonParsed' }],
|
|
599
|
+
}),
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
if (!response.ok) {
|
|
603
|
+
return null;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
const data = (await response.json()) as {
|
|
607
|
+
result?: {
|
|
608
|
+
value?: Array<{
|
|
609
|
+
account: {
|
|
610
|
+
data: {
|
|
611
|
+
parsed: {
|
|
612
|
+
info: {
|
|
613
|
+
tokenAmount: {
|
|
614
|
+
uiAmount: number;
|
|
615
|
+
};
|
|
616
|
+
};
|
|
617
|
+
};
|
|
618
|
+
};
|
|
619
|
+
};
|
|
620
|
+
}>;
|
|
621
|
+
};
|
|
622
|
+
};
|
|
623
|
+
|
|
624
|
+
const accounts = data.result?.value ?? [];
|
|
625
|
+
let totalUsdcBalance = 0;
|
|
626
|
+
|
|
627
|
+
for (const account of accounts) {
|
|
628
|
+
const uiAmount =
|
|
629
|
+
account.account.data.parsed.info.tokenAmount.uiAmount ?? 0;
|
|
630
|
+
totalUsdcBalance += uiAmount;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
return {
|
|
634
|
+
walletAddress,
|
|
635
|
+
usdcBalance: totalUsdcBalance,
|
|
636
|
+
network,
|
|
637
|
+
};
|
|
638
|
+
} catch {
|
|
639
|
+
return null;
|
|
640
|
+
}
|
|
641
|
+
}
|