@agi-cli/sdk 0.1.168 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agi-cli/sdk",
3
- "version": "0.1.168",
3
+ "version": "0.1.170",
4
4
  "description": "AI agent SDK for building intelligent assistants - tree-shakable and comprehensive",
5
5
  "author": "nitishxyz",
6
6
  "license": "MIT",
@@ -0,0 +1,190 @@
1
+ import { spawn } from 'node:child_process';
2
+
3
+ const CLIENT_ID = 'Ov23li8tweQw6odWQebz';
4
+ const POLLING_SAFETY_MARGIN_MS = 3000;
5
+
6
+ const DEVICE_CODE_URL = 'https://github.com/login/device/code';
7
+ const ACCESS_TOKEN_URL = 'https://github.com/login/oauth/access_token';
8
+
9
+ export type CopilotDeviceCodeResponse = {
10
+ verification_uri: string;
11
+ user_code: string;
12
+ device_code: string;
13
+ interval: number;
14
+ };
15
+
16
+ export type CopilotOAuthResult = {
17
+ access_token: string;
18
+ };
19
+
20
+ async function openBrowser(url: string) {
21
+ const platform = process.platform;
22
+ let command: string;
23
+ switch (platform) {
24
+ case 'darwin':
25
+ command = `open "${url}"`;
26
+ break;
27
+ case 'win32':
28
+ command = `start "${url}"`;
29
+ break;
30
+ default:
31
+ command = `xdg-open "${url}"`;
32
+ break;
33
+ }
34
+ return new Promise<void>((resolve, reject) => {
35
+ const child = spawn(command, [], { shell: true });
36
+ child.on('error', reject);
37
+ child.on('exit', (code) => {
38
+ if (code === 0) resolve();
39
+ else reject(new Error(`Failed to open browser (exit code ${code})`));
40
+ });
41
+ });
42
+ }
43
+
44
+ export async function requestDeviceCode(): Promise<CopilotDeviceCodeResponse> {
45
+ const response = await fetch(DEVICE_CODE_URL, {
46
+ method: 'POST',
47
+ headers: {
48
+ Accept: 'application/json',
49
+ 'Content-Type': 'application/json',
50
+ },
51
+ body: JSON.stringify({
52
+ client_id: CLIENT_ID,
53
+ scope: 'read:user',
54
+ }),
55
+ });
56
+
57
+ if (!response.ok) {
58
+ throw new Error('Failed to initiate GitHub device authorization');
59
+ }
60
+
61
+ return (await response.json()) as CopilotDeviceCodeResponse;
62
+ }
63
+
64
+ export async function pollForToken(
65
+ deviceCode: string,
66
+ interval: number,
67
+ ): Promise<string> {
68
+ while (true) {
69
+ const response = await fetch(ACCESS_TOKEN_URL, {
70
+ method: 'POST',
71
+ headers: {
72
+ Accept: 'application/json',
73
+ 'Content-Type': 'application/json',
74
+ },
75
+ body: JSON.stringify({
76
+ client_id: CLIENT_ID,
77
+ device_code: deviceCode,
78
+ grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
79
+ }),
80
+ });
81
+
82
+ if (!response.ok) {
83
+ throw new Error('Token exchange request failed');
84
+ }
85
+
86
+ const data = (await response.json()) as {
87
+ access_token?: string;
88
+ error?: string;
89
+ interval?: number;
90
+ };
91
+
92
+ if (data.access_token) {
93
+ return data.access_token;
94
+ }
95
+
96
+ if (data.error === 'authorization_pending') {
97
+ await Bun.sleep(interval * 1000 + POLLING_SAFETY_MARGIN_MS);
98
+ continue;
99
+ }
100
+
101
+ if (data.error === 'slow_down') {
102
+ let newInterval = (interval + 5) * 1000;
103
+ const serverInterval = data.interval;
104
+ if (
105
+ serverInterval &&
106
+ typeof serverInterval === 'number' &&
107
+ serverInterval > 0
108
+ ) {
109
+ newInterval = serverInterval * 1000;
110
+ }
111
+ await Bun.sleep(newInterval + POLLING_SAFETY_MARGIN_MS);
112
+ continue;
113
+ }
114
+
115
+ if (data.error) {
116
+ throw new Error(`GitHub OAuth error: ${data.error}`);
117
+ }
118
+
119
+ await Bun.sleep(interval * 1000 + POLLING_SAFETY_MARGIN_MS);
120
+ }
121
+ }
122
+
123
+ export async function authorizeCopilot(): Promise<{
124
+ verificationUri: string;
125
+ userCode: string;
126
+ deviceCode: string;
127
+ interval: number;
128
+ }> {
129
+ const deviceData = await requestDeviceCode();
130
+ return {
131
+ verificationUri: deviceData.verification_uri,
132
+ userCode: deviceData.user_code,
133
+ deviceCode: deviceData.device_code,
134
+ interval: deviceData.interval,
135
+ };
136
+ }
137
+
138
+ export type CopilotPollResult =
139
+ | { status: 'complete'; accessToken: string }
140
+ | { status: 'pending' }
141
+ | { status: 'error'; error: string };
142
+
143
+ export async function pollForTokenOnce(
144
+ deviceCode: string,
145
+ ): Promise<CopilotPollResult> {
146
+ const response = await fetch(ACCESS_TOKEN_URL, {
147
+ method: 'POST',
148
+ headers: {
149
+ Accept: 'application/json',
150
+ 'Content-Type': 'application/json',
151
+ },
152
+ body: JSON.stringify({
153
+ client_id: CLIENT_ID,
154
+ device_code: deviceCode,
155
+ grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
156
+ }),
157
+ });
158
+
159
+ if (!response.ok) {
160
+ return { status: 'error', error: 'Token exchange request failed' };
161
+ }
162
+
163
+ const data = (await response.json()) as {
164
+ access_token?: string;
165
+ error?: string;
166
+ };
167
+
168
+ if (data.access_token) {
169
+ return { status: 'complete', accessToken: data.access_token };
170
+ }
171
+
172
+ if (data.error === 'authorization_pending' || data.error === 'slow_down') {
173
+ return { status: 'pending' };
174
+ }
175
+
176
+ if (data.error) {
177
+ return { status: 'error', error: `GitHub OAuth error: ${data.error}` };
178
+ }
179
+
180
+ return { status: 'pending' };
181
+ }
182
+
183
+ export async function openCopilotAuthUrl(url: string): Promise<boolean> {
184
+ try {
185
+ await openBrowser(url);
186
+ return true;
187
+ } catch {
188
+ return false;
189
+ }
190
+ }
@@ -89,3 +89,12 @@ export {
89
89
  ensureSetuWallet,
90
90
  type WalletInfo,
91
91
  } from './wallet.ts';
