@deadragdoll/tellymcp 0.0.1
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/.env.example.client +93 -0
- package/.env.example.gateway +120 -0
- package/CHANGELOG.md +155 -0
- package/LICENSE +21 -0
- package/README-ru.md +338 -0
- package/README.md +1262 -0
- package/STANDALONE-ru.md +266 -0
- package/STANDALONE.md +266 -0
- package/TOOLS.md +1296 -0
- package/config/templates/env.both.template +83 -0
- package/config/templates/env.client.template +60 -0
- package/config/templates/env.gateway.template +82 -0
- package/dist/cli.js +636 -0
- package/dist/index.js +17 -0
- package/dist/lib/logfeed/store.js +52 -0
- package/dist/lib/middlewares/tracer.js +172 -0
- package/dist/lib/mixins/db.js +267 -0
- package/dist/lib/mixins/logfeed.js +34 -0
- package/dist/lib/mixins/session.errors.js +142 -0
- package/dist/lib/moleculer.js +2 -0
- package/dist/lib/trace.js +147 -0
- package/dist/lib/traceContext.js +116 -0
- package/dist/moleculer.config.js +274 -0
- package/dist/services/features/telegram-mcp/approval.service.js +33 -0
- package/dist/services/features/telegram-mcp/browser.service.js +42 -0
- package/dist/services/features/telegram-mcp/collaboration.service.js +53 -0
- package/dist/services/features/telegram-mcp/ensuredb.service.js +337 -0
- package/dist/services/features/telegram-mcp/gateway-delivery.service.js +378 -0
- package/dist/services/features/telegram-mcp/gateway-loopback.js +10 -0
- package/dist/services/features/telegram-mcp/gateway-rmq.service.js +294 -0
- package/dist/services/features/telegram-mcp/gateway-socket.service.js +1463 -0
- package/dist/services/features/telegram-mcp/gateway.service.js +1141 -0
- package/dist/services/features/telegram-mcp/inbox.service.js +33 -0
- package/dist/services/features/telegram-mcp/mcp-http.service.js +76 -0
- package/dist/services/features/telegram-mcp/mcp-server.service.js +127 -0
- package/dist/services/features/telegram-mcp/notify.service.js +33 -0
- package/dist/services/features/telegram-mcp/pair.service.js +33 -0
- package/dist/services/features/telegram-mcp/runtime.service.js +36 -0
- package/dist/services/features/telegram-mcp/session-context.service.js +33 -0
- package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +103 -0
- package/dist/services/features/telegram-mcp/src/app/config/env.js +317 -0
- package/dist/services/features/telegram-mcp/src/app/http.js +774 -0
- package/dist/services/features/telegram-mcp/src/app/index.js +2 -0
- package/dist/services/features/telegram-mcp/src/app/providers/mcp/server.js +13 -0
- package/dist/services/features/telegram-mcp/src/app/providers/redis/client.js +18 -0
- package/dist/services/features/telegram-mcp/src/app/webapp/assets.js +740 -0
- package/dist/services/features/telegram-mcp/src/app/webapp/auth.js +267 -0
- package/dist/services/features/telegram-mcp/src/app/webapp/relay.js +69 -0
- package/dist/services/features/telegram-mcp/src/app/webapp/tmux.js +9 -0
- package/dist/services/features/telegram-mcp/src/entities/auth/model/types.js +2 -0
- package/dist/services/features/telegram-mcp/src/entities/browser/model/types.js +2 -0
- package/dist/services/features/telegram-mcp/src/entities/collaboration/model/types.js +2 -0
- package/dist/services/features/telegram-mcp/src/entities/inbox/model/types.js +2 -0
- package/dist/services/features/telegram-mcp/src/entities/request/model/schema.js +545 -0
- package/dist/services/features/telegram-mcp/src/entities/request/model/types.js +2 -0
- package/dist/services/features/telegram-mcp/src/entities/session/model/types.js +2 -0
- package/dist/services/features/telegram-mcp/src/features/ask-user/model/askUserTelegram.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserClearLogsTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserClickTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserCloseTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserComputedStyleTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserConsoleTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserDomTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserErrorsTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserFillTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserNetworkFailuresTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserOpenTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserPressTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserReloadTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserScreenshotTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +689 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserWaitForTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserWaitForUrlTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/backend.js +2 -0
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/collaborationService.js +26 -0
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/localCollaborationBackend.js +390 -0
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileService.js +102 -0
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerNoteTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayCollaborationBackend.js +69 -0
- package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayHttpService.js +657 -0
- package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayReplyResolution.js +17 -0
- package/dist/services/features/telegram-mcp/src/features/inbox/model/deleteTelegramInboxMessageTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/inbox/model/getTelegramInboxCountTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/inbox/model/getTelegramInboxTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/inbox/model/inboxService.js +77 -0
- package/dist/services/features/telegram-mcp/src/features/notify/model/notifyService.js +93 -0
- package/dist/services/features/telegram-mcp/src/features/notify/model/notifyTelegramTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/pair-session/model/clearSessionPairingTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/pair-session/model/createSessionPairCodeTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/pair-session/model/generatePairCode.js +202 -0
- package/dist/services/features/telegram-mcp/src/features/session-context/model/clearSessionContextTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/session-context/model/getSessionContextTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/session-context/model/getTmuxTargetTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/session-context/model/renameSessionTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/session-context/model/sessionContextService.js +409 -0
- package/dist/services/features/telegram-mcp/src/features/session-context/model/setSessionContextTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/session-context/model/setTmuxTargetTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/tools-sync/model/refreshToolsMarkdownService.js +123 -0
- package/dist/services/features/telegram-mcp/src/features/tools-sync/model/refreshToolsMarkdownTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/processes/human-approval/model/orchestrator.js +243 -0
- package/dist/services/features/telegram-mcp/src/shared/api/storage/contract.js +2 -0
- package/dist/services/features/telegram-mcp/src/shared/api/tool-registry/registry.js +8 -0
- package/dist/services/features/telegram-mcp/src/shared/api/tool-registry/types.js +2 -0
- package/dist/services/features/telegram-mcp/src/shared/api/transport/contract.js +2 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/object-storage/minioExchangeStore.js +86 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/redis/stateStore.js +436 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/collabSemantics.js +21 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/collabUi.js +87 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/messageFormat.js +60 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/proxyFetch.js +46 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transport.js +6534 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/tmux/client.js +280 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/ids/ids.js +34 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/logger/logger.js +68 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/project-identity/projectIdentity.js +223 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/redact-secrets/redactSecrets.js +22 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/truncate/truncate.js +12 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/version/versionHandshake.js +124 -0
- package/dist/services/features/telegram-mcp/src/shared/types/common.js +2 -0
- package/dist/services/features/telegram-mcp/standalone-http.service.js +113 -0
- package/dist/services/features/telegram-mcp/tools-sync.service.js +33 -0
- package/package.json +110 -0
- package/scripts/postinstall.js +60 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RedisStateStore = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const storage_redis_1 = require("@grammyjs/storage-redis");
|
|
6
|
+
const redactSecrets_1 = require("../../lib/redact-secrets/redactSecrets");
|
|
7
|
+
const KEY_PREFIX = "telegram-mcp";
|
|
8
|
+
function sessionKey(sessionId) {
|
|
9
|
+
return `${KEY_PREFIX}:session:${sessionId}`;
|
|
10
|
+
}
|
|
11
|
+
function bindingKey(sessionId) {
|
|
12
|
+
return `${KEY_PREFIX}:binding:${sessionId}`;
|
|
13
|
+
}
|
|
14
|
+
function pairCodeKey(code) {
|
|
15
|
+
return `${KEY_PREFIX}:pair-code:${code}`;
|
|
16
|
+
}
|
|
17
|
+
function requestKey(requestId) {
|
|
18
|
+
return `${KEY_PREFIX}:request:${requestId}`;
|
|
19
|
+
}
|
|
20
|
+
function principalSessionsKey(principal) {
|
|
21
|
+
return `${KEY_PREFIX}:principal:${principal.telegramChatId}:${principal.telegramUserId}:sessions`;
|
|
22
|
+
}
|
|
23
|
+
function principalActiveSessionKey(principal) {
|
|
24
|
+
return `${KEY_PREFIX}:principal:${principal.telegramChatId}:${principal.telegramUserId}:active-session`;
|
|
25
|
+
}
|
|
26
|
+
function principalActiveSessionMatchPattern(telegramUserId) {
|
|
27
|
+
return `${KEY_PREFIX}:principal:*:${telegramUserId}:active-session`;
|
|
28
|
+
}
|
|
29
|
+
function inboxListKey(sessionId) {
|
|
30
|
+
return `${KEY_PREFIX}:inbox:${sessionId}`;
|
|
31
|
+
}
|
|
32
|
+
function inboxMessageKey(sessionId, messageId) {
|
|
33
|
+
return `${KEY_PREFIX}:inbox-message:${sessionId}:${messageId}`;
|
|
34
|
+
}
|
|
35
|
+
function menuPayloadKey(key) {
|
|
36
|
+
return `${KEY_PREFIX}:menu-payload:${key}`;
|
|
37
|
+
}
|
|
38
|
+
function gatewayClientUuidKey() {
|
|
39
|
+
return `${KEY_PREFIX}:gateway:client-uuid`;
|
|
40
|
+
}
|
|
41
|
+
function outgoingDeliveryNoticeKey(deliveryUuid) {
|
|
42
|
+
return `${KEY_PREFIX}:gateway:outgoing-delivery:${deliveryUuid}`;
|
|
43
|
+
}
|
|
44
|
+
function projectMenuViewStateKey(projectUuid, sessionId) {
|
|
45
|
+
return `${KEY_PREFIX}:project-menu-view:${projectUuid}:${sessionId}`;
|
|
46
|
+
}
|
|
47
|
+
function xchangeFileMetaKey(sessionId, filePath) {
|
|
48
|
+
const fingerprint = (0, node_crypto_1.createHash)("sha1").update(filePath).digest("hex");
|
|
49
|
+
return `${KEY_PREFIX}:xchange-file:${sessionId}:${fingerprint}`;
|
|
50
|
+
}
|
|
51
|
+
function activeRequestKey() {
|
|
52
|
+
return `${KEY_PREFIX}:pending:active`;
|
|
53
|
+
}
|
|
54
|
+
function queueKey() {
|
|
55
|
+
return `${KEY_PREFIX}:pending:queue`;
|
|
56
|
+
}
|
|
57
|
+
function parseJson(raw) {
|
|
58
|
+
if (!raw) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
return JSON.parse(raw);
|
|
62
|
+
}
|
|
63
|
+
class RedisStateStore {
|
|
64
|
+
redis;
|
|
65
|
+
sessionAdapter;
|
|
66
|
+
constructor(redis) {
|
|
67
|
+
this.redis = redis;
|
|
68
|
+
this.sessionAdapter = new storage_redis_1.RedisAdapter({ instance: redis });
|
|
69
|
+
}
|
|
70
|
+
async getSession(sessionId) {
|
|
71
|
+
const session = await this.sessionAdapter.read(sessionKey(sessionId));
|
|
72
|
+
return session ?? null;
|
|
73
|
+
}
|
|
74
|
+
async listSessions() {
|
|
75
|
+
const sessions = [];
|
|
76
|
+
let cursor = "0";
|
|
77
|
+
do {
|
|
78
|
+
const [nextCursor, keys] = await this.redis.scan(cursor, "MATCH", `${KEY_PREFIX}:session:*`, "COUNT", 100);
|
|
79
|
+
cursor = nextCursor;
|
|
80
|
+
if (keys.length === 0) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const rows = await this.redis.mget(...keys);
|
|
84
|
+
for (const row of rows) {
|
|
85
|
+
const session = parseJson(row);
|
|
86
|
+
if (session) {
|
|
87
|
+
sessions.push(session);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} while (cursor !== "0");
|
|
91
|
+
return sessions;
|
|
92
|
+
}
|
|
93
|
+
async setSession(session) {
|
|
94
|
+
await this.sessionAdapter.write(sessionKey(session.sessionId), session);
|
|
95
|
+
}
|
|
96
|
+
async clearSession(sessionId) {
|
|
97
|
+
await this.unlinkPartnerSession(sessionId);
|
|
98
|
+
await this.clearInboxMessages(sessionId);
|
|
99
|
+
await this.clearXchangeFileMetas(sessionId);
|
|
100
|
+
await this.clearProjectMenuViewStates(sessionId);
|
|
101
|
+
await this.clearOutgoingDeliveryNoticesForSession(sessionId);
|
|
102
|
+
await this.sessionAdapter.delete(sessionKey(sessionId));
|
|
103
|
+
}
|
|
104
|
+
async createPairCode(record, ttlSeconds) {
|
|
105
|
+
const result = await this.redis.set(pairCodeKey(record.code), JSON.stringify(record), "EX", ttlSeconds, "NX");
|
|
106
|
+
return result === "OK";
|
|
107
|
+
}
|
|
108
|
+
async consumePairCode(code) {
|
|
109
|
+
const raw = await this.redis.getdel(pairCodeKey(code));
|
|
110
|
+
return parseJson(raw);
|
|
111
|
+
}
|
|
112
|
+
async getBinding(sessionId) {
|
|
113
|
+
const raw = await this.redis.get(bindingKey(sessionId));
|
|
114
|
+
return parseJson(raw);
|
|
115
|
+
}
|
|
116
|
+
async setBinding(binding) {
|
|
117
|
+
const previous = await this.getBinding(binding.sessionId);
|
|
118
|
+
if (previous &&
|
|
119
|
+
(previous.telegramChatId !== binding.telegramChatId ||
|
|
120
|
+
previous.telegramUserId !== binding.telegramUserId)) {
|
|
121
|
+
await this.detachSessionFromPrincipal(previous, binding.sessionId);
|
|
122
|
+
}
|
|
123
|
+
await this.redis.set(bindingKey(binding.sessionId), JSON.stringify(binding));
|
|
124
|
+
await this.redis.sadd(principalSessionsKey(binding), binding.sessionId);
|
|
125
|
+
await this.redis.set(principalActiveSessionKey(binding), binding.sessionId);
|
|
126
|
+
}
|
|
127
|
+
async clearBinding(sessionId) {
|
|
128
|
+
await this.unlinkPartnerSession(sessionId);
|
|
129
|
+
const existing = await this.getBinding(sessionId);
|
|
130
|
+
await this.redis.del(bindingKey(sessionId));
|
|
131
|
+
if (existing) {
|
|
132
|
+
await this.detachSessionFromPrincipal(existing, sessionId);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async getActiveSessionIdForPrincipal(principal) {
|
|
136
|
+
return (await this.redis.get(principalActiveSessionKey(principal))) ?? null;
|
|
137
|
+
}
|
|
138
|
+
async getActiveSessionIdForTelegramUser(telegramUserId) {
|
|
139
|
+
let cursor = "0";
|
|
140
|
+
do {
|
|
141
|
+
const [nextCursor, keys] = await this.redis.scan(cursor, "MATCH", principalActiveSessionMatchPattern(telegramUserId), "COUNT", 50);
|
|
142
|
+
cursor = nextCursor;
|
|
143
|
+
if (keys.length === 0) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
for (const key of keys) {
|
|
147
|
+
const sessionId = await this.redis.get(key);
|
|
148
|
+
if (sessionId) {
|
|
149
|
+
return sessionId;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
} while (cursor !== "0");
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
async setActiveSessionIdForPrincipal(principal, sessionId) {
|
|
156
|
+
const isBound = await this.redis.sismember(principalSessionsKey(principal), sessionId);
|
|
157
|
+
if (!isBound) {
|
|
158
|
+
await this.redis.sadd(principalSessionsKey(principal), sessionId);
|
|
159
|
+
}
|
|
160
|
+
await this.redis.set(principalActiveSessionKey(principal), sessionId);
|
|
161
|
+
}
|
|
162
|
+
async listBoundSessionIdsForPrincipal(principal) {
|
|
163
|
+
return this.redis.smembers(principalSessionsKey(principal));
|
|
164
|
+
}
|
|
165
|
+
async resetRuntimeState() {
|
|
166
|
+
await this.redis.del(activeRequestKey());
|
|
167
|
+
await this.redis.del(queueKey());
|
|
168
|
+
}
|
|
169
|
+
async getActive() {
|
|
170
|
+
const activeId = await this.redis.get(activeRequestKey());
|
|
171
|
+
if (!activeId) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
const raw = await this.redis.get(requestKey(activeId));
|
|
175
|
+
return parseJson(raw);
|
|
176
|
+
}
|
|
177
|
+
async createPending(request) {
|
|
178
|
+
await this.redis.set(activeRequestKey(), request.requestId);
|
|
179
|
+
await this.redis.set(requestKey(request.requestId), JSON.stringify(request));
|
|
180
|
+
}
|
|
181
|
+
async updatePending(request) {
|
|
182
|
+
await this.redis.set(requestKey(request.requestId), JSON.stringify(request));
|
|
183
|
+
}
|
|
184
|
+
async resolvePending(requestId, resolution) {
|
|
185
|
+
const raw = await this.redis.get(requestKey(requestId));
|
|
186
|
+
const current = parseJson(raw);
|
|
187
|
+
if (!current) {
|
|
188
|
+
await this.redis.del(activeRequestKey());
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const next = {
|
|
192
|
+
...current,
|
|
193
|
+
status: resolution.status,
|
|
194
|
+
...(resolution.answer
|
|
195
|
+
? { answer: (0, redactSecrets_1.redactSecrets)(resolution.answer) }
|
|
196
|
+
: {}),
|
|
197
|
+
...(resolution.receivedAt ? { receivedAt: resolution.receivedAt } : {}),
|
|
198
|
+
...(resolution.fallbackUsed
|
|
199
|
+
? { fallbackIfTimeout: resolution.fallbackUsed }
|
|
200
|
+
: {}),
|
|
201
|
+
};
|
|
202
|
+
await this.redis.set(requestKey(requestId), JSON.stringify(next));
|
|
203
|
+
await this.redis.del(activeRequestKey());
|
|
204
|
+
}
|
|
205
|
+
async enqueue(request) {
|
|
206
|
+
await this.redis.set(requestKey(request.requestId), JSON.stringify(request));
|
|
207
|
+
await this.redis.rpush(queueKey(), request.requestId);
|
|
208
|
+
}
|
|
209
|
+
async dequeueNext() {
|
|
210
|
+
const requestId = await this.redis.lpop(queueKey());
|
|
211
|
+
if (!requestId) {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
const raw = await this.redis.get(requestKey(requestId));
|
|
215
|
+
return parseJson(raw);
|
|
216
|
+
}
|
|
217
|
+
async createInboxMessage(message) {
|
|
218
|
+
await this.redis.set(inboxMessageKey(message.sessionId, message.id), JSON.stringify(message));
|
|
219
|
+
await this.redis.lpush(inboxListKey(message.sessionId), message.id);
|
|
220
|
+
}
|
|
221
|
+
async listInboxMessages(sessionId, limit) {
|
|
222
|
+
const ids = await this.redis.lrange(inboxListKey(sessionId), 0, limit - 1);
|
|
223
|
+
if (ids.length === 0) {
|
|
224
|
+
return [];
|
|
225
|
+
}
|
|
226
|
+
const rows = await this.redis.mget(...ids.map((messageId) => inboxMessageKey(sessionId, messageId)));
|
|
227
|
+
return rows
|
|
228
|
+
.map((row) => parseJson(row))
|
|
229
|
+
.filter((row) => row !== null);
|
|
230
|
+
}
|
|
231
|
+
async countInboxMessages(sessionId) {
|
|
232
|
+
return this.redis.llen(inboxListKey(sessionId));
|
|
233
|
+
}
|
|
234
|
+
async getInboxMessage(sessionId, messageId) {
|
|
235
|
+
const raw = await this.redis.get(inboxMessageKey(sessionId, messageId));
|
|
236
|
+
return parseJson(raw);
|
|
237
|
+
}
|
|
238
|
+
async deleteInboxMessage(sessionId, messageId) {
|
|
239
|
+
const deletedCount = await this.redis.del(inboxMessageKey(sessionId, messageId));
|
|
240
|
+
await this.redis.lrem(inboxListKey(sessionId), 0, messageId);
|
|
241
|
+
return deletedCount > 0;
|
|
242
|
+
}
|
|
243
|
+
async createMenuPayload(record, ttlSeconds) {
|
|
244
|
+
await this.redis.set(menuPayloadKey(record.key), JSON.stringify(record), "EX", ttlSeconds);
|
|
245
|
+
}
|
|
246
|
+
async getMenuPayload(key) {
|
|
247
|
+
const raw = await this.redis.get(menuPayloadKey(key));
|
|
248
|
+
return parseJson(raw);
|
|
249
|
+
}
|
|
250
|
+
async setXchangeFileMeta(meta) {
|
|
251
|
+
await this.redis.set(xchangeFileMetaKey(meta.sessionId, meta.filePath), JSON.stringify(meta));
|
|
252
|
+
}
|
|
253
|
+
async listXchangeFileMetas(sessionId) {
|
|
254
|
+
const records = [];
|
|
255
|
+
let cursor = "0";
|
|
256
|
+
do {
|
|
257
|
+
const [nextCursor, keys] = await this.redis.scan(cursor, "MATCH", `${KEY_PREFIX}:xchange-file:${sessionId}:*`, "COUNT", 200);
|
|
258
|
+
cursor = nextCursor;
|
|
259
|
+
if (keys.length === 0) {
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
const rows = await this.redis.mget(...keys);
|
|
263
|
+
for (const row of rows) {
|
|
264
|
+
const meta = parseJson(row);
|
|
265
|
+
if (meta) {
|
|
266
|
+
records.push(meta);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
} while (cursor !== "0");
|
|
270
|
+
return records.sort((left, right) => right.uploadedAt.localeCompare(left.uploadedAt));
|
|
271
|
+
}
|
|
272
|
+
async getXchangeFileMeta(sessionId, filePath) {
|
|
273
|
+
const raw = await this.redis.get(xchangeFileMetaKey(sessionId, filePath));
|
|
274
|
+
return parseJson(raw);
|
|
275
|
+
}
|
|
276
|
+
async deleteXchangeFileMeta(sessionId, filePath) {
|
|
277
|
+
const deletedCount = await this.redis.del(xchangeFileMetaKey(sessionId, filePath));
|
|
278
|
+
return deletedCount > 0;
|
|
279
|
+
}
|
|
280
|
+
async pruneAll() {
|
|
281
|
+
let cursor = "0";
|
|
282
|
+
let deletedKeys = 0;
|
|
283
|
+
do {
|
|
284
|
+
const [nextCursor, keys] = await this.redis.scan(cursor, "MATCH", `${KEY_PREFIX}:*`, "COUNT", 200);
|
|
285
|
+
cursor = nextCursor;
|
|
286
|
+
if (keys.length === 0) {
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
deletedKeys += await this.redis.del(...keys);
|
|
290
|
+
} while (cursor !== "0");
|
|
291
|
+
return { deletedKeys };
|
|
292
|
+
}
|
|
293
|
+
async getGatewayClientUuid() {
|
|
294
|
+
return (await this.redis.get(gatewayClientUuidKey())) ?? null;
|
|
295
|
+
}
|
|
296
|
+
async setGatewayClientUuid(clientUuid) {
|
|
297
|
+
await this.redis.set(gatewayClientUuidKey(), clientUuid);
|
|
298
|
+
}
|
|
299
|
+
async setProjectMenuViewState(state) {
|
|
300
|
+
await this.redis.set(projectMenuViewStateKey(state.projectUuid, state.sessionId), JSON.stringify(state));
|
|
301
|
+
}
|
|
302
|
+
async listProjectMenuViewStates(projectUuid) {
|
|
303
|
+
const states = [];
|
|
304
|
+
let cursor = "0";
|
|
305
|
+
const pattern = `${KEY_PREFIX}:project-menu-view:${projectUuid}:*`;
|
|
306
|
+
do {
|
|
307
|
+
const [nextCursor, keys] = await this.redis.scan(cursor, "MATCH", pattern, "COUNT", 100);
|
|
308
|
+
cursor = nextCursor;
|
|
309
|
+
if (keys.length === 0) {
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
const rows = await this.redis.mget(...keys);
|
|
313
|
+
for (const row of rows) {
|
|
314
|
+
const state = parseJson(row);
|
|
315
|
+
if (state) {
|
|
316
|
+
states.push(state);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
} while (cursor !== "0");
|
|
320
|
+
return states;
|
|
321
|
+
}
|
|
322
|
+
async deleteProjectMenuViewState(sessionId, projectUuid) {
|
|
323
|
+
return ((await this.redis.del(projectMenuViewStateKey(projectUuid, sessionId))) > 0);
|
|
324
|
+
}
|
|
325
|
+
async setOutgoingDeliveryNotice(notice) {
|
|
326
|
+
await this.redis.set(outgoingDeliveryNoticeKey(notice.deliveryUuid), JSON.stringify(notice));
|
|
327
|
+
}
|
|
328
|
+
async listOutgoingDeliveryNotices() {
|
|
329
|
+
const notices = [];
|
|
330
|
+
let cursor = "0";
|
|
331
|
+
do {
|
|
332
|
+
const [nextCursor, keys] = await this.redis.scan(cursor, "MATCH", `${KEY_PREFIX}:gateway:outgoing-delivery:*`, "COUNT", 100);
|
|
333
|
+
cursor = nextCursor;
|
|
334
|
+
if (keys.length === 0) {
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
const rows = await this.redis.mget(...keys);
|
|
338
|
+
for (const row of rows) {
|
|
339
|
+
const notice = parseJson(row);
|
|
340
|
+
if (notice) {
|
|
341
|
+
notices.push(notice);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
} while (cursor !== "0");
|
|
345
|
+
return notices;
|
|
346
|
+
}
|
|
347
|
+
async deleteOutgoingDeliveryNotice(deliveryUuid) {
|
|
348
|
+
const deleted = await this.redis.del(outgoingDeliveryNoticeKey(deliveryUuid));
|
|
349
|
+
return deleted > 0;
|
|
350
|
+
}
|
|
351
|
+
async detachSessionFromPrincipal(principal, sessionId) {
|
|
352
|
+
const sessionsKey = principalSessionsKey(principal);
|
|
353
|
+
const activeKey = principalActiveSessionKey(principal);
|
|
354
|
+
await this.redis.srem(sessionsKey, sessionId);
|
|
355
|
+
const activeSessionId = await this.redis.get(activeKey);
|
|
356
|
+
if (activeSessionId !== sessionId) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const remaining = await this.redis.smembers(sessionsKey);
|
|
360
|
+
const nextSessionId = remaining[0];
|
|
361
|
+
if (nextSessionId) {
|
|
362
|
+
await this.redis.set(activeKey, nextSessionId);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
await this.redis.del(activeKey);
|
|
366
|
+
}
|
|
367
|
+
async unlinkPartnerSession(sessionId) {
|
|
368
|
+
const sourceSession = await this.getSession(sessionId);
|
|
369
|
+
if (!sourceSession?.linkedSessionId) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const { linkedSessionId: partnerId } = sourceSession;
|
|
373
|
+
const { linkedSessionId: _linkedSessionId, ...restSource } = sourceSession;
|
|
374
|
+
await this.setSession({
|
|
375
|
+
...restSource,
|
|
376
|
+
updatedAt: new Date().toISOString(),
|
|
377
|
+
});
|
|
378
|
+
const partnerSession = await this.getSession(partnerId);
|
|
379
|
+
if (!partnerSession || partnerSession.linkedSessionId !== sessionId) {
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
const { linkedSessionId: _partnerLinkedSessionId, ...restPartner } = partnerSession;
|
|
383
|
+
await this.setSession({
|
|
384
|
+
...restPartner,
|
|
385
|
+
updatedAt: new Date().toISOString(),
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
async clearInboxMessages(sessionId) {
|
|
389
|
+
const listKey = inboxListKey(sessionId);
|
|
390
|
+
const messageKeys = [];
|
|
391
|
+
let cursor = "0";
|
|
392
|
+
const pattern = `${KEY_PREFIX}:inbox-message:${sessionId}:*`;
|
|
393
|
+
do {
|
|
394
|
+
const [nextCursor, keys] = await this.redis.scan(cursor, "MATCH", pattern, "COUNT", 200);
|
|
395
|
+
cursor = nextCursor;
|
|
396
|
+
messageKeys.push(...keys);
|
|
397
|
+
} while (cursor !== "0");
|
|
398
|
+
if (messageKeys.length > 0) {
|
|
399
|
+
await this.redis.del(...messageKeys);
|
|
400
|
+
}
|
|
401
|
+
await this.redis.del(listKey);
|
|
402
|
+
}
|
|
403
|
+
async clearXchangeFileMetas(sessionId) {
|
|
404
|
+
let cursor = "0";
|
|
405
|
+
const pattern = `${KEY_PREFIX}:xchange-file:${sessionId}:*`;
|
|
406
|
+
do {
|
|
407
|
+
const [nextCursor, keys] = await this.redis.scan(cursor, "MATCH", pattern, "COUNT", 200);
|
|
408
|
+
cursor = nextCursor;
|
|
409
|
+
if (keys.length > 0) {
|
|
410
|
+
await this.redis.del(...keys);
|
|
411
|
+
}
|
|
412
|
+
} while (cursor !== "0");
|
|
413
|
+
}
|
|
414
|
+
async clearProjectMenuViewStates(sessionId) {
|
|
415
|
+
let cursor = "0";
|
|
416
|
+
const pattern = `${KEY_PREFIX}:project-menu-view:*:${sessionId}`;
|
|
417
|
+
do {
|
|
418
|
+
const [nextCursor, keys] = await this.redis.scan(cursor, "MATCH", pattern, "COUNT", 100);
|
|
419
|
+
cursor = nextCursor;
|
|
420
|
+
if (keys.length > 0) {
|
|
421
|
+
await this.redis.del(...keys);
|
|
422
|
+
}
|
|
423
|
+
} while (cursor !== "0");
|
|
424
|
+
}
|
|
425
|
+
async clearOutgoingDeliveryNoticesForSession(sessionId) {
|
|
426
|
+
const notices = await this.listOutgoingDeliveryNotices();
|
|
427
|
+
const deliveryUuids = notices
|
|
428
|
+
.filter((item) => item.sessionId === sessionId)
|
|
429
|
+
.map((item) => item.deliveryUuid);
|
|
430
|
+
if (deliveryUuids.length === 0) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
await this.redis.del(...deliveryUuids.map((deliveryUuid) => outgoingDeliveryNoticeKey(deliveryUuid)));
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
exports.RedisStateStore = RedisStateStore;
|
package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/collabSemantics.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isExecutorTargetKind = isExecutorTargetKind;
|
|
4
|
+
exports.getCollabRouteSemantics = getCollabRouteSemantics;
|
|
5
|
+
function isExecutorTargetKind(kind) {
|
|
6
|
+
return kind === "question" || kind === "request";
|
|
7
|
+
}
|
|
8
|
+
function getCollabRouteSemantics(input) {
|
|
9
|
+
const executesOnTarget = isExecutorTargetKind(input.kind);
|
|
10
|
+
return executesOnTarget
|
|
11
|
+
? {
|
|
12
|
+
executesOnTarget,
|
|
13
|
+
route: `${input.targetLabel} -> ${input.sourceLabel}`,
|
|
14
|
+
expectedReplyRoute: `${input.targetLabel} -> ${input.sourceLabel}`,
|
|
15
|
+
}
|
|
16
|
+
: {
|
|
17
|
+
executesOnTarget,
|
|
18
|
+
route: `${input.sourceLabel} -> ${input.targetLabel}`,
|
|
19
|
+
sendRoute: `${input.sourceLabel} -> ${input.targetLabel}`,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildProjectMemberDetailText = buildProjectMemberDetailText;
|
|
4
|
+
exports.buildPartnerNotePromptText = buildPartnerNotePromptText;
|
|
5
|
+
const collabSemantics_1 = require("./collabSemantics");
|
|
6
|
+
function buildProjectMemberDetailText(input) {
|
|
7
|
+
const askSemantics = (0, collabSemantics_1.getCollabRouteSemantics)({
|
|
8
|
+
kind: "question",
|
|
9
|
+
sourceLabel: input.sourceLabel,
|
|
10
|
+
targetLabel: input.targetLabel,
|
|
11
|
+
});
|
|
12
|
+
const shareSemantics = (0, collabSemantics_1.getCollabRouteSemantics)({
|
|
13
|
+
kind: "share",
|
|
14
|
+
sourceLabel: input.sourceLabel,
|
|
15
|
+
targetLabel: input.targetLabel,
|
|
16
|
+
});
|
|
17
|
+
return [
|
|
18
|
+
"🤝 Сессия проекта",
|
|
19
|
+
"",
|
|
20
|
+
`Проект: ${input.projectName}`,
|
|
21
|
+
`Текущая сессия: ${input.sourceLabel}`,
|
|
22
|
+
`Исполнитель: ${input.targetLabel}`,
|
|
23
|
+
`Ask: ${askSemantics.route}`,
|
|
24
|
+
`Share: ${shareSemantics.route}`,
|
|
25
|
+
"Live: по подтверждению выбранной сессии",
|
|
26
|
+
"",
|
|
27
|
+
"Выбери тип действия для этой пары сессий.",
|
|
28
|
+
].join("\n");
|
|
29
|
+
}
|
|
30
|
+
function buildPartnerNotePromptText(input) {
|
|
31
|
+
const semantics = (0, collabSemantics_1.getCollabRouteSemantics)({
|
|
32
|
+
kind: input.kind,
|
|
33
|
+
sourceLabel: input.sourceLabel,
|
|
34
|
+
targetLabel: input.targetLabel,
|
|
35
|
+
});
|
|
36
|
+
const executesOnTarget = semantics.executesOnTarget;
|
|
37
|
+
const kindLabel = input.kind === "question"
|
|
38
|
+
? input.isProjectTarget
|
|
39
|
+
? "Вопрос участнику"
|
|
40
|
+
: "Вопрос напарнику"
|
|
41
|
+
: input.kind === "reply"
|
|
42
|
+
? input.isProjectTarget
|
|
43
|
+
? "Ответ участнику"
|
|
44
|
+
: "Ответ напарнику"
|
|
45
|
+
: input.kind === "handoff"
|
|
46
|
+
? input.isProjectTarget
|
|
47
|
+
? "Передача участнику"
|
|
48
|
+
: "Передача напарнику"
|
|
49
|
+
: input.isProjectTarget
|
|
50
|
+
? "Поделиться с участником"
|
|
51
|
+
: "Поделиться обновлением";
|
|
52
|
+
return {
|
|
53
|
+
kindLabel,
|
|
54
|
+
text: [
|
|
55
|
+
`🤝 ${kindLabel}`,
|
|
56
|
+
"",
|
|
57
|
+
`Текущая сессия: ${input.sourceLabel}`,
|
|
58
|
+
executesOnTarget
|
|
59
|
+
? input.isProjectTarget
|
|
60
|
+
? `Исполнитель: ${input.targetLabel}`
|
|
61
|
+
: `Напарник: ${input.targetLabel}`
|
|
62
|
+
: input.isProjectTarget
|
|
63
|
+
? `Получатель: ${input.targetLabel}`
|
|
64
|
+
: `Напарник: ${input.targetLabel}`,
|
|
65
|
+
executesOnTarget
|
|
66
|
+
? `Ожидаемый ответ: ${semantics.expectedReplyRoute}`
|
|
67
|
+
: `Маршрут отправки: ${semantics.sendRoute}`,
|
|
68
|
+
"",
|
|
69
|
+
executesOnTarget
|
|
70
|
+
? "Отправь следующим сообщением задачу для выбранной сессии."
|
|
71
|
+
: "Отправь следующим сообщением, чем текущая сессия должна поделиться.",
|
|
72
|
+
executesOnTarget
|
|
73
|
+
? input.isProjectTarget
|
|
74
|
+
? "Агент выбранной сессии получит задачу и сможет отправить результат обратно в текущую сессию проекта."
|
|
75
|
+
: "Агент напарника получит задачу и сможет отправить результат обратно в текущую сессию."
|
|
76
|
+
: input.isProjectTarget
|
|
77
|
+
? "Агент текущей сессии получит задачу и сам отправит результат в выбранную сессию проекта."
|
|
78
|
+
: "Агент текущей сессии получит задачу и сам отправит результат напарнику.",
|
|
79
|
+
"Формат:",
|
|
80
|
+
"1. Первая строка = короткое summary",
|
|
81
|
+
"2. Пустая строка опциональна",
|
|
82
|
+
"3. Остальной текст = основное сообщение",
|
|
83
|
+
"",
|
|
84
|
+
"Команды вроде /menu или /help отменят этот режим.",
|
|
85
|
+
].join("\n"),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatTelegramMessage = formatTelegramMessage;
|
|
4
|
+
exports.formatTelegramNotification = formatTelegramNotification;
|
|
5
|
+
function renderList(items) {
|
|
6
|
+
return items.map((item, index) => `${index + 1}. ${item}`).join("\n");
|
|
7
|
+
}
|
|
8
|
+
function renderFiles(files) {
|
|
9
|
+
return files.map((item) => `- ${item}`).join("\n");
|
|
10
|
+
}
|
|
11
|
+
function compactMetaLine(input) {
|
|
12
|
+
const parts = [];
|
|
13
|
+
if (input.task) {
|
|
14
|
+
parts.push(`Task: ${input.task}`);
|
|
15
|
+
}
|
|
16
|
+
if (input.riskLevel && input.riskLevel !== "low") {
|
|
17
|
+
parts.push(`Risk: ${input.riskLevel}`);
|
|
18
|
+
}
|
|
19
|
+
return parts.length > 0 ? parts.join(" | ") : null;
|
|
20
|
+
}
|
|
21
|
+
function formatTelegramMessage(input, _limits) {
|
|
22
|
+
const question = input.question;
|
|
23
|
+
const context = input.context;
|
|
24
|
+
const metaLine = compactMetaLine({
|
|
25
|
+
...(input.task ? { task: input.task } : {}),
|
|
26
|
+
...(input.riskLevel ? { riskLevel: input.riskLevel } : {}),
|
|
27
|
+
});
|
|
28
|
+
const sections = [
|
|
29
|
+
input.sessionLabel ?? input.sessionId,
|
|
30
|
+
...(metaLine ? ["", metaLine] : []),
|
|
31
|
+
"",
|
|
32
|
+
question,
|
|
33
|
+
...(context ? ["", context] : []),
|
|
34
|
+
...(input.affectedFiles?.length
|
|
35
|
+
? ["", "Files:", renderFiles(input.affectedFiles)]
|
|
36
|
+
: []),
|
|
37
|
+
...(input.options?.length
|
|
38
|
+
? ["", "Options:", renderList(input.options)]
|
|
39
|
+
: []),
|
|
40
|
+
...(input.recommendedOption
|
|
41
|
+
? ["", `Recommended: ${input.recommendedOption}`]
|
|
42
|
+
: []),
|
|
43
|
+
...(input.fallbackIfTimeout
|
|
44
|
+
? ["", `Fallback if no answer: ${input.fallbackIfTimeout}`]
|
|
45
|
+
: []),
|
|
46
|
+
"",
|
|
47
|
+
"Reply to this message.",
|
|
48
|
+
];
|
|
49
|
+
return sections.join("\n");
|
|
50
|
+
}
|
|
51
|
+
function formatTelegramNotification(input, _limits) {
|
|
52
|
+
const context = input.context;
|
|
53
|
+
const message = input.message;
|
|
54
|
+
return [
|
|
55
|
+
input.sessionLabel ?? input.sessionId,
|
|
56
|
+
"",
|
|
57
|
+
message,
|
|
58
|
+
...(context ? ["", context] : []),
|
|
59
|
+
].join("\n");
|
|
60
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createTelegramFetch = createTelegramFetch;
|
|
7
|
+
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
8
|
+
const https_proxy_agent_1 = require("https-proxy-agent");
|
|
9
|
+
const socks_proxy_agent_1 = require("socks-proxy-agent");
|
|
10
|
+
function maskProxyUrl(rawUrl) {
|
|
11
|
+
try {
|
|
12
|
+
const url = new URL(rawUrl);
|
|
13
|
+
if (url.username) {
|
|
14
|
+
url.username = "[REDACTED]";
|
|
15
|
+
}
|
|
16
|
+
if (url.password) {
|
|
17
|
+
url.password = "[REDACTED]";
|
|
18
|
+
}
|
|
19
|
+
return url.toString();
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return "[INVALID_PROXY_URL]";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function createTelegramFetch(config, logger) {
|
|
26
|
+
if (!config.telegram.proxy) {
|
|
27
|
+
logger.debug("Telegram proxy disabled");
|
|
28
|
+
return globalThis.fetch.bind(globalThis);
|
|
29
|
+
}
|
|
30
|
+
const agent = config.telegram.proxy.type === "http"
|
|
31
|
+
? new https_proxy_agent_1.HttpsProxyAgent(config.telegram.proxy.url)
|
|
32
|
+
: new socks_proxy_agent_1.SocksProxyAgent(config.telegram.proxy.url);
|
|
33
|
+
logger.info("Telegram proxy configured", {
|
|
34
|
+
proxyType: config.telegram.proxy.type,
|
|
35
|
+
proxyUrl: maskProxyUrl(config.telegram.proxy.url),
|
|
36
|
+
});
|
|
37
|
+
const proxiedFetch = async (...args) => {
|
|
38
|
+
const [input, init] = args;
|
|
39
|
+
const requestInit = {
|
|
40
|
+
...(init ? init : {}),
|
|
41
|
+
agent,
|
|
42
|
+
};
|
|
43
|
+
return (0, node_fetch_1.default)(input, requestInit);
|
|
44
|
+
};
|
|
45
|
+
return proxiedFetch;
|
|
46
|
+
}
|