@agi-cli/sdk 0.1.131 → 0.1.132
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/core/src/providers/resolver.ts +20 -5
- package/src/index.ts +3 -0
- package/src/providers/src/catalog-manual.ts +21 -2
- package/src/providers/src/catalog.ts +80 -5
- package/src/providers/src/index.ts +3 -0
- package/src/providers/src/openai-oauth-client.ts +46 -3
- package/src/providers/src/solforge-client.ts +101 -19
package/package.json
CHANGED
|
@@ -10,6 +10,23 @@ import {
|
|
|
10
10
|
} from '../../../providers/src/index.ts';
|
|
11
11
|
import type { OAuth } from '../../../types/src/index.ts';
|
|
12
12
|
|
|
13
|
+
function needsResponsesApi(model: string): boolean {
|
|
14
|
+
const m = model.toLowerCase();
|
|
15
|
+
if (m.includes('gpt-5')) return true;
|
|
16
|
+
if (m.startsWith('o1')) return true;
|
|
17
|
+
if (m.startsWith('o3')) return true;
|
|
18
|
+
if (m.startsWith('o4')) return true;
|
|
19
|
+
if (m.includes('codex-mini')) return true;
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function resolveOpenAIModel(
|
|
24
|
+
instance: ReturnType<typeof createOpenAI>,
|
|
25
|
+
model: string,
|
|
26
|
+
) {
|
|
27
|
+
return needsResponsesApi(model) ? instance.responses(model) : instance(model);
|
|
28
|
+
}
|
|
29
|
+
|
|
13
30
|
export type ProviderName =
|
|
14
31
|
| 'openai'
|
|
15
32
|
| 'anthropic'
|
|
@@ -45,13 +62,13 @@ export async function resolveModel(
|
|
|
45
62
|
apiKey: config.apiKey || 'oauth-token',
|
|
46
63
|
fetch: config.customFetch,
|
|
47
64
|
});
|
|
48
|
-
return instance
|
|
65
|
+
return resolveOpenAIModel(instance, model);
|
|
49
66
|
}
|
|
50
67
|
if (config.apiKey) {
|
|
51
68
|
const instance = createOpenAI({ apiKey: config.apiKey });
|
|
52
|
-
return instance
|
|
69
|
+
return resolveOpenAIModel(instance, model);
|
|
53
70
|
}
|
|
54
|
-
return openai(model);
|
|
71
|
+
return needsResponsesApi(model) ? openai.responses(model) : openai(model);
|
|
55
72
|
}
|
|
56
73
|
|
|
57
74
|
if (provider === 'anthropic') {
|
|
@@ -143,14 +160,12 @@ export async function resolveModel(
|
|
|
143
160
|
}
|
|
144
161
|
const baseURL = config.baseURL || process.env.SOLFORGE_BASE_URL;
|
|
145
162
|
const rpcURL = process.env.SOLFORGE_SOLANA_RPC_URL;
|
|
146
|
-
const topupAmount = process.env.SOLFORGE_TOPUP_MICRO_USDC;
|
|
147
163
|
return createSolforgeModel(
|
|
148
164
|
model,
|
|
149
165
|
{ privateKey },
|
|
150
166
|
{
|
|
151
167
|
baseURL,
|
|
152
168
|
rpcURL,
|
|
153
|
-
topupAmountMicroUsdc: topupAmount,
|
|
154
169
|
},
|
|
155
170
|
);
|
|
156
171
|
}
|
package/src/index.ts
CHANGED
|
@@ -57,10 +57,13 @@ export {
|
|
|
57
57
|
export {
|
|
58
58
|
createSolforgeFetch,
|
|
59
59
|
createSolforgeModel,
|
|
60
|
+
fetchSolforgeBalance,
|
|
61
|
+
getPublicKeyFromPrivate,
|
|
60
62
|
} from './providers/src/index.ts';
|
|
61
63
|
export type {
|
|
62
64
|
SolforgeAuth,
|
|
63
65
|
SolforgeProviderOptions,
|
|
66
|
+
SolforgeBalanceResponse,
|
|
64
67
|
} from './providers/src/index.ts';
|
|
65
68
|
export {
|
|
66
69
|
createOpenAIOAuthFetch,
|
|
@@ -7,6 +7,20 @@ import type {
|
|
|
7
7
|
type CatalogMap = Partial<Record<ProviderId, ProviderCatalogEntry>>;
|
|
8
8
|
|
|
9
9
|
const SOLFORGE_ID: ProviderId = 'solforge';
|
|
10
|
+
|
|
11
|
+
const isAllowedOpenAIModel = (id: string): boolean => {
|
|
12
|
+
if (id === 'codex-mini-latest') return true;
|
|
13
|
+
if (id.startsWith('gpt-5')) return true;
|
|
14
|
+
if (id.includes('codex')) return true;
|
|
15
|
+
return false;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const isAllowedAnthropicModel = (id: string): boolean => {
|
|
19
|
+
if (id.includes('-4-') || id.includes('-4.')) return true;
|
|
20
|
+
if (id.match(/claude-(haiku|sonnet|opus)-4/)) return true;
|
|
21
|
+
return false;
|
|
22
|
+
};
|
|
23
|
+
|
|
10
24
|
const SOLFORGE_SOURCES: Array<{ id: ProviderId; npm: string }> = [
|
|
11
25
|
{
|
|
12
26
|
id: 'openai',
|
|
@@ -39,7 +53,12 @@ function cloneModel(model: ModelInfo): ModelInfo {
|
|
|
39
53
|
|
|
40
54
|
function buildSolforgeEntry(base: CatalogMap): ProviderCatalogEntry | null {
|
|
41
55
|
const solforgeModels = SOLFORGE_SOURCES.flatMap(({ id, npm }) => {
|
|
42
|
-
const
|
|
56
|
+
const allModels = base[id]?.models ?? [];
|
|
57
|
+
const sourceModels = allModels.filter((model) => {
|
|
58
|
+
if (id === 'openai') return isAllowedOpenAIModel(model.id);
|
|
59
|
+
if (id === 'anthropic') return isAllowedAnthropicModel(model.id);
|
|
60
|
+
return true;
|
|
61
|
+
});
|
|
43
62
|
return sourceModels.map((model) => {
|
|
44
63
|
const cloned = cloneModel(model);
|
|
45
64
|
cloned.provider = { ...(cloned.provider ?? {}), npm };
|
|
@@ -61,7 +80,7 @@ function buildSolforgeEntry(base: CatalogMap): ProviderCatalogEntry | null {
|
|
|
61
80
|
return providerA.localeCompare(providerB);
|
|
62
81
|
});
|
|
63
82
|
|
|
64
|
-
const defaultModelId = '
|
|
83
|
+
const defaultModelId = 'codex-mini-latest';
|
|
65
84
|
const defaultIdx = solforgeModels.findIndex((m) => m.id === defaultModelId);
|
|
66
85
|
if (defaultIdx > 0) {
|
|
67
86
|
const [picked] = solforgeModels.splice(defaultIdx, 1);
|
|
@@ -324,7 +324,7 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
324
324
|
cost: {
|
|
325
325
|
input: 1.25,
|
|
326
326
|
output: 10,
|
|
327
|
-
cacheRead: 0.
|
|
327
|
+
cacheRead: 0.125,
|
|
328
328
|
},
|
|
329
329
|
limit: {
|
|
330
330
|
context: 400000,
|
|
@@ -398,7 +398,7 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
398
398
|
cost: {
|
|
399
399
|
input: 0.25,
|
|
400
400
|
output: 2,
|
|
401
|
-
cacheRead: 0.
|
|
401
|
+
cacheRead: 0.025,
|
|
402
402
|
},
|
|
403
403
|
limit: {
|
|
404
404
|
context: 400000,
|
|
@@ -423,7 +423,7 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
423
423
|
cost: {
|
|
424
424
|
input: 0.05,
|
|
425
425
|
output: 0.4,
|
|
426
|
-
cacheRead: 0.
|
|
426
|
+
cacheRead: 0.005,
|
|
427
427
|
},
|
|
428
428
|
limit: {
|
|
429
429
|
context: 400000,
|
|
@@ -633,7 +633,7 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
633
633
|
id: 'gpt-5.2-codex',
|
|
634
634
|
label: 'GPT-5.2 Codex',
|
|
635
635
|
modalities: {
|
|
636
|
-
input: ['text', 'image'],
|
|
636
|
+
input: ['text', 'image', 'pdf'],
|
|
637
637
|
output: ['text'],
|
|
638
638
|
},
|
|
639
639
|
toolCall: true,
|
|
@@ -4278,6 +4278,31 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
4278
4278
|
output: 128000,
|
|
4279
4279
|
},
|
|
4280
4280
|
},
|
|
4281
|
+
{
|
|
4282
|
+
id: 'openai/gpt-5.1-codex-max',
|
|
4283
|
+
label: 'GPT-5.1-Codex-Max',
|
|
4284
|
+
modalities: {
|
|
4285
|
+
input: ['text', 'image'],
|
|
4286
|
+
output: ['text'],
|
|
4287
|
+
},
|
|
4288
|
+
toolCall: true,
|
|
4289
|
+
reasoning: true,
|
|
4290
|
+
attachment: true,
|
|
4291
|
+
temperature: true,
|
|
4292
|
+
knowledge: '2024-09-30',
|
|
4293
|
+
releaseDate: '2025-11-13',
|
|
4294
|
+
lastUpdated: '2025-11-13',
|
|
4295
|
+
openWeights: false,
|
|
4296
|
+
cost: {
|
|
4297
|
+
input: 1.1,
|
|
4298
|
+
output: 9,
|
|
4299
|
+
cacheRead: 0.11,
|
|
4300
|
+
},
|
|
4301
|
+
limit: {
|
|
4302
|
+
context: 400000,
|
|
4303
|
+
output: 128000,
|
|
4304
|
+
},
|
|
4305
|
+
},
|
|
4281
4306
|
{
|
|
4282
4307
|
id: 'openai/gpt-5.1-codex-mini',
|
|
4283
4308
|
label: 'GPT-5.1-Codex-Mini',
|
|
@@ -5925,6 +5950,31 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
5925
5950
|
output: 131072,
|
|
5926
5951
|
},
|
|
5927
5952
|
},
|
|
5953
|
+
{
|
|
5954
|
+
id: 'glm-4.7',
|
|
5955
|
+
label: 'GLM-4.7',
|
|
5956
|
+
modalities: {
|
|
5957
|
+
input: ['text'],
|
|
5958
|
+
output: ['text'],
|
|
5959
|
+
},
|
|
5960
|
+
toolCall: true,
|
|
5961
|
+
reasoning: true,
|
|
5962
|
+
attachment: false,
|
|
5963
|
+
temperature: true,
|
|
5964
|
+
knowledge: '2025-04',
|
|
5965
|
+
releaseDate: '2025-12-22',
|
|
5966
|
+
lastUpdated: '2025-12-22',
|
|
5967
|
+
openWeights: true,
|
|
5968
|
+
cost: {
|
|
5969
|
+
input: 0.6,
|
|
5970
|
+
output: 2.2,
|
|
5971
|
+
cacheRead: 0.1,
|
|
5972
|
+
},
|
|
5973
|
+
limit: {
|
|
5974
|
+
context: 204800,
|
|
5975
|
+
output: 131072,
|
|
5976
|
+
},
|
|
5977
|
+
},
|
|
5928
5978
|
{
|
|
5929
5979
|
id: 'glm-4.7-free',
|
|
5930
5980
|
label: 'GLM-4.7',
|
|
@@ -6178,7 +6228,7 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
6178
6228
|
id: 'gpt-5.2-codex',
|
|
6179
6229
|
label: 'GPT-5.2 Codex',
|
|
6180
6230
|
modalities: {
|
|
6181
|
-
input: ['text', 'image'],
|
|
6231
|
+
input: ['text', 'image', 'pdf'],
|
|
6182
6232
|
output: ['text'],
|
|
6183
6233
|
},
|
|
6184
6234
|
toolCall: true,
|
|
@@ -6694,6 +6744,31 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
6694
6744
|
output: 131072,
|
|
6695
6745
|
},
|
|
6696
6746
|
},
|
|
6747
|
+
{
|
|
6748
|
+
id: 'glm-4.7-flash',
|
|
6749
|
+
label: 'GLM-4.7-Flash',
|
|
6750
|
+
modalities: {
|
|
6751
|
+
input: ['text'],
|
|
6752
|
+
output: ['text'],
|
|
6753
|
+
},
|
|
6754
|
+
toolCall: true,
|
|
6755
|
+
reasoning: true,
|
|
6756
|
+
attachment: false,
|
|
6757
|
+
temperature: true,
|
|
6758
|
+
knowledge: '2025-04',
|
|
6759
|
+
releaseDate: '2026-01-19',
|
|
6760
|
+
lastUpdated: '2026-01-19',
|
|
6761
|
+
openWeights: true,
|
|
6762
|
+
cost: {
|
|
6763
|
+
input: 0,
|
|
6764
|
+
output: 0,
|
|
6765
|
+
cacheRead: 0,
|
|
6766
|
+
},
|
|
6767
|
+
limit: {
|
|
6768
|
+
context: 200000,
|
|
6769
|
+
output: 131072,
|
|
6770
|
+
},
|
|
6771
|
+
},
|
|
6697
6772
|
],
|
|
6698
6773
|
label: 'Z.AI Coding Plan',
|
|
6699
6774
|
env: ['ZHIPU_API_KEY'],
|
|
@@ -20,11 +20,14 @@ export { providerEnvVar, readEnvKey, setEnvKey } from './env.ts';
|
|
|
20
20
|
export {
|
|
21
21
|
createSolforgeFetch,
|
|
22
22
|
createSolforgeModel,
|
|
23
|
+
fetchSolforgeBalance,
|
|
24
|
+
getPublicKeyFromPrivate,
|
|
23
25
|
} from './solforge-client.ts';
|
|
24
26
|
export type {
|
|
25
27
|
SolforgeAuth,
|
|
26
28
|
SolforgeProviderOptions,
|
|
27
29
|
SolforgePaymentCallbacks,
|
|
30
|
+
SolforgeBalanceResponse,
|
|
28
31
|
} from './solforge-client.ts';
|
|
29
32
|
export {
|
|
30
33
|
createOpenAIOAuthFetch,
|
|
@@ -14,6 +14,8 @@ export type OpenAIOAuthConfig = {
|
|
|
14
14
|
instructions?: string;
|
|
15
15
|
reasoningEffort?: 'none' | 'low' | 'medium' | 'high' | 'xhigh';
|
|
16
16
|
reasoningSummary?: 'auto' | 'detailed';
|
|
17
|
+
promptCacheKey?: string;
|
|
18
|
+
promptCacheRetention?: 'in_memory' | '24h';
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
async function ensureValidToken(
|
|
@@ -44,10 +46,38 @@ async function ensureValidToken(
|
|
|
44
46
|
|
|
45
47
|
function stripIdsFromInput(input: unknown): unknown {
|
|
46
48
|
if (Array.isArray(input)) {
|
|
47
|
-
|
|
49
|
+
const filtered = input.filter((item) => {
|
|
50
|
+
if (item && typeof item === 'object' && 'type' in item) {
|
|
51
|
+
if (item.type === 'item_reference') return false;
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const validCallIds = new Set<string>();
|
|
57
|
+
for (const item of filtered) {
|
|
58
|
+
if (
|
|
59
|
+
item &&
|
|
60
|
+
typeof item === 'object' &&
|
|
61
|
+
'type' in item &&
|
|
62
|
+
item.type === 'function_call' &&
|
|
63
|
+
'call_id' in item &&
|
|
64
|
+
typeof item.call_id === 'string'
|
|
65
|
+
) {
|
|
66
|
+
validCallIds.add(item.call_id);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return filtered
|
|
48
71
|
.filter((item) => {
|
|
49
|
-
if (
|
|
50
|
-
|
|
72
|
+
if (
|
|
73
|
+
item &&
|
|
74
|
+
typeof item === 'object' &&
|
|
75
|
+
'type' in item &&
|
|
76
|
+
item.type === 'function_call_output' &&
|
|
77
|
+
'call_id' in item &&
|
|
78
|
+
typeof item.call_id === 'string'
|
|
79
|
+
) {
|
|
80
|
+
return validCallIds.has(item.call_id);
|
|
51
81
|
}
|
|
52
82
|
return true;
|
|
53
83
|
})
|
|
@@ -155,6 +185,19 @@ export function createOpenAIOAuthFetch(config: OpenAIOAuthConfig) {
|
|
|
155
185
|
}
|
|
156
186
|
}
|
|
157
187
|
|
|
188
|
+
const cacheKey =
|
|
189
|
+
parsed.providerOptions?.openai?.promptCacheKey ||
|
|
190
|
+
config.promptCacheKey;
|
|
191
|
+
const cacheRetention =
|
|
192
|
+
parsed.providerOptions?.openai?.promptCacheRetention ||
|
|
193
|
+
config.promptCacheRetention;
|
|
194
|
+
if (cacheKey) {
|
|
195
|
+
parsed.prompt_cache_key = cacheKey;
|
|
196
|
+
}
|
|
197
|
+
if (cacheRetention) {
|
|
198
|
+
parsed.prompt_cache_retention = cacheRetention;
|
|
199
|
+
}
|
|
200
|
+
|
|
158
201
|
delete parsed.max_output_tokens;
|
|
159
202
|
delete parsed.max_completion_tokens;
|
|
160
203
|
|
|
@@ -10,14 +10,17 @@ import { createAnthropic } from '@ai-sdk/anthropic';
|
|
|
10
10
|
|
|
11
11
|
const DEFAULT_BASE_URL = 'https://router.solforge.sh';
|
|
12
12
|
const DEFAULT_RPC_URL = 'https://api.mainnet-beta.solana.com';
|
|
13
|
-
const DEFAULT_TOPUP_AMOUNT = '5000000'; // $5.00
|
|
14
13
|
const DEFAULT_MAX_ATTEMPTS = 3;
|
|
15
14
|
const DEFAULT_MAX_PAYMENT_ATTEMPTS = 20;
|
|
16
15
|
|
|
17
16
|
export type SolforgePaymentCallbacks = {
|
|
18
17
|
onPaymentRequired?: (amountUsd: number) => void;
|
|
19
18
|
onPaymentSigning?: () => void;
|
|
20
|
-
onPaymentComplete?: (data: {
|
|
19
|
+
onPaymentComplete?: (data: {
|
|
20
|
+
amountUsd: number;
|
|
21
|
+
newBalance: number;
|
|
22
|
+
transactionId?: string;
|
|
23
|
+
}) => void;
|
|
21
24
|
onPaymentError?: (error: string) => void;
|
|
22
25
|
};
|
|
23
26
|
|
|
@@ -25,11 +28,12 @@ export type SolforgeProviderOptions = {
|
|
|
25
28
|
baseURL?: string;
|
|
26
29
|
rpcURL?: string;
|
|
27
30
|
network?: string;
|
|
28
|
-
topupAmountMicroUsdc?: string;
|
|
29
31
|
maxRequestAttempts?: number;
|
|
30
32
|
maxPaymentAttempts?: number;
|
|
31
33
|
callbacks?: SolforgePaymentCallbacks;
|
|
32
34
|
providerNpm?: string;
|
|
35
|
+
promptCacheKey?: string;
|
|
36
|
+
promptCacheRetention?: 'in_memory' | '24h';
|
|
33
37
|
};
|
|
34
38
|
|
|
35
39
|
export type SolforgeAuth = {
|
|
@@ -60,6 +64,7 @@ type PaymentResponse = {
|
|
|
60
64
|
new_balance?: number | string;
|
|
61
65
|
amount?: number;
|
|
62
66
|
balance?: number;
|
|
67
|
+
transaction?: string;
|
|
63
68
|
};
|
|
64
69
|
|
|
65
70
|
export function createSolforgeFetch(
|
|
@@ -71,11 +76,12 @@ export function createSolforgeFetch(
|
|
|
71
76
|
const walletAddress = keypair.publicKey.toBase58();
|
|
72
77
|
const baseURL = trimTrailingSlash(options.baseURL ?? DEFAULT_BASE_URL);
|
|
73
78
|
const rpcURL = options.rpcURL ?? DEFAULT_RPC_URL;
|
|
74
|
-
const targetTopup = options.topupAmountMicroUsdc ?? DEFAULT_TOPUP_AMOUNT;
|
|
75
79
|
const maxAttempts = options.maxRequestAttempts ?? DEFAULT_MAX_ATTEMPTS;
|
|
76
80
|
const maxPaymentAttempts =
|
|
77
81
|
options.maxPaymentAttempts ?? DEFAULT_MAX_PAYMENT_ATTEMPTS;
|
|
78
82
|
const callbacks = options.callbacks ?? {};
|
|
83
|
+
const promptCacheKey = options.promptCacheKey;
|
|
84
|
+
const promptCacheRetention = options.promptCacheRetention;
|
|
79
85
|
|
|
80
86
|
const baseFetch = globalThis.fetch.bind(globalThis);
|
|
81
87
|
let paymentAttempts = 0;
|
|
@@ -98,19 +104,36 @@ export function createSolforgeFetch(
|
|
|
98
104
|
|
|
99
105
|
while (attempt < maxAttempts) {
|
|
100
106
|
attempt++;
|
|
107
|
+
let body = init?.body;
|
|
108
|
+
if (
|
|
109
|
+
body &&
|
|
110
|
+
typeof body === 'string' &&
|
|
111
|
+
(promptCacheKey || promptCacheRetention)
|
|
112
|
+
) {
|
|
113
|
+
try {
|
|
114
|
+
const parsed = JSON.parse(body);
|
|
115
|
+
if (promptCacheKey) {
|
|
116
|
+
parsed.prompt_cache_key = promptCacheKey;
|
|
117
|
+
}
|
|
118
|
+
if (promptCacheRetention) {
|
|
119
|
+
parsed.prompt_cache_retention = promptCacheRetention;
|
|
120
|
+
}
|
|
121
|
+
body = JSON.stringify(parsed);
|
|
122
|
+
} catch {}
|
|
123
|
+
}
|
|
101
124
|
const headers = new Headers(init?.headers);
|
|
102
125
|
const walletHeaders = buildWalletHeaders();
|
|
103
126
|
headers.set('x-wallet-address', walletHeaders['x-wallet-address']);
|
|
104
127
|
headers.set('x-wallet-nonce', walletHeaders['x-wallet-nonce']);
|
|
105
128
|
headers.set('x-wallet-signature', walletHeaders['x-wallet-signature']);
|
|
106
|
-
const response = await baseFetch(input, { ...init, headers });
|
|
129
|
+
const response = await baseFetch(input, { ...init, body, headers });
|
|
107
130
|
|
|
108
131
|
if (response.status !== 402) {
|
|
109
132
|
return response;
|
|
110
133
|
}
|
|
111
134
|
|
|
112
135
|
const payload = await response.json().catch(() => ({}));
|
|
113
|
-
const requirement = pickPaymentRequirement(payload
|
|
136
|
+
const requirement = pickPaymentRequirement(payload);
|
|
114
137
|
if (!requirement) {
|
|
115
138
|
callbacks.onPaymentError?.('Unsupported payment requirement');
|
|
116
139
|
throw new Error('Solforge: unsupported payment requirement');
|
|
@@ -183,7 +206,7 @@ export function createSolforgeModel(
|
|
|
183
206
|
apiKey: 'solforge-wallet-auth',
|
|
184
207
|
fetch: customFetch,
|
|
185
208
|
});
|
|
186
|
-
return openai(model);
|
|
209
|
+
return openai.responses(model);
|
|
187
210
|
}
|
|
188
211
|
|
|
189
212
|
function trimTrailingSlash(url: string) {
|
|
@@ -202,7 +225,6 @@ type PaymentRequirementResponse = {
|
|
|
202
225
|
|
|
203
226
|
function pickPaymentRequirement(
|
|
204
227
|
payload: unknown,
|
|
205
|
-
targetAmount: string,
|
|
206
228
|
): ExactPaymentRequirement | null {
|
|
207
229
|
const acceptsValue =
|
|
208
230
|
typeof payload === 'object' && payload !== null
|
|
@@ -211,17 +233,7 @@ function pickPaymentRequirement(
|
|
|
211
233
|
const accepts = Array.isArray(acceptsValue)
|
|
212
234
|
? (acceptsValue as ExactPaymentRequirement[])
|
|
213
235
|
: [];
|
|
214
|
-
|
|
215
|
-
(option) =>
|
|
216
|
-
option &&
|
|
217
|
-
option.scheme === 'exact' &&
|
|
218
|
-
option.maxAmountRequired === targetAmount,
|
|
219
|
-
);
|
|
220
|
-
if (exactMatch) return exactMatch;
|
|
221
|
-
const fallback = accepts.find(
|
|
222
|
-
(option) => option && option.scheme === 'exact',
|
|
223
|
-
);
|
|
224
|
-
return fallback ?? null;
|
|
236
|
+
return accepts.find((option) => option && option.scheme === 'exact') ?? null;
|
|
225
237
|
}
|
|
226
238
|
|
|
227
239
|
async function handlePayment(args: {
|
|
@@ -318,6 +330,7 @@ async function processSinglePayment(args: {
|
|
|
318
330
|
args.callbacks.onPaymentComplete?.({
|
|
319
331
|
amountUsd,
|
|
320
332
|
newBalance,
|
|
333
|
+
transactionId: parsed.transaction,
|
|
321
334
|
});
|
|
322
335
|
console.log(
|
|
323
336
|
`Solforge payment complete: +$${amountUsd} (balance: $${newBalance})`,
|
|
@@ -360,3 +373,72 @@ async function createPaymentPayload(args: {
|
|
|
360
373
|
},
|
|
361
374
|
} as PaymentPayload;
|
|
362
375
|
}
|
|
376
|
+
|
|
377
|
+
export type SolforgeBalanceResponse = {
|
|
378
|
+
walletAddress: string;
|
|
379
|
+
balance: number;
|
|
380
|
+
totalSpent: number;
|
|
381
|
+
totalTopups: number;
|
|
382
|
+
requestCount: number;
|
|
383
|
+
createdAt?: string;
|
|
384
|
+
lastRequest?: string;
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
export async function fetchSolforgeBalance(
|
|
388
|
+
auth: SolforgeAuth,
|
|
389
|
+
baseURL?: string,
|
|
390
|
+
): Promise<SolforgeBalanceResponse | null> {
|
|
391
|
+
try {
|
|
392
|
+
const privateKeyBytes = bs58.decode(auth.privateKey);
|
|
393
|
+
const keypair = Keypair.fromSecretKey(privateKeyBytes);
|
|
394
|
+
const walletAddress = keypair.publicKey.toBase58();
|
|
395
|
+
const url = trimTrailingSlash(baseURL ?? DEFAULT_BASE_URL);
|
|
396
|
+
|
|
397
|
+
const nonce = Date.now().toString();
|
|
398
|
+
const signature = signNonce(nonce, privateKeyBytes);
|
|
399
|
+
|
|
400
|
+
const response = await fetch(`${url}/v1/balance`, {
|
|
401
|
+
headers: {
|
|
402
|
+
'x-wallet-address': walletAddress,
|
|
403
|
+
'x-wallet-nonce': nonce,
|
|
404
|
+
'x-wallet-signature': signature,
|
|
405
|
+
},
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
if (!response.ok) {
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const data = (await response.json()) as {
|
|
413
|
+
wallet_address: string;
|
|
414
|
+
balance_usd: number;
|
|
415
|
+
total_spent: number;
|
|
416
|
+
total_topups: number;
|
|
417
|
+
request_count: number;
|
|
418
|
+
created_at?: string;
|
|
419
|
+
last_request?: string;
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
return {
|
|
423
|
+
walletAddress: data.wallet_address,
|
|
424
|
+
balance: data.balance_usd,
|
|
425
|
+
totalSpent: data.total_spent,
|
|
426
|
+
totalTopups: data.total_topups,
|
|
427
|
+
requestCount: data.request_count,
|
|
428
|
+
createdAt: data.created_at,
|
|
429
|
+
lastRequest: data.last_request,
|
|
430
|
+
};
|
|
431
|
+
} catch {
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export function getPublicKeyFromPrivate(privateKey: string): string | null {
|
|
437
|
+
try {
|
|
438
|
+
const privateKeyBytes = bs58.decode(privateKey);
|
|
439
|
+
const keypair = Keypair.fromSecretKey(privateKeyBytes);
|
|
440
|
+
return keypair.publicKey.toBase58();
|
|
441
|
+
} catch {
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
}
|