92
+
93
+ export {
94
+ authorizeCopilot,
95
+ pollForToken as pollForCopilotToken,
96
+ pollForTokenOnce as pollForCopilotTokenOnce,
97
+ openCopilotAuthUrl,
98
+ type CopilotDeviceCodeResponse,
99
+ type CopilotPollResult,
100
+ } from './copilot-oauth.ts';
@@ -8,6 +8,7 @@ import {
8
8
  createSetuModel,
9
9
  createOpenAIOAuthModel,
10
10
  } from '../../../providers/src/index.ts';
11
+ import { createCopilotModel } from '../../../providers/src/copilot-client.ts';
11
12
  import type { OAuth } from '../../../types/src/index.ts';
12
13
 
13
14
  function needsResponsesApi(model: string): boolean {
@@ -33,6 +34,7 @@ export type ProviderName =
33
34
  | 'google'
34
35
  | 'openrouter'
35
36
  | 'opencode'
37
+ | 'copilot'
36
38
  | 'setu'
37
39
  | 'zai'
38
40
  | 'zai-coding'
@@ -152,6 +154,15 @@ export async function resolveModel(
152
154
  return ocOpenAI(resolvedModelId);
153
155
  }
154
156
 
157
+ if (provider === 'copilot') {
158
+ if (config.oauth) {
159
+ return createCopilotModel(model, { oauth: config.oauth });
160
+ }
161
+ throw new Error(
162
+ 'Copilot provider requires OAuth. Run `agi auth login copilot`.',
163
+ );
164
+ }
165
+
155
166
  if (provider === 'setu') {
156
167
  const privateKey = config.apiKey || process.env.SETU_PRIVATE_KEY || '';
157
168
  if (!privateKey) {
@@ -184,6 +184,12 @@ The tool will convert it automatically, but the enveloped format is still recomm
184
184
 
185
185
  ## Common Errors
186
186
 
187
+ **"Missing *** End Patch marker"**: Every patch MUST end with `*** End Patch` on its own line.
188
+ This is the most common error. Double-check your patch ends with:
189
+ ```
190
+ *** End Patch
191
+ ```
192
+
187
193
  **"Failed to find expected lines"**: The file content doesn't match your patch. Common causes:
188
194
  - Missing context lines (lines with space prefix)
189
195
  - Using `@@` line as context instead of real context lines
package/src/index.ts CHANGED
@@ -106,6 +106,11 @@ export { createOpencodeModel } from './providers/src/index.ts';
106
106
  export type { OpencodeProviderConfig } from './providers/src/index.ts';
107
107
  export { createMoonshotModel } from './providers/src/index.ts';
108
108
  export type { MoonshotProviderConfig } from './providers/src/index.ts';
109
+ export {
110
+ createCopilotFetch,
111
+ createCopilotModel,
112
+ } from './providers/src/index.ts';
113
+ export type { CopilotOAuthConfig } from './providers/src/index.ts';
109
114
 
110
115
  // =======================
111
116
  // Authentication (from internal auth module)
@@ -140,6 +145,16 @@ export {
140
145
  ensureSetuWallet,
141
146
  } from './auth/src/index.ts';
142
147
  export type { WalletInfo } from './auth/src/index.ts';
148
+ export {
149
+ authorizeCopilot,
150
+ pollForCopilotToken,
151
+ pollForCopilotTokenOnce,
152
+ openCopilotAuthUrl,
153
+ } from './auth/src/index.ts';
154
+ export type {
155
+ CopilotDeviceCodeResponse,
156
+ CopilotPollResult,
157
+ } from './auth/src/index.ts';
143
158
 
144
159
  // =======================
145
160
  // Configuration (from internal config module)
@@ -82,7 +82,6 @@ function buildSetuEntry(base: CatalogMap): ProviderCatalogEntry | null {
82
82
 
83
83
  if (!setuModels.length) return null;
84
84
 
85
- // Prefer OpenAI-family models first so defaults are stable
86
85
  setuModels.sort((a, b) => {
87
86
  const providerA = a.provider?.npm ?? '';
88
87
  const providerB = b.provider?.npm ?? '';