@agi-cli/sdk 0.1.169 → 0.1.170
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/copilot-oauth.ts +190 -0
- package/src/auth/src/index.ts +9 -0
- package/src/core/src/providers/resolver.ts +11 -0
- package/src/core/src/tools/builtin/patch.txt +6 -0
- package/src/index.ts +15 -0
- package/src/providers/src/catalog-manual.ts +0 -1
- package/src/providers/src/catalog.ts +1417 -109
- package/src/providers/src/copilot-client.ts +40 -0
- package/src/providers/src/env.ts +1 -0
- package/src/providers/src/index.ts +2 -0
- package/src/providers/src/openai-oauth-client.ts +15 -122
- package/src/providers/src/pricing.ts +1 -0
- package/src/providers/src/setu-client.ts +1 -1
- package/src/providers/src/utils.ts +3 -0
- package/src/types/src/index.ts +1 -0
- package/src/types/src/provider.ts +1 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
|
|
2
|
+
import type { OAuth } from '../../types/src/index.ts';
|
|
3
|
+
|
|
4
|
+
const COPILOT_BASE_URL = 'https://api.githubcopilot.com';
|
|
5
|
+
|
|
6
|
+
export type CopilotOAuthConfig = {
|
|
7
|
+
oauth: OAuth;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function createCopilotFetch(config: CopilotOAuthConfig): typeof fetch {
|
|
11
|
+
return async (
|
|
12
|
+
input: string | URL | Request,
|
|
13
|
+
init?: RequestInit,
|
|
14
|
+
): Promise<Response> => {
|
|
15
|
+
const headers = new Headers(init?.headers);
|
|
16
|
+
headers.delete('Authorization');
|
|
17
|
+
headers.delete('authorization');
|
|
18
|
+
headers.set('Authorization', `Bearer ${config.oauth.refresh}`);
|
|
19
|
+
headers.set('Openai-Intent', 'conversation-edits');
|
|
20
|
+
headers.set('User-Agent', 'agi-cli');
|
|
21
|
+
|
|
22
|
+
return fetch(input, {
|
|
23
|
+
...init,
|
|
24
|
+
headers,
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function createCopilotModel(model: string, config: CopilotOAuthConfig) {
|
|
30
|
+
const customFetch = createCopilotFetch(config);
|
|
31
|
+
|
|
32
|
+
const provider = createOpenAICompatible({
|
|
33
|
+
name: 'github-copilot',
|
|
34
|
+
baseURL: COPILOT_BASE_URL,
|
|
35
|
+
apiKey: 'copilot-oauth',
|
|
36
|
+
fetch: customFetch,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return provider.chatModel(model);
|
|
40
|
+
}
|
package/src/providers/src/env.ts
CHANGED
|
@@ -71,3 +71,5 @@ export { createOpencodeModel } from './opencode-client.ts';
|
|
|
71
71
|
export type { OpencodeProviderConfig } from './opencode-client.ts';
|
|
72
72
|
export { createMoonshotModel } from './moonshot-client.ts';
|
|
73
73
|
export type { MoonshotProviderConfig } from './moonshot-client.ts';
|
|
74
|
+
export { createCopilotFetch, createCopilotModel } from './copilot-client.ts';
|
|
75
|
+
export type { CopilotOAuthConfig } from './copilot-client.ts';
|
|
@@ -3,25 +3,18 @@ import type { OAuth } from '../../types/src/index.ts';
|
|
|
3
3
|
import { refreshOpenAIToken } from '../../auth/src/openai-oauth.ts';
|
|
4
4
|
import { setAuth, getAuth } from '../../auth/src/index.ts';
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
const OPENAI_API_URL = 'https://api.openai.com/v1';
|
|
8
|
-
|
|
9
|
-
const DEFAULT_INSTRUCTIONS = `You are a helpful coding assistant. Be concise and direct.`;
|
|
6
|
+
const CODEX_API_ENDPOINT = 'https://chatgpt.com/backend-api/codex/responses';
|
|
10
7
|
|
|
11
8
|
export type OpenAIOAuthConfig = {
|
|
12
9
|
oauth: OAuth;
|
|
13
10
|
projectRoot?: string;
|
|
14
|
-
instructions?: string;
|
|
15
|
-
reasoningEffort?: 'none' | 'low' | 'medium' | 'high' | 'xhigh';
|
|
16
|
-
reasoningSummary?: 'auto' | 'detailed';
|
|
17
11
|
};
|
|
18
12
|
|
|
19
13
|
async function ensureValidToken(
|
|
20
14
|
oauth: OAuth,
|
|
21
15
|
projectRoot?: string,
|
|
22
16
|
): Promise<{ access: string; accountId?: string }> {
|
|
23
|
-
|
|
24
|
-
if (oauth.expires > Date.now() + bufferMs) {
|
|
17
|
+
if (oauth.access && oauth.expires > Date.now()) {
|
|
25
18
|
return { access: oauth.access, accountId: oauth.accountId };
|
|
26
19
|
}
|
|
27
20
|
|
|
@@ -42,78 +35,19 @@ async function ensureValidToken(
|
|
|
42
35
|
}
|
|
43
36
|
}
|
|
44
37
|
|
|
45
|
-
function stripIdsFromInput(input: unknown): unknown {
|
|
46
|
-
if (Array.isArray(input)) {
|
|
47
|
-
const filtered = input.filter((item) => {
|
|
48
|
-
if (item && typeof item === 'object' && 'type' in item) {
|
|
49
|
-
if (item.type === 'item_reference') return false;
|
|
50
|
-
}
|
|
51
|
-
return true;
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
const validCallIds = new Set<string>();
|
|
55
|
-
for (const item of filtered) {
|
|
56
|
-
if (
|
|
57
|
-
item &&
|
|
58
|
-
typeof item === 'object' &&
|
|
59
|
-
'type' in item &&
|
|
60
|
-
item.type === 'function_call' &&
|
|
61
|
-
'call_id' in item &&
|
|
62
|
-
typeof item.call_id === 'string'
|
|
63
|
-
) {
|
|
64
|
-
validCallIds.add(item.call_id);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return filtered
|
|
69
|
-
.filter((item) => {
|
|
70
|
-
if (
|
|
71
|
-
item &&
|
|
72
|
-
typeof item === 'object' &&
|
|
73
|
-
'type' in item &&
|
|
74
|
-
item.type === 'function_call_output' &&
|
|
75
|
-
'call_id' in item &&
|
|
76
|
-
typeof item.call_id === 'string'
|
|
77
|
-
) {
|
|
78
|
-
return validCallIds.has(item.call_id);
|
|
79
|
-
}
|
|
80
|
-
return true;
|
|
81
|
-
})
|
|
82
|
-
.map((item) => {
|
|
83
|
-
if (item && typeof item === 'object') {
|
|
84
|
-
const result: Record<string, unknown> = {};
|
|
85
|
-
for (const [key, value] of Object.entries(item)) {
|
|
86
|
-
if (key === 'id') continue;
|
|
87
|
-
result[key] = stripIdsFromInput(value);
|
|
88
|
-
}
|
|
89
|
-
return result;
|
|
90
|
-
}
|
|
91
|
-
return item;
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
if (input && typeof input === 'object') {
|
|
95
|
-
const result: Record<string, unknown> = {};
|
|
96
|
-
for (const [key, value] of Object.entries(input)) {
|
|
97
|
-
if (key === 'id') continue;
|
|
98
|
-
result[key] = stripIdsFromInput(value);
|
|
99
|
-
}
|
|
100
|
-
return result;
|
|
101
|
-
}
|
|
102
|
-
return input;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
38
|
function rewriteUrl(url: string): string {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
39
|
+
const parsed = new URL(url);
|
|
40
|
+
if (
|
|
41
|
+
parsed.pathname.includes('/v1/responses') ||
|
|
42
|
+
parsed.pathname.includes('/chat/completions')
|
|
43
|
+
) {
|
|
44
|
+
return CODEX_API_ENDPOINT;
|
|
110
45
|
}
|
|
111
|
-
return url
|
|
46
|
+
return url;
|
|
112
47
|
}
|
|
113
48
|
|
|
114
49
|
export function createOpenAIOAuthFetch(config: OpenAIOAuthConfig) {
|
|
115
50
|
let currentOAuth = config.oauth;
|
|
116
|
-
const instructions = config.instructions || DEFAULT_INSTRUCTIONS;
|
|
117
51
|
|
|
118
52
|
const customFetch = async (
|
|
119
53
|
input: Parameters<typeof fetch>[0],
|
|
@@ -132,57 +66,17 @@ export function createOpenAIOAuthFetch(config: OpenAIOAuthConfig) {
|
|
|
132
66
|
: input.url;
|
|
133
67
|
const targetUrl = rewriteUrl(originalUrl);
|
|
134
68
|
|
|
135
|
-
let body = init?.body;
|
|
136
|
-
if (body && typeof body === 'string') {
|
|
137
|
-
try {
|
|
138
|
-
const parsed = JSON.parse(body);
|
|
139
|
-
|
|
140
|
-
parsed.store = false;
|
|
141
|
-
// ChatGPT backend codex endpoint requires streaming
|
|
142
|
-
parsed.stream = true;
|
|
143
|
-
parsed.instructions = instructions;
|
|
144
|
-
|
|
145
|
-
if (parsed.input) {
|
|
146
|
-
parsed.input = stripIdsFromInput(parsed.input);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (!parsed.include) {
|
|
150
|
-
parsed.include = ['reasoning.encrypted_content'];
|
|
151
|
-
} else if (
|
|
152
|
-
Array.isArray(parsed.include) &&
|
|
153
|
-
!parsed.include.includes('reasoning.encrypted_content')
|
|
154
|
-
) {
|
|
155
|
-
parsed.include.push('reasoning.encrypted_content');
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const existingReasoning = parsed.reasoning || {};
|
|
159
|
-
parsed.reasoning = {
|
|
160
|
-
effort:
|
|
161
|
-
existingReasoning.effort || config.reasoningEffort || 'medium',
|
|
162
|
-
summary:
|
|
163
|
-
existingReasoning.summary || config.reasoningSummary || 'auto',
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
delete parsed.max_output_tokens;
|
|
167
|
-
delete parsed.max_completion_tokens;
|
|
168
|
-
|
|
169
|
-
body = JSON.stringify(parsed);
|
|
170
|
-
} catch {}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
69
|
const headers = new Headers(init?.headers);
|
|
174
|
-
headers.delete('
|
|
175
|
-
headers.
|
|
176
|
-
headers.set('
|
|
177
|
-
headers.set('originator', '
|
|
178
|
-
headers.set('accept', 'text/event-stream');
|
|
70
|
+
headers.delete('Authorization');
|
|
71
|
+
headers.delete('authorization');
|
|
72
|
+
headers.set('authorization', `Bearer ${accessToken}`);
|
|
73
|
+
headers.set('originator', 'agi');
|
|
179
74
|
if (accountId) {
|
|
180
|
-
headers.set('
|
|
75
|
+
headers.set('ChatGPT-Account-Id', accountId);
|
|
181
76
|
}
|
|
182
77
|
|
|
183
78
|
const response = await fetch(targetUrl, {
|
|
184
79
|
...init,
|
|
185
|
-
body,
|
|
186
80
|
headers,
|
|
187
81
|
});
|
|
188
82
|
|
|
@@ -207,9 +101,8 @@ export function createOpenAIOAuthModel(
|
|
|
207
101
|
|
|
208
102
|
const provider = createOpenAI({
|
|
209
103
|
apiKey: 'chatgpt-oauth',
|
|
210
|
-
baseURL: CHATGPT_BACKEND_URL,
|
|
211
104
|
fetch: customFetch,
|
|
212
105
|
});
|
|
213
106
|
|
|
214
|
-
return provider(model);
|
|
107
|
+
return provider.responses(model);
|
|
215
108
|
}
|
|
@@ -38,7 +38,7 @@ function simplifyPaymentError(errMsg: string): string {
|
|
|
38
38
|
return short.length < errMsg.length ? `${short}...` : errMsg;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
const DEFAULT_BASE_URL = 'https://setu.
|
|
41
|
+
const DEFAULT_BASE_URL = 'https://api.setu.nitish.sh';
|
|
42
42
|
const DEFAULT_RPC_URL = 'https://api.mainnet-beta.solana.com';
|
|
43
43
|
const DEFAULT_MAX_ATTEMPTS = 3;
|
|
44
44
|
const DEFAULT_MAX_PAYMENT_ATTEMPTS = 20;
|
|
@@ -48,6 +48,7 @@ const PREFERRED_FAST_MODELS: Partial<Record<ProviderId, string[]>> = {
|
|
|
48
48
|
'codex-mini-latest',
|
|
49
49
|
],
|
|
50
50
|
zai: ['glm-4.5-flash', 'glm-4.5-air'],
|
|
51
|
+
copilot: ['gpt-4o-mini', 'gpt-4.1-nano', 'gpt-4.1-mini'],
|
|
51
52
|
};
|
|
52
53
|
|
|
53
54
|
export function getFastModel(provider: ProviderId): string | undefined {
|
|
@@ -150,6 +151,7 @@ export function getUnderlyingProviderKey(
|
|
|
150
151
|
if (provider === 'openai') return 'openai';
|
|
151
152
|
if (provider === 'google') return 'google';
|
|
152
153
|
if (provider === 'moonshot') return 'moonshot';
|
|
154
|
+
if (provider === 'copilot') return 'openai';
|
|
153
155
|
|
|
154
156
|
const npm = getModelNpmBinding(provider, model);
|
|
155
157
|
if (npm === '@ai-sdk/anthropic') return 'anthropic';
|
|
@@ -169,6 +171,7 @@ export function getModelFamily(
|
|
|
169
171
|
if (provider === 'openai') return 'openai';
|
|
170
172
|
if (provider === 'google') return 'google';
|
|
171
173
|
if (provider === 'moonshot') return 'moonshot';
|
|
174
|
+
if (provider === 'copilot') return 'openai';
|
|
172
175
|
|
|
173
176
|
// 2) For aggregate providers, infer from model ID patterns
|
|
174
177
|
if (provider === 'openrouter' || provider === 'opencode') {
|
package/src/types/src/index.ts
CHANGED