@miruzero/zgpt 0.1.3 → 0.1.5
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/README.md +112 -11
- package/docs/architecture.md +5 -5
- package/docs/custom-mcp-management-api.md +132 -0
- package/package.json +10 -3
- package/src/api/browser-observed.json +82 -0
- package/src/api/contracts.json +101 -0
- package/src/api/discovered.json +165 -0
- package/src/api/index.js +63 -5
- package/src/cli/sdk-dispatch.js +341 -0
- package/src/cli.js +8 -15
- package/src/connector/index.js +2 -1
- package/src/connector/mcp.js +154 -0
- package/src/connector/write.js +2 -1
- package/src/conversation/index.js +29 -0
- package/src/conversation/read.js +41 -7
- package/src/index.js +2 -3
- package/src/profile/index.js +41 -0
- package/src/project/index.js +35 -1
- package/src/session/direct-transport.js +60 -68
- package/src/settings/index.js +3 -1
- package/src/settings/web.js +38 -0
- package/src/transport/direct/http-client.js +40 -12
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
'use strict';
|
|
2
2
|
|
|
3
3
|
const https = require('node:https');
|
|
4
4
|
const fs = require('node:fs');
|
|
@@ -6,6 +6,8 @@ const path = require('node:path');
|
|
|
6
6
|
const paths = require('../../config/paths');
|
|
7
7
|
|
|
8
8
|
const RETRY_AFTER_CAP_MS = 30000;
|
|
9
|
+
const DEFAULT_HTTP_IDLE_TIMEOUT_MS = 30_000;
|
|
10
|
+
const DEFAULT_HTTP_TOTAL_TIMEOUT_MS = 45_000;
|
|
9
11
|
const DEFAULT_RATE_LIMIT_COOLDOWN_MS = 30000;
|
|
10
12
|
const MAX_RATE_LIMIT_COOLDOWN_MS = 15 * 60 * 1000;
|
|
11
13
|
|
|
@@ -141,6 +143,14 @@ exports.parseRetryAfterMs = (headers = {}) => {
|
|
|
141
143
|
};
|
|
142
144
|
const safeParse = (text) => { try { return JSON.parse(text); } catch { return null; } };
|
|
143
145
|
const rawRequest = (target, token, sessionHeaders, options, parseBody) => new Promise((resolve, reject) => {
|
|
146
|
+
let settled = false;
|
|
147
|
+
let deadlineTimer = null;
|
|
148
|
+
const finish = (next, value) => {
|
|
149
|
+
if (settled) return;
|
|
150
|
+
settled = true;
|
|
151
|
+
if (deadlineTimer) clearTimeout(deadlineTimer);
|
|
152
|
+
next(value);
|
|
153
|
+
};
|
|
144
154
|
const payload = options.body === undefined ? null : Buffer.from(JSON.stringify(options.body));
|
|
145
155
|
let headers = exports.buildHeaders(token, sessionHeaders, target, options.route);
|
|
146
156
|
if (options.headers) headers = cleanHeaders({ ...headers, ...options.headers });
|
|
@@ -151,28 +161,47 @@ const rawRequest = (target, token, sessionHeaders, options, parseBody) => new Pr
|
|
|
151
161
|
const aborted = () => {
|
|
152
162
|
const error = new Error(`ChatGPT upstream stream aborted: ${target}`);
|
|
153
163
|
error.code = 'UPSTREAM_ABORTED';
|
|
154
|
-
reject
|
|
164
|
+
finish(reject, error);
|
|
155
165
|
};
|
|
156
166
|
res.on('data', (chunk) => { options.onChunk?.(chunk); chunks.push(chunk); });
|
|
157
167
|
res.on('end', () => {
|
|
158
168
|
ended = true;
|
|
159
169
|
const text = Buffer.concat(chunks).toString('utf8');
|
|
160
170
|
const response = { status: res.statusCode, ...(parseBody ? { json: safeParse(text) } : { text }), contentType: res.headers['content-type'] || null, headers: { ...res.headers } };
|
|
161
|
-
|
|
162
|
-
|
|
171
|
+
try {
|
|
172
|
+
exports.persistResponseHeaders(sessionHeaders, response.headers, options);
|
|
173
|
+
finish(resolve, response);
|
|
174
|
+
} catch (error) { finish(reject, error); }
|
|
163
175
|
});
|
|
164
176
|
res.on('aborted', aborted);
|
|
165
|
-
res.on('error', reject);
|
|
177
|
+
res.on('error', error => finish(reject, error));
|
|
166
178
|
res.on('close', () => { if (!ended) aborted(); });
|
|
167
179
|
});
|
|
168
|
-
req.on('error', reject);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
180
|
+
req.on('error', error => finish(reject, error));
|
|
181
|
+
const idleTimeoutMs = options.idleTimeoutMs === 0 ? 0
|
|
182
|
+
: Number.isFinite(options.idleTimeoutMs) && options.idleTimeoutMs > 0
|
|
183
|
+
? options.idleTimeoutMs : DEFAULT_HTTP_IDLE_TIMEOUT_MS;
|
|
184
|
+
if (idleTimeoutMs && typeof req.setTimeout === 'function') {
|
|
185
|
+
req.setTimeout(idleTimeoutMs, () => {
|
|
186
|
+
const error = new Error(`ChatGPT upstream idle timeout: ${target}`);
|
|
172
187
|
error.code = 'UPSTREAM_IDLE_TIMEOUT';
|
|
173
|
-
req.destroy(error);
|
|
188
|
+
if (typeof req.destroy === 'function') req.destroy(error);
|
|
189
|
+
finish(reject, error);
|
|
174
190
|
});
|
|
175
191
|
}
|
|
192
|
+
// Active SSE streams may run longer than a fixed total deadline; they still
|
|
193
|
+
// have an idle timeout. Ordinary HTTP gets both an idle and a total budget.
|
|
194
|
+
const streaming = typeof options.onChunk === 'function' ||
|
|
195
|
+
/text\/event-stream/i.test(String(options.headers?.accept || ''));
|
|
196
|
+
const totalTimeoutMs = options.totalTimeoutMs === 0 ? 0
|
|
197
|
+
: Number.isFinite(options.totalTimeoutMs) && options.totalTimeoutMs > 0
|
|
198
|
+
? options.totalTimeoutMs : streaming ? 0 : DEFAULT_HTTP_TOTAL_TIMEOUT_MS;
|
|
199
|
+
if (totalTimeoutMs) deadlineTimer = setTimeout(() => {
|
|
200
|
+
const error = new Error(`ChatGPT upstream request deadline exceeded: ${target}`);
|
|
201
|
+
error.code = 'UPSTREAM_TIMEOUT';
|
|
202
|
+
if (typeof req.destroy === 'function') req.destroy(error);
|
|
203
|
+
finish(reject, error);
|
|
204
|
+
}, totalTimeoutMs);
|
|
176
205
|
if (payload) req.write(payload);
|
|
177
206
|
req.end();
|
|
178
207
|
});
|
|
@@ -187,5 +216,4 @@ const guardedRequest = async (target, token, sessionHeaders, options, parseBody)
|
|
|
187
216
|
exports.requestJson = (target, token, sessionHeaders = {}, options = {}) =>
|
|
188
217
|
guardedRequest(target, token, sessionHeaders, options, true);
|
|
189
218
|
exports.requestText = (target, token, sessionHeaders = {}, options = {}) =>
|
|
190
|
-
guardedRequest(target, token, sessionHeaders, options, false);
|
|
191
|
-
|
|
219
|
+
guardedRequest(target, token, sessionHeaders, options, false);
|