neoagent 3.2.1-beta.2 → 3.2.1-beta.4
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/extensions/chrome-browser/background.mjs +318 -88
- package/extensions/chrome-browser/http.mjs +136 -0
- package/extensions/chrome-browser/protocol.mjs +511 -89
- package/flutter_app/lib/main_chat.dart +118 -739
- package/flutter_app/lib/main_controller.dart +29 -20
- package/flutter_app/lib/main_models.dart +3 -0
- package/flutter_app/lib/main_operations.dart +334 -321
- package/flutter_app/lib/main_settings.dart +4 -3
- package/flutter_app/lib/main_shared.dart +14 -11
- package/flutter_app/lib/src/desktop_companion_actions.dart +185 -31
- package/flutter_app/lib/src/desktop_companion_io.dart +319 -86
- package/flutter_app/windows/runner/flutter_window.cpp +143 -32
- package/lib/manager.js +106 -89
- package/lib/schema_migrations.js +67 -13
- package/package.json +20 -13
- package/runtime/paths.js +49 -5
- package/server/guest_agent.js +52 -30
- package/server/http/middleware.js +24 -0
- package/server/http/routes.js +4 -6
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +30803 -30805
- package/server/routes/admin.js +1 -1
- package/server/routes/android.js +30 -34
- package/server/routes/browser.js +23 -15
- package/server/routes/desktop.js +18 -1
- package/server/routes/integrations.js +5 -1
- package/server/routes/memory.js +1 -0
- package/server/routes/settings.js +16 -5
- package/server/routes/social_reach.js +12 -3
- package/server/routes/social_video.js +4 -0
- package/server/services/ai/compaction.js +7 -2
- package/server/services/ai/history.js +44 -5
- package/server/services/ai/integrated_tools/http_request.js +8 -0
- package/server/services/ai/loop/agent_engine_core.js +347 -162
- package/server/services/ai/loop/callbacks.js +1 -0
- package/server/services/ai/loop/completion_judge.js +12 -0
- package/server/services/ai/loop/conversation_loop.js +348 -242
- package/server/services/ai/loop/messaging_delivery.js +129 -57
- package/server/services/ai/loop/model_call_guard.js +91 -0
- package/server/services/ai/loop/model_io.js +8 -41
- package/server/services/ai/loop/tool_dispatch.js +19 -8
- package/server/services/ai/loopPolicy.js +24 -19
- package/server/services/ai/model_discovery.js +227 -0
- package/server/services/ai/model_identity.js +71 -0
- package/server/services/ai/models.js +67 -162
- package/server/services/ai/providerRetry.js +17 -59
- package/server/services/ai/provider_selector.js +111 -0
- package/server/services/ai/providers/anthropic.js +2 -2
- package/server/services/ai/providers/claudeCode.js +15 -28
- package/server/services/ai/providers/githubCopilot.js +36 -16
- package/server/services/ai/providers/google.js +23 -5
- package/server/services/ai/providers/grok.js +4 -3
- package/server/services/ai/providers/grokOauth.js +13 -22
- package/server/services/ai/providers/nvidia.js +10 -5
- package/server/services/ai/providers/ollama.js +102 -82
- package/server/services/ai/providers/ollama_stream.js +142 -0
- package/server/services/ai/providers/openai.js +39 -5
- package/server/services/ai/providers/openaiCodex.js +11 -4
- package/server/services/ai/providers/openrouter.js +29 -7
- package/server/services/ai/providers/provider_error.js +36 -0
- package/server/services/ai/settings.js +26 -2
- package/server/services/ai/taskAnalysis.js +5 -1
- package/server/services/ai/terminal_reply.js +45 -0
- package/server/services/ai/toolEvidence.js +58 -29
- package/server/services/ai/tools.js +124 -111
- package/server/services/android/controller.js +770 -237
- package/server/services/android/process.js +140 -0
- package/server/services/android/sdk_download.js +143 -0
- package/server/services/android/uia.js +6 -5
- package/server/services/artifacts/store.js +24 -0
- package/server/services/browser/controller.js +736 -385
- package/server/services/browser/extension/gateway.js +40 -16
- package/server/services/browser/extension/protocol.js +12 -1
- package/server/services/browser/extension/provider.js +59 -47
- package/server/services/browser/extension/registry.js +155 -34
- package/server/services/cli/executor.js +62 -9
- package/server/services/desktop/gateway.js +41 -4
- package/server/services/desktop/protocol.js +3 -0
- package/server/services/desktop/provider.js +39 -42
- package/server/services/desktop/registry.js +137 -52
- package/server/services/integrations/figma/provider.js +78 -12
- package/server/services/integrations/github/common.js +11 -6
- package/server/services/integrations/github/provider.js +52 -53
- package/server/services/integrations/google/provider.js +55 -19
- package/server/services/integrations/home_assistant/network.js +17 -20
- package/server/services/integrations/home_assistant/provider.js +7 -5
- package/server/services/integrations/home_assistant/tools.js +17 -5
- package/server/services/integrations/http.js +51 -0
- package/server/services/integrations/manager.js +158 -53
- package/server/services/integrations/microsoft/provider.js +80 -13
- package/server/services/integrations/neoarchive/provider.js +55 -29
- package/server/services/integrations/neorecall/client.js +17 -10
- package/server/services/integrations/neorecall/provider.js +20 -11
- package/server/services/integrations/notion/provider.js +16 -13
- package/server/services/integrations/oauth_provider.js +115 -51
- package/server/services/integrations/slack/provider.js +98 -9
- package/server/services/integrations/spotify/provider.js +67 -71
- package/server/services/integrations/trello/provider.js +21 -7
- package/server/services/integrations/weather/provider.js +18 -12
- package/server/services/integrations/whatsapp/provider.js +76 -16
- package/server/services/manager.js +87 -1
- package/server/services/memory/embedding_index.js +20 -8
- package/server/services/memory/embeddings.js +151 -90
- package/server/services/memory/ingestion.js +50 -9
- package/server/services/memory/ingestion_documents.js +13 -3
- package/server/services/memory/manager.js +52 -19
- package/server/services/messaging/automation.js +84 -9
- package/server/services/messaging/http_platforms.js +33 -13
- package/server/services/messaging/inbound_queue.js +78 -24
- package/server/services/messaging/inbound_store.js +224 -0
- package/server/services/messaging/manager.js +326 -51
- package/server/services/messaging/typing_keepalive.js +5 -2
- package/server/services/messaging/whatsapp.js +22 -14
- package/server/services/network/http.js +210 -0
- package/server/services/network/safe_request.js +307 -0
- package/server/services/runtime/backends/local-vm.js +214 -66
- package/server/services/runtime/manager.js +17 -12
- package/server/services/social_reach/channels/github.js +10 -4
- package/server/services/social_reach/channels/reddit.js +4 -4
- package/server/services/social_reach/channels/rss.js +2 -2
- package/server/services/social_reach/channels/social_video.js +12 -7
- package/server/services/social_reach/channels/v2ex.js +21 -8
- package/server/services/social_reach/channels/x.js +2 -2
- package/server/services/social_reach/channels/xueqiu.js +5 -5
- package/server/services/social_reach/service.js +9 -6
- package/server/services/social_reach/utils.js +65 -14
- package/server/services/social_video/service.js +160 -50
- package/server/services/tasks/integration_runtime.js +18 -8
- package/server/services/tasks/runtime.js +39 -4
- package/server/services/voice/agentBridge.js +17 -4
- package/server/services/voice/bufferedLiveRelayAdapter.js +5 -0
- package/server/services/voice/liveSession.js +31 -0
- package/server/services/voice/openaiSpeech.js +33 -8
- package/server/services/voice/providers.js +233 -151
- package/server/services/voice/runtimeManager.js +118 -20
- package/server/services/voice/turnRunner.js +6 -0
- package/server/services/wearable/firmware_manifest.js +51 -13
- package/server/services/wearable/service.js +1 -0
- package/server/utils/abort.js +96 -0
- package/server/utils/cloud-security.js +110 -3
- package/server/utils/files.js +31 -0
- package/server/utils/image_payload.js +95 -0
- package/server/utils/retry.js +107 -0
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const { getPlatformDefinition } = require('../platforms');
|
|
4
4
|
const { SocialReachChannel } = require('./base');
|
|
5
5
|
const { normalizeAndDetectPlatform } = require('../../social_video/url');
|
|
6
|
+
const { createAbortError } = require('../../../utils/abort');
|
|
6
7
|
|
|
7
8
|
const PLATFORM_LABELS = Object.freeze({
|
|
8
9
|
youtube: 'YouTube',
|
|
@@ -26,7 +27,7 @@ class SocialVideoReachChannel extends SocialReachChannel {
|
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
async check() {
|
|
30
|
+
async check({ signal } = {}) {
|
|
30
31
|
if (!this.socialVideoService || typeof this.socialVideoService.getHealthStatus !== 'function') {
|
|
31
32
|
return {
|
|
32
33
|
...(await super.check()),
|
|
@@ -37,11 +38,14 @@ class SocialVideoReachChannel extends SocialReachChannel {
|
|
|
37
38
|
};
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
const health = await this.socialVideoService.getHealthStatus().catch((error) =>
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
const health = await this.socialVideoService.getHealthStatus({ signal }).catch((error) => {
|
|
42
|
+
if (signal?.aborted) throw createAbortError(signal);
|
|
43
|
+
return {
|
|
44
|
+
ready: false,
|
|
45
|
+
dependencies: [],
|
|
46
|
+
error: error.message || String(error),
|
|
47
|
+
};
|
|
48
|
+
});
|
|
45
49
|
const missing = (health.dependencies || [])
|
|
46
50
|
.filter((item) => !item.available)
|
|
47
51
|
.map((item) => item.name)
|
|
@@ -59,7 +63,7 @@ class SocialVideoReachChannel extends SocialReachChannel {
|
|
|
59
63
|
};
|
|
60
64
|
}
|
|
61
65
|
|
|
62
|
-
async read({ userId, url, include_frame: includeFrame, force_stt: forceStt, agentId }) {
|
|
66
|
+
async read({ userId, url, include_frame: includeFrame, force_stt: forceStt, agentId, signal }) {
|
|
63
67
|
if (!this.socialVideoService || typeof this.socialVideoService.extractFromUrl !== 'function') {
|
|
64
68
|
const error = new Error('Social video extraction is not connected right now.');
|
|
65
69
|
error.status = 503;
|
|
@@ -70,6 +74,7 @@ class SocialVideoReachChannel extends SocialReachChannel {
|
|
|
70
74
|
includeFrame: includeFrame !== false,
|
|
71
75
|
forceStt: forceStt === true,
|
|
72
76
|
agentId: agentId || null,
|
|
77
|
+
signal,
|
|
73
78
|
});
|
|
74
79
|
return {
|
|
75
80
|
...result,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const { SocialReachChannel } = require('./base');
|
|
4
4
|
const { getPlatformDefinition } = require('../platforms');
|
|
5
5
|
const { assertHttpUrl, fetchJson, normalizeLimit } = require('../utils');
|
|
6
|
+
const { createAbortError } = require('../../../utils/abort');
|
|
6
7
|
|
|
7
8
|
function shapeTopic(item = {}) {
|
|
8
9
|
const node = item.node || {};
|
|
@@ -33,17 +34,26 @@ class V2exChannel extends SocialReachChannel {
|
|
|
33
34
|
};
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
async read({ url, limit }) {
|
|
37
|
+
async read({ url, limit, signal }) {
|
|
37
38
|
const parsed = assertHttpUrl(url);
|
|
38
39
|
const match = parsed.pathname.match(/^\/t\/(\d+)/);
|
|
39
40
|
if (!match) {
|
|
40
|
-
return this.search({ query: parsed.searchParams.get('q') || 'hot', limit });
|
|
41
|
+
return this.search({ query: parsed.searchParams.get('q') || 'hot', limit, signal });
|
|
41
42
|
}
|
|
42
43
|
const topicId = match[1];
|
|
43
|
-
const topicData = await fetchJson(
|
|
44
|
+
const topicData = await fetchJson(
|
|
45
|
+
`https://www.v2ex.com/api/topics/show.json?id=${encodeURIComponent(topicId)}`,
|
|
46
|
+
{ signal },
|
|
47
|
+
);
|
|
44
48
|
const topic = Array.isArray(topicData) ? topicData[0] || {} : topicData || {};
|
|
45
|
-
const replies = await fetchJson(
|
|
46
|
-
.
|
|
49
|
+
const replies = await fetchJson(
|
|
50
|
+
`https://www.v2ex.com/api/replies/show.json?topic_id=${encodeURIComponent(topicId)}&page=1`,
|
|
51
|
+
{ signal },
|
|
52
|
+
)
|
|
53
|
+
.catch((error) => {
|
|
54
|
+
if (signal?.aborted) throw createAbortError(signal);
|
|
55
|
+
return [];
|
|
56
|
+
});
|
|
47
57
|
return {
|
|
48
58
|
platform: this.id,
|
|
49
59
|
...shapeTopic(topic),
|
|
@@ -58,11 +68,14 @@ class V2exChannel extends SocialReachChannel {
|
|
|
58
68
|
};
|
|
59
69
|
}
|
|
60
70
|
|
|
61
|
-
async search({ query, limit }) {
|
|
71
|
+
async search({ query, limit, signal }) {
|
|
62
72
|
const normalized = String(query || '').trim().toLowerCase();
|
|
63
73
|
const capped = normalizeLimit(limit, 20, 100);
|
|
64
74
|
if (normalized && normalized !== 'hot') {
|
|
65
|
-
const data = await fetchJson(
|
|
75
|
+
const data = await fetchJson(
|
|
76
|
+
`https://www.v2ex.com/api/topics/show.json?node_name=${encodeURIComponent(normalized)}&page=1`,
|
|
77
|
+
{ signal },
|
|
78
|
+
);
|
|
66
79
|
return {
|
|
67
80
|
platform: this.id,
|
|
68
81
|
query: normalized,
|
|
@@ -70,7 +83,7 @@ class V2exChannel extends SocialReachChannel {
|
|
|
70
83
|
source: 'v2ex_public_api',
|
|
71
84
|
};
|
|
72
85
|
}
|
|
73
|
-
const data = await fetchJson('https://www.v2ex.com/api/topics/hot.json');
|
|
86
|
+
const data = await fetchJson('https://www.v2ex.com/api/topics/hot.json', { signal });
|
|
74
87
|
return {
|
|
75
88
|
platform: this.id,
|
|
76
89
|
query: 'hot',
|
|
@@ -43,7 +43,7 @@ class XChannel extends SocialReachChannel {
|
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
async read({ userId, url }) {
|
|
46
|
+
async read({ userId, url, signal }) {
|
|
47
47
|
const id = extractStatusId(url);
|
|
48
48
|
if (!id) {
|
|
49
49
|
const error = new Error('A public X post URL is required.');
|
|
@@ -54,7 +54,7 @@ class XChannel extends SocialReachChannel {
|
|
|
54
54
|
const source = cookieHeaderForPlatform(userId, this.id) ? 'x_syndication_cookies' : 'x_syndication_public';
|
|
55
55
|
const data = await fetchJson(
|
|
56
56
|
`https://cdn.syndication.twimg.com/tweet-result?id=${encodeURIComponent(id)}&lang=en`,
|
|
57
|
-
{ headers: this.#headers(userId) },
|
|
57
|
+
{ headers: this.#headers(userId), signal },
|
|
58
58
|
);
|
|
59
59
|
return {
|
|
60
60
|
platform: this.id,
|
|
@@ -37,7 +37,7 @@ class XueqiuChannel extends SocialReachChannel {
|
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
async search({ userId, query, limit }) {
|
|
40
|
+
async search({ userId, query, limit, signal }) {
|
|
41
41
|
const q = String(query || '').trim();
|
|
42
42
|
if (!q) {
|
|
43
43
|
const error = new Error('query is required.');
|
|
@@ -45,7 +45,7 @@ class XueqiuChannel extends SocialReachChannel {
|
|
|
45
45
|
throw error;
|
|
46
46
|
}
|
|
47
47
|
const url = `https://xueqiu.com/stock/search.json?code=${encodeURIComponent(q)}&size=${normalizeLimit(limit, 10, 50)}`;
|
|
48
|
-
const data = await fetchJson(url, { headers: this.#headers(userId) });
|
|
48
|
+
const data = await fetchJson(url, { headers: this.#headers(userId), signal });
|
|
49
49
|
return {
|
|
50
50
|
platform: this.id,
|
|
51
51
|
query: q,
|
|
@@ -58,12 +58,12 @@ class XueqiuChannel extends SocialReachChannel {
|
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
async read({ userId, symbol, limit }) {
|
|
61
|
+
async read({ userId, symbol, limit, signal }) {
|
|
62
62
|
const raw = String(symbol || '').trim().toUpperCase();
|
|
63
63
|
if (raw) {
|
|
64
64
|
const data = await fetchJson(
|
|
65
65
|
`https://stock.xueqiu.com/v5/stock/batch/quote.json?symbol=${encodeURIComponent(raw)}`,
|
|
66
|
-
{ headers: this.#headers(userId) },
|
|
66
|
+
{ headers: this.#headers(userId), signal },
|
|
67
67
|
);
|
|
68
68
|
const quote = data.data?.items?.[0]?.quote || {};
|
|
69
69
|
return {
|
|
@@ -76,7 +76,7 @@ class XueqiuChannel extends SocialReachChannel {
|
|
|
76
76
|
}
|
|
77
77
|
const data = await fetchJson(
|
|
78
78
|
'https://xueqiu.com/v4/statuses/public_timeline_by_category.json?since_id=-1&max_id=-1&count=20&category=-1',
|
|
79
|
-
{ headers: this.#headers(userId) },
|
|
79
|
+
{ headers: this.#headers(userId), signal },
|
|
80
80
|
);
|
|
81
81
|
return {
|
|
82
82
|
platform: this.id,
|
|
@@ -5,6 +5,7 @@ const { createChannels } = require('./channels');
|
|
|
5
5
|
const { deleteCookieBundle, getCookieSummary, writeCookieBundle } = require('./store');
|
|
6
6
|
const { domainsForPlatform, getPlatformDefinition, normalizePlatformId } = require('./platforms');
|
|
7
7
|
const { assertHttpUrl } = require('./utils');
|
|
8
|
+
const { createAbortError } = require('../../utils/abort');
|
|
8
9
|
|
|
9
10
|
const COOKIE_IMPORT_PLATFORMS = new Set(['xueqiu', 'x']);
|
|
10
11
|
const MAX_IMPORTED_COOKIES = 80;
|
|
@@ -59,16 +60,17 @@ class SocialReachService {
|
|
|
59
60
|
return this.channels.find((channel) => channel.canHandleUrl(parsed.toString())) || null;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
async getStatus(userId) {
|
|
63
|
+
async getStatus(userId, options = {}) {
|
|
63
64
|
const statuses = [];
|
|
64
65
|
for (const channel of this.channels) {
|
|
65
66
|
try {
|
|
66
|
-
const status = await channel.check({ userId });
|
|
67
|
+
const status = await channel.check({ userId, signal: options.signal });
|
|
67
68
|
if (!status.cookie && channel.setupKind === 'cookies') {
|
|
68
69
|
status.cookie = getCookieSummary(userId, channel.id);
|
|
69
70
|
}
|
|
70
71
|
statuses.push(status);
|
|
71
72
|
} catch (error) {
|
|
73
|
+
if (options.signal?.aborted) throw createAbortError(options.signal);
|
|
72
74
|
statuses.push({
|
|
73
75
|
platform: channel.id,
|
|
74
76
|
label: channel.label,
|
|
@@ -87,7 +89,7 @@ class SocialReachService {
|
|
|
87
89
|
};
|
|
88
90
|
}
|
|
89
91
|
|
|
90
|
-
async read(userId, args = {}) {
|
|
92
|
+
async read(userId, args = {}, options = {}) {
|
|
91
93
|
const platform = normalizePlatformId(args.platform || '');
|
|
92
94
|
const channel = platform ? this.getChannel(platform) : this.detectChannelForUrl(args.url);
|
|
93
95
|
if (!channel) {
|
|
@@ -97,10 +99,10 @@ class SocialReachService {
|
|
|
97
99
|
error.status = 400;
|
|
98
100
|
throw error;
|
|
99
101
|
}
|
|
100
|
-
return channel.read({ ...args, userId });
|
|
102
|
+
return channel.read({ ...args, userId, signal: options.signal });
|
|
101
103
|
}
|
|
102
104
|
|
|
103
|
-
async search(userId, args = {}) {
|
|
105
|
+
async search(userId, args = {}, options = {}) {
|
|
104
106
|
const platform = normalizePlatformId(args.platform || '');
|
|
105
107
|
const channel = this.getChannel(platform);
|
|
106
108
|
if (!channel) {
|
|
@@ -108,7 +110,7 @@ class SocialReachService {
|
|
|
108
110
|
error.status = 400;
|
|
109
111
|
throw error;
|
|
110
112
|
}
|
|
111
|
-
return channel.search({ ...args, userId });
|
|
113
|
+
return channel.search({ ...args, userId, signal: options.signal });
|
|
112
114
|
}
|
|
113
115
|
|
|
114
116
|
async importCookiesFromExtension(userId, platform, options = {}) {
|
|
@@ -132,6 +134,7 @@ class SocialReachService {
|
|
|
132
134
|
}, {
|
|
133
135
|
tokenId: options.tokenId || null,
|
|
134
136
|
timeoutMs: 15000,
|
|
137
|
+
signal: options.signal,
|
|
135
138
|
});
|
|
136
139
|
const cookies = sanitizeCookies(response?.cookies || [], domains);
|
|
137
140
|
if (cookies.length === 0) {
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const cheerio = require('cheerio');
|
|
4
|
+
const { fetchResponseText } = require('../network/http');
|
|
5
|
+
const { executeSafeHttpRequest } = require('../network/safe_request');
|
|
4
6
|
|
|
5
7
|
const DEFAULT_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 '
|
|
6
8
|
+ '(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36';
|
|
9
|
+
const DEFAULT_TIMEOUT_MS = 20000;
|
|
10
|
+
const MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
|
|
7
11
|
|
|
8
12
|
function normalizeLimit(value, fallback = 10, max = 50) {
|
|
9
13
|
const n = Number(value);
|
|
@@ -31,19 +35,60 @@ function assertHttpUrl(value) {
|
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
async function fetchText(url, options = {}) {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
const {
|
|
39
|
+
lookup,
|
|
40
|
+
maxResponseBytes = MAX_RESPONSE_BYTES,
|
|
41
|
+
publicOnly = false,
|
|
42
|
+
requestImpl,
|
|
43
|
+
signal,
|
|
44
|
+
timeoutMs = DEFAULT_TIMEOUT_MS,
|
|
45
|
+
...requestOptions
|
|
46
|
+
} = options;
|
|
47
|
+
const headers = {
|
|
48
|
+
'user-agent': DEFAULT_UA,
|
|
49
|
+
accept: 'text/plain,text/html,application/xml,application/rss+xml,application/atom+xml,*/*',
|
|
50
|
+
...(options.headers || {}),
|
|
51
|
+
};
|
|
52
|
+
let status;
|
|
53
|
+
let text;
|
|
54
|
+
if (publicOnly) {
|
|
55
|
+
const result = await executeSafeHttpRequest({
|
|
56
|
+
url,
|
|
57
|
+
method: requestOptions.method || 'GET',
|
|
58
|
+
headers,
|
|
59
|
+
body: requestOptions.body,
|
|
60
|
+
timeout_ms: timeoutMs,
|
|
61
|
+
}, {
|
|
62
|
+
signal,
|
|
63
|
+
lookup,
|
|
64
|
+
requestImpl,
|
|
65
|
+
maxResponseBytes,
|
|
66
|
+
});
|
|
67
|
+
if (result.truncated) {
|
|
68
|
+
const error = new Error('Response exceeded the Social Reach safety limit.');
|
|
69
|
+
error.code = 'SOCIAL_REACH_RESPONSE_TOO_LARGE';
|
|
70
|
+
error.status = 502;
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
status = result.status;
|
|
74
|
+
text = result.body;
|
|
75
|
+
} else {
|
|
76
|
+
const result = await fetchResponseText(url, {
|
|
77
|
+
...requestOptions,
|
|
78
|
+
headers,
|
|
79
|
+
signal,
|
|
80
|
+
timeoutMs,
|
|
81
|
+
maxResponseBytes,
|
|
82
|
+
serviceName: 'Social Reach',
|
|
83
|
+
timeoutCode: 'SOCIAL_REACH_TIMEOUT',
|
|
84
|
+
tooLargeCode: 'SOCIAL_REACH_RESPONSE_TOO_LARGE',
|
|
85
|
+
});
|
|
86
|
+
status = result.response.status;
|
|
87
|
+
text = result.text;
|
|
88
|
+
}
|
|
89
|
+
if (status < 200 || status >= 300) {
|
|
90
|
+
const error = new Error(`Request failed with HTTP ${status}`);
|
|
91
|
+
error.status = status;
|
|
47
92
|
error.body = text.slice(0, 500);
|
|
48
93
|
throw error;
|
|
49
94
|
}
|
|
@@ -58,7 +103,13 @@ async function fetchJson(url, options = {}) {
|
|
|
58
103
|
...(options.headers || {}),
|
|
59
104
|
},
|
|
60
105
|
});
|
|
61
|
-
|
|
106
|
+
try {
|
|
107
|
+
return JSON.parse(text);
|
|
108
|
+
} catch (cause) {
|
|
109
|
+
const error = new Error('Social Reach returned malformed JSON.', { cause });
|
|
110
|
+
error.status = 502;
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
62
113
|
}
|
|
63
114
|
|
|
64
115
|
function htmlToText(html, maxChars = 20000) {
|