liuw15 commited on
Commit
407d303
·
1 Parent(s): f973658

添加指纹请求器,添加真正的非流

Browse files
config.json CHANGED
@@ -6,6 +6,7 @@
6
  "api": {
7
  "url": "https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:streamGenerateContent?alt=sse",
8
  "modelsUrl": "https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:fetchAvailableModels",
 
9
  "host": "daily-cloudcode-pa.sandbox.googleapis.com",
10
  "userAgent": "antigravity/1.11.3 windows/amd64"
11
  },
 
6
  "api": {
7
  "url": "https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:streamGenerateContent?alt=sse",
8
  "modelsUrl": "https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:fetchAvailableModels",
9
+ "noStreamUrl": "https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:generateContent",
10
  "host": "daily-cloudcode-pa.sandbox.googleapis.com",
11
  "userAgent": "antigravity/1.11.3 windows/amd64"
12
  },
src/AntigravityRequester.js CHANGED
@@ -22,14 +22,14 @@ class antigravityRequester {
22
  const arch = os.arch();
23
 
24
  let filename;
25
- if (platform === 'win32' && arch === 'x64') {
26
  filename = 'antigravity_requester_windows_amd64.exe';
27
- } else if (platform === 'android' && arch === 'arm64') {
28
  filename = 'antigravity_requester_android_arm64';
29
- } else if (platform === 'linux' && arch === 'x64') {
30
  filename = 'antigravity_requester_linux_amd64';
31
  } else {
32
- throw new Error(`Unsupported platform/architecture: ${platform}/${arch}. Only supports: windows/x64, android/arm64, linux/x64`);
33
  }
34
 
35
  const binPath = this.binPath || path.join(__dirname, 'bin');
@@ -57,35 +57,45 @@ class antigravityRequester {
57
  this.proc.stdin.setDefaultEncoding('utf8');
58
  }
59
 
 
 
 
 
 
 
60
  this.proc.stdout.on('data', (data) => {
61
  this.buffer += data.toString();
62
- const lines = this.buffer.split('\n');
63
- this.buffer = lines.pop();
 
 
 
64
 
65
- for (const line of lines) {
66
- if (!line.trim()) continue;
67
- try {
68
- const response = JSON.parse(line);
69
- const pending = this.pendingRequests.get(response.id);
70
- if (!pending) continue;
71
 
72
- if (pending.streamResponse) {
73
- pending.streamResponse._handleChunk(response);
74
- if (response.type === 'end' || response.type === 'error') {
75
- this.pendingRequests.delete(response.id);
76
- }
77
- } else {
78
- this.pendingRequests.delete(response.id);
79
- if (response.ok) {
80
- pending.resolve(new antigravityResponse(response));
81
  } else {
82
- pending.reject(new Error(response.error || 'Request failed'));
 
 
 
 
 
83
  }
 
 
84
  }
85
- } catch (e) {
86
- console.error('Failed to parse response:', e);
87
  }
88
- }
89
  });
90
 
91
  this.proc.stderr.on('data', (data) => {
 
22
  const arch = os.arch();
23
 
24
  let filename;
25
+ if (platform === 'win32') {
26
  filename = 'antigravity_requester_windows_amd64.exe';
27
+ } else if (platform === 'android') {
28
  filename = 'antigravity_requester_android_arm64';
29
+ } else if (platform === 'linux') {
30
  filename = 'antigravity_requester_linux_amd64';
31
  } else {
32
+ throw new Error(`Unsupported platform: ${platform}`);
33
  }
34
 
35
  const binPath = this.binPath || path.join(__dirname, 'bin');
 
57
  this.proc.stdin.setDefaultEncoding('utf8');
58
  }
59
 
60
+ // 增大 stdout 缓冲区以减少背压
61
+ if (this.proc.stdout.setEncoding) {
62
+ this.proc.stdout.setEncoding('utf8');
63
+ }
64
+
65
+ // 使用 setImmediate 异步处理数据,避免阻塞
66
  this.proc.stdout.on('data', (data) => {
67
  this.buffer += data.toString();
68
+
69
+ // 使用 setImmediate 异步处理,避免阻塞 stdout 读取
70
+ setImmediate(() => {
71
+ const lines = this.buffer.split('\n');
72
+ this.buffer = lines.pop();
73
 
74
+ for (const line of lines) {
75
+ if (!line.trim()) continue;
76
+ try {
77
+ const response = JSON.parse(line);
78
+ const pending = this.pendingRequests.get(response.id);
79
+ if (!pending) continue;
80
 
81
+ if (pending.streamResponse) {
82
+ pending.streamResponse._handleChunk(response);
83
+ if (response.type === 'end' || response.type === 'error') {
84
+ this.pendingRequests.delete(response.id);
85
+ }
 
 
 
 
86
  } else {
87
+ this.pendingRequests.delete(response.id);
88
+ if (response.ok) {
89
+ pending.resolve(new antigravityResponse(response));
90
+ } else {
91
+ pending.reject(new Error(response.error || 'Request failed'));
92
+ }
93
  }
94
+ } catch (e) {
95
+ console.error('Failed to parse response:', e, 'Line:', line);
96
  }
 
 
97
  }
98
+ });
99
  });
100
 
101
  this.proc.stderr.on('data', (data) => {
src/api/client.js CHANGED
@@ -17,6 +17,53 @@ if (config.useNativeFetch !== true) {
17
  useNativeFetch = true;
18
  }
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  export async function generateAssistantResponse(requestBody, callback) {
21
  const token = await tokenManager.getToken();
22
 
@@ -43,8 +90,7 @@ export async function generateAssistantResponse(requestBody, callback) {
43
  body: JSON.stringify(requestBody)
44
  });
45
 
46
- let thinkingStarted = false;
47
- let toolCalls = [];
48
  let buffer = '';
49
  let errorBody = '';
50
  let statusCode = null;
@@ -53,9 +99,7 @@ export async function generateAssistantResponse(requestBody, callback) {
53
  streamResponse
54
  .onStart(({ status }) => {
55
  statusCode = status;
56
- if (status === 403) {
57
- tokenManager.disableCurrentToken(token);
58
- }
59
  })
60
  .onData((chunk) => {
61
  if (statusCode !== 200) {
@@ -67,50 +111,7 @@ export async function generateAssistantResponse(requestBody, callback) {
67
  const lines = buffer.split('\n');
68
  buffer = lines.pop();
69
 
70
- for (const line of lines.filter(l => l.startsWith('data: '))) {
71
- const jsonStr = line.slice(6);
72
- try {
73
- const data = JSON.parse(jsonStr);
74
- const parts = data.response?.candidates?.[0]?.content?.parts;
75
- if (parts) {
76
- for (const part of parts) {
77
- if (part.thought === true) {
78
- if (!thinkingStarted) {
79
- callback({ type: 'thinking', content: '<think>\n' });
80
- thinkingStarted = true;
81
- }
82
- callback({ type: 'thinking', content: part.text || '' });
83
- } else if (part.text !== undefined) {
84
- if (thinkingStarted) {
85
- callback({ type: 'thinking', content: '\n</think>\n' });
86
- thinkingStarted = false;
87
- }
88
- callback({ type: 'text', content: part.text });
89
- } else if (part.functionCall) {
90
- toolCalls.push({
91
- id: part.functionCall.id || generateToolCallId(),
92
- type: 'function',
93
- function: {
94
- name: part.functionCall.name,
95
- arguments: JSON.stringify(part.functionCall.args)
96
- }
97
- });
98
- }
99
- }
100
- }
101
-
102
- if (data.response?.candidates?.[0]?.finishReason && toolCalls.length > 0) {
103
- if (thinkingStarted) {
104
- callback({ type: 'thinking', content: '\n</think>\n' });
105
- thinkingStarted = false;
106
- }
107
- callback({ type: 'tool_calls', tool_calls: toolCalls });
108
- toolCalls = [];
109
- }
110
- } catch (e) {
111
- // 忽略解析错误
112
- }
113
- }
114
  })
115
  .onEnd(() => {
116
  if (statusCode === 403) {
@@ -164,17 +165,16 @@ async function generateWithNativeFetch(url, headers, requestBody, callback, toke
164
  body: JSON.stringify(requestBody)
165
  });
166
 
167
- if (response.status === 403) {
168
- tokenManager.disableCurrentToken(token);
169
- throw new Error('该账号没有使用权限,已自动禁用');
170
- }
171
-
172
  if (!response.ok) {
173
- throw new Error(`API请求失败 (${response.status})`);
 
 
 
 
 
174
  }
175
 
176
- let thinkingStarted = false;
177
- let toolCalls = [];
178
  const reader = response.body.getReader();
179
  const decoder = new TextDecoder();
180
  let buffer = '';
@@ -187,51 +187,68 @@ async function generateWithNativeFetch(url, headers, requestBody, callback, toke
187
  const lines = buffer.split('\n');
188
  buffer = lines.pop();
189
 
190
- for (const line of lines.filter(l => l.startsWith('data: '))) {
191
- const jsonStr = line.slice(6);
192
- try {
193
- const data = JSON.parse(jsonStr);
194
- const parts = data.response?.candidates?.[0]?.content?.parts;
195
- if (parts) {
196
- for (const part of parts) {
197
- if (part.thought === true) {
198
- if (!thinkingStarted) {
199
- callback({ type: 'thinking', content: '<think>\n' });
200
- thinkingStarted = true;
201
- }
202
- callback({ type: 'thinking', content: part.text || '' });
203
- } else if (part.text !== undefined) {
204
- if (thinkingStarted) {
205
- callback({ type: 'thinking', content: '\n</think>\n' });
206
- thinkingStarted = false;
207
- }
208
- callback({ type: 'text', content: part.text });
209
- } else if (part.functionCall) {
210
- toolCalls.push({
211
- id: part.functionCall.id || generateToolCallId(),
212
- type: 'function',
213
- function: {
214
- name: part.functionCall.name,
215
- arguments: JSON.stringify(part.functionCall.args)
216
- }
217
- });
218
- }
219
- }
220
- }
221
 
222
- if (data.response?.candidates?.[0]?.finishReason && toolCalls.length > 0) {
223
- if (thinkingStarted) {
224
- callback({ type: 'thinking', content: '\n</think>\n' });
225
- thinkingStarted = false;
226
- }
227
- callback({ type: 'tool_calls', tool_calls: toolCalls });
228
- toolCalls = [];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  }
230
- } catch (e) {
231
- // 忽略解析错误
232
- }
233
  }
234
  }
 
 
 
 
 
 
235
  }
236
 
237
  export function closeRequester() {
 
17
  useNativeFetch = true;
18
  }
19
 
20
+ function processStreamLine(line, state, callback) {
21
+ if (!line.startsWith('data: ')) return;
22
+
23
+ try {
24
+ const data = JSON.parse(line.slice(6));
25
+ const parts = data.response?.candidates?.[0]?.content?.parts;
26
+
27
+ if (parts) {
28
+ for (const part of parts) {
29
+ if (part.thought === true) {
30
+ if (!state.thinkingStarted) {
31
+ callback({ type: 'thinking', content: '<think>\n' });
32
+ state.thinkingStarted = true;
33
+ }
34
+ callback({ type: 'thinking', content: part.text || '' });
35
+ } else if (part.text !== undefined) {
36
+ if (state.thinkingStarted) {
37
+ callback({ type: 'thinking', content: '\n</think>\n' });
38
+ state.thinkingStarted = false;
39
+ }
40
+ callback({ type: 'text', content: part.text });
41
+ } else if (part.functionCall) {
42
+ state.toolCalls.push({
43
+ id: part.functionCall.id || generateToolCallId(),
44
+ type: 'function',
45
+ function: {
46
+ name: part.functionCall.name,
47
+ arguments: JSON.stringify(part.functionCall.args)
48
+ }
49
+ });
50
+ }
51
+ }
52
+ }
53
+
54
+ if (data.response?.candidates?.[0]?.finishReason && state.toolCalls.length > 0) {
55
+ if (state.thinkingStarted) {
56
+ callback({ type: 'thinking', content: '\n</think>\n' });
57
+ state.thinkingStarted = false;
58
+ }
59
+ callback({ type: 'tool_calls', tool_calls: state.toolCalls });
60
+ state.toolCalls = [];
61
+ }
62
+ } catch (e) {
63
+ // 忽略解析错误
64
+ }
65
+ }
66
+
67
  export async function generateAssistantResponse(requestBody, callback) {
68
  const token = await tokenManager.getToken();
69
 
 
90
  body: JSON.stringify(requestBody)
91
  });
92
 
93
+ const state = { thinkingStarted: false, toolCalls: [] };
 
94
  let buffer = '';
95
  let errorBody = '';
96
  let statusCode = null;
 
99
  streamResponse
100
  .onStart(({ status }) => {
101
  statusCode = status;
102
+ if (status === 403) tokenManager.disableCurrentToken(token);
 
 
103
  })
104
  .onData((chunk) => {
105
  if (statusCode !== 200) {
 
111
  const lines = buffer.split('\n');
112
  buffer = lines.pop();
113
 
114
+ lines.forEach(line => processStreamLine(line, state, callback));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  })
116
  .onEnd(() => {
117
  if (statusCode === 403) {
 
165
  body: JSON.stringify(requestBody)
166
  });
167
 
 
 
 
 
 
168
  if (!response.ok) {
169
+ const errorBody = await response.text();
170
+ if (response.status === 403) {
171
+ tokenManager.disableCurrentToken(token);
172
+ throw new Error(`该账号没有使用权限,已自动禁用。错误详情: ${errorBody}`);
173
+ }
174
+ throw new Error(`API请求失败 (${response.status}): ${errorBody}`);
175
  }
176
 
177
+ const state = { thinkingStarted: false, toolCalls: [] };
 
178
  const reader = response.body.getReader();
179
  const decoder = new TextDecoder();
180
  let buffer = '';
 
187
  const lines = buffer.split('\n');
188
  buffer = lines.pop();
189
 
190
+ lines.forEach(line => processStreamLine(line, state, callback));
191
+ }
192
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
 
194
+ export async function generateAssistantResponseNoStream(requestBody) {
195
+ const token = await tokenManager.getToken();
196
+
197
+ if (!token) {
198
+ throw new Error('没有可用的token,请运行 npm run login 获取token');
199
+ }
200
+
201
+ const response = await fetch(config.api.noStreamUrl, {
202
+ method: 'POST',
203
+ headers: {
204
+ 'Host': config.api.host,
205
+ 'User-Agent': config.api.userAgent,
206
+ 'Authorization': `Bearer ${token.access_token}`,
207
+ 'Content-Type': 'application/json',
208
+ 'Accept-Encoding': 'gzip'
209
+ },
210
+ body: JSON.stringify(requestBody)
211
+ });
212
+
213
+ if (!response.ok) {
214
+ const errorBody = await response.text();
215
+ if (response.status === 403) {
216
+ tokenManager.disableCurrentToken(token);
217
+ throw new Error(`该账号没有使用权限,已自动禁用。错误详情: ${errorBody}`);
218
+ }
219
+ throw new Error(`API请求失败 (${response.status}): ${errorBody}`);
220
+ }
221
+
222
+ const data = await response.json();
223
+ //console.log(JSON.stringify(data,null,2))
224
+ const parts = data.response?.candidates?.[0]?.content?.parts || [];
225
+
226
+ let content = '';
227
+ let thinkingContent = '';
228
+ const toolCalls = [];
229
+
230
+ for (const part of parts) {
231
+ if (part.thought === true) {
232
+ thinkingContent += part.text || '';
233
+ } else if (part.text !== undefined) {
234
+ content += part.text;
235
+ } else if (part.functionCall) {
236
+ toolCalls.push({
237
+ id: part.functionCall.id || generateToolCallId(),
238
+ type: 'function',
239
+ function: {
240
+ name: part.functionCall.name,
241
+ arguments: JSON.stringify(part.functionCall.args)
242
  }
243
+ });
 
 
244
  }
245
  }
246
+
247
+ if (thinkingContent) {
248
+ content = `<think>\n${thinkingContent}\n</think>\n${content}`;
249
+ }
250
+
251
+ return { content, toolCalls };
252
  }
253
 
254
  export function closeRequester() {
src/bin/antigravity_requester_android_arm64 CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:39d953841d55d4673f77fb2a225d76bc202569a3726ed476a7daaa218e3d4bd6
3
  size 8192353
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:198c940794a575933d68803f15280658a3674eafb45c90e76b6c8f7cf360da94
3
  size 8192353
src/bin/antigravity_requester_linux_amd64 CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:9f362ec3859dc5d189f67de7d7426ab88e9c84819697dca2e3e7ac3d6b1a0846
3
  size 7930040
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1375c1b833468b4b04998753f76697412920b1d32aeddfc8ba6daaf98723d63f
3
  size 7930040
src/bin/antigravity_requester_windows_amd64.exe CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:a755b4a7cb2bdadd278c5701c13e3d9bcd03d0051df66f6b5a4745e81f229a77
3
- size 8096768
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e957f08b72e519e470641ab327dcd59514d6fe5005b238379e501672fa67bb41
3
+ size 8097792
src/config/config.js CHANGED
@@ -6,6 +6,7 @@ const defaultConfig = {
6
  api: {
7
  url: 'https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:streamGenerateContent?alt=sse',
8
  modelsUrl: 'https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:fetchAvailableModels',
 
9
  host: 'daily-cloudcode-pa.sandbox.googleapis.com',
10
  userAgent: 'antigravity/1.11.3 windows/amd64'
11
  },
 
6
  api: {
7
  url: 'https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:streamGenerateContent?alt=sse',
8
  modelsUrl: 'https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:fetchAvailableModels',
9
+ noStreamUrl: "https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:generateContent",
10
  host: 'daily-cloudcode-pa.sandbox.googleapis.com',
11
  userAgent: 'antigravity/1.11.3 windows/amd64'
12
  },
src/server/index.js CHANGED
@@ -1,5 +1,5 @@
1
  import express from 'express';
2
- import { generateAssistantResponse, getAvailableModels, closeRequester } from '../api/client.js';
3
  import { generateRequestBody } from '../utils/utils.js';
4
  import logger from '../utils/logger.js';
5
  import config from '../config/config.js';
@@ -99,17 +99,9 @@ app.post('/v1/chat/completions', async (req, res) => {
99
  res.write('data: [DONE]\n\n');
100
  res.end();
101
  } else {
102
- let fullContent = '';
103
- let toolCalls = [];
104
- await generateAssistantResponse(requestBody, (data) => {
105
- if (data.type === 'tool_calls') {
106
- toolCalls = data.tool_calls;
107
- } else {
108
- fullContent += data.content;
109
- }
110
- });
111
 
112
- const message = { role: 'assistant', content: fullContent };
113
  if (toolCalls.length > 0) {
114
  message.tool_calls = toolCalls;
115
  }
@@ -152,7 +144,17 @@ app.post('/v1/chat/completions', async (req, res) => {
152
  res.write('data: [DONE]\n\n');
153
  res.end();
154
  } else {
155
- res.status(500).json({ error: error.message });
 
 
 
 
 
 
 
 
 
 
156
  }
157
  }
158
  }
 
1
  import express from 'express';
2
+ import { generateAssistantResponse, generateAssistantResponseNoStream, getAvailableModels, closeRequester } from '../api/client.js';
3
  import { generateRequestBody } from '../utils/utils.js';
4
  import logger from '../utils/logger.js';
5
  import config from '../config/config.js';
 
99
  res.write('data: [DONE]\n\n');
100
  res.end();
101
  } else {
102
+ const { content, toolCalls } = await generateAssistantResponseNoStream(requestBody);
 
 
 
 
 
 
 
 
103
 
104
+ const message = { role: 'assistant', content };
105
  if (toolCalls.length > 0) {
106
  message.tool_calls = toolCalls;
107
  }
 
144
  res.write('data: [DONE]\n\n');
145
  res.end();
146
  } else {
147
+ res.json({
148
+ id: `chatcmpl-${Date.now()}`,
149
+ object: 'chat.completion',
150
+ created: Math.floor(Date.now() / 1000),
151
+ model,
152
+ choices: [{
153
+ index: 0,
154
+ message: { role: 'assistant', content: `错误: ${error.message}` },
155
+ finish_reason: 'stop'
156
+ }]
157
+ });
158
  }
159
  }
160
  }
test/test-request.js ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { generateRequestBody } from '../src/utils/utils.js';
2
+ import tokenManager from '../src/auth/token_manager.js';
3
+ import config from '../src/config/config.js';
4
+
5
+ async function testRequest() {
6
+ try {
7
+ const token = await tokenManager.getToken();
8
+
9
+ const tools = [{
10
+ type: 'function',
11
+ function: {
12
+ name: 'get_weather',
13
+ description: '获取天气信息',
14
+ parameters: {
15
+ type: 'object',
16
+ properties: {
17
+ location: { type: 'string', description: '城市名称' }
18
+ },
19
+ required: ['location']
20
+ }
21
+ }
22
+ }];
23
+
24
+ const requestBody = await generateRequestBody(
25
+ [{ role: 'user', content: '你是谁?' }],
26
+ 'gemini-3-pro-high',
27
+ {},
28
+ []
29
+ //tools
30
+ );
31
+
32
+ const response = await fetch('https://daily-cloudcode-pa.sandbox.googleapis.com/v1internal:generateContent', {
33
+ method: 'POST',
34
+ headers: {
35
+ 'Host': config.api.host,
36
+ 'User-Agent': config.api.userAgent,
37
+ 'Authorization': `Bearer ${token.access_token}`,
38
+ 'Content-Type': 'application/json',
39
+ 'Accept-Encoding': 'gzip'
40
+ },
41
+ body: JSON.stringify(requestBody)
42
+ });
43
+
44
+ const result = await response.json();
45
+ console.log(JSON.stringify(result, null, 2));
46
+ } catch (error) {
47
+ console.error('Error:', error.message);
48
+ }
49
+ }
50
+
51
+ testRequest();