@deadragdoll/tellymcp 0.0.13 → 0.0.15
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 +23 -39
- package/.env.example.gateway +52 -61
- package/CHANGELOG.md +57 -0
- package/README-ru.md +80 -6
- package/README.md +80 -6
- package/TOOLS.md +219 -10
- package/config/templates/env.both.template +19 -7
- package/config/templates/env.client.template +9 -19
- package/config/templates/env.gateway.template +19 -31
- package/dist/cli.js +252 -47
- package/dist/configureServer.js +966 -0
- package/dist/envMigration.js +316 -0
- package/dist/moleculer.config.js +1 -3
- package/dist/services/features/telegram-mcp/approval.service.js +1 -1
- package/dist/services/features/telegram-mcp/browser.service.js +18 -0
- package/dist/services/features/telegram-mcp/collaboration.service.js +2 -2
- package/dist/services/features/telegram-mcp/ensuredb.service.js +1 -1
- package/dist/services/features/telegram-mcp/file-content.service.js +94 -0
- package/dist/services/features/telegram-mcp/gateway-delivery.service.js +5 -1
- package/dist/services/features/telegram-mcp/gateway-socket.service.js +43 -11
- package/dist/services/features/telegram-mcp/gateway.service.js +1 -1
- package/dist/services/features/telegram-mcp/mcp-http.service.js +1 -0
- package/dist/services/features/telegram-mcp/mcp-server.service.js +20 -0
- package/dist/services/features/telegram-mcp/session-context.service.js +25 -1
- package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +79 -67
- package/dist/services/features/telegram-mcp/src/app/config/env.js +129 -38
- package/dist/services/features/telegram-mcp/src/app/config/environmentContract.js +66 -0
- package/dist/services/features/telegram-mcp/src/app/http.js +139 -99
- package/dist/services/features/telegram-mcp/src/app/oauthFacade.js +642 -0
- package/dist/services/features/telegram-mcp/src/app/webapp/assets.js +151 -170
- package/dist/services/features/telegram-mcp/src/app/webapp/auth.js +96 -99
- package/dist/services/features/telegram-mcp/src/entities/request/model/schema.js +88 -19
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserAttachActiveTabTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserAttachTabTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserDetachTabTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserListAttachedInstancesTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserListTabsTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +412 -29
- package/dist/services/features/telegram-mcp/src/features/browser-attach/model/browserRecordingBundle.js +37 -3
- package/dist/services/features/telegram-mcp/src/features/browser-attach/model/firefoxAttachRegistry.js +7 -6
- package/dist/services/features/telegram-mcp/src/features/browser-attach/model/firefoxAttachServer.js +214 -37
- package/dist/services/features/telegram-mcp/src/features/browser-attach/model/types.js +186 -0
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/collaborationService.js +2 -0
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/gatewaySessionsService.js +5 -5
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileService.js +6 -3
- package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayClientAccess.js +5 -2
- package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayCollaborationBackend.js +4 -4
- package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayHttpService.js +33 -36
- package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/remoteConsoleActionClient.js +4 -3
- package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileListTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileService.js +327 -0
- package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileTool.js +81 -0
- package/dist/services/features/telegram-mcp/src/features/file-content/model/temporaryFileLinkStore.js +307 -0
- package/dist/services/features/telegram-mcp/src/features/file-content/model/workspaceFilePolicy.js +115 -0
- package/dist/services/features/telegram-mcp/src/features/notify/model/notifyService.js +9 -5
- package/dist/services/features/telegram-mcp/src/features/session-context/model/getRuntimeDiagnosticsTool.js +30 -0
- package/dist/services/features/telegram-mcp/src/features/session-context/model/sessionContextService.js +169 -7
- package/dist/services/features/telegram-mcp/src/shared/integrations/memory/processLocalStateStore.js +260 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/object-storage/minioExchangeStore.js +1 -1
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transport.js +4 -1
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportConsoleRegistry.js +2 -2
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportFileHandoffActions.js +6 -3
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMessageFlow.js +1 -1
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectState.js +2 -2
- package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/client.js +29 -6
- package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/ptyRegistry.js +100 -2
- package/dist/services/features/telegram-mcp/src/shared/lib/bodyLimits.js +63 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/gatewayAuth.js +13 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/gatewayScope.js +5 -5
- package/dist/services/features/telegram-mcp/src/shared/lib/project-identity/projectIdentity.js +10 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/time/localTimestamp.js +21 -0
- package/docs/CHAT_CONNECTOR.md +134 -0
- package/docs/STANDALONE-ru.md +41 -3
- package/docs/STANDALONE.md +41 -3
- package/package.json +5 -3
- package/packages/chrome-attach-extension/dist/background.js +572 -163
- package/packages/chrome-attach-extension/dist/manifest.json +2 -1
- package/packages/chrome-attach-extension/dist/options.js +15 -2
- package/packages/chrome-attach-extension/dist/recorder-content.js +14 -1
- package/packages/chrome-attach-extension/dist/recorder-page.js +34 -18
- package/packages/firefox-attach-extension/dist/background.js +413 -33
- package/packages/firefox-attach-extension/dist/manifest.json +0 -12
- package/packages/firefox-attach-extension/dist/options.js +14 -1
- package/packages/firefox-attach-extension/dist/recorder-content.js +14 -1
- package/packages/firefox-attach-extension/dist/recorder-page.js +34 -18
- package/scripts/postinstall.js +33 -1
|
@@ -9,16 +9,174 @@ class SessionContextService {
|
|
|
9
9
|
logger;
|
|
10
10
|
projectIdentityResolver;
|
|
11
11
|
remoteConsoleInvoker;
|
|
12
|
-
|
|
12
|
+
runtimeDiagnostics;
|
|
13
|
+
constructor(sessionStore, bindingStore, logger, projectIdentityResolver, remoteConsoleInvoker, runtimeDiagnostics) {
|
|
13
14
|
this.sessionStore = sessionStore;
|
|
14
15
|
this.bindingStore = bindingStore;
|
|
15
16
|
this.logger = logger;
|
|
16
17
|
this.projectIdentityResolver = projectIdentityResolver;
|
|
17
18
|
this.remoteConsoleInvoker = remoteConsoleInvoker;
|
|
19
|
+
this.runtimeDiagnostics = runtimeDiagnostics;
|
|
20
|
+
}
|
|
21
|
+
getRemoteConsoleInvoker() {
|
|
22
|
+
return this.runtimeDiagnostics?.mode === "client"
|
|
23
|
+
? undefined
|
|
24
|
+
: this.remoteConsoleInvoker;
|
|
25
|
+
}
|
|
26
|
+
formatDiagnosticError(error) {
|
|
27
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
28
|
+
const redacted = (0, redactSecrets_1.redactSecrets)(message).replace(/\s+/gu, " ").trim();
|
|
29
|
+
return redacted.length > 500
|
|
30
|
+
? `${redacted.slice(0, 497)}...`
|
|
31
|
+
: redacted || "Unknown error";
|
|
32
|
+
}
|
|
33
|
+
finalizeDiagnostics(output) {
|
|
34
|
+
const degraded = Object.values(output.checks).some((check) => check.status !== "ok");
|
|
35
|
+
return {
|
|
36
|
+
...output,
|
|
37
|
+
status: degraded ? "degraded" : "ok",
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
async collectLocalDiagnostics(sessionId) {
|
|
41
|
+
const runtime = this.runtimeDiagnostics;
|
|
42
|
+
let session = null;
|
|
43
|
+
let sessionStoreCheck;
|
|
44
|
+
try {
|
|
45
|
+
session = await this.sessionStore.getSession(sessionId);
|
|
46
|
+
await this.bindingStore.getBinding(sessionId);
|
|
47
|
+
sessionStoreCheck = {
|
|
48
|
+
status: "ok",
|
|
49
|
+
message: session
|
|
50
|
+
? "Session and route stores are readable; session metadata exists."
|
|
51
|
+
: "Session and route stores are readable; no saved metadata exists for this id.",
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
sessionStoreCheck = {
|
|
56
|
+
status: "error",
|
|
57
|
+
message: `Session store check failed: ${this.formatDiagnosticError(error)}`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
let redisCheck;
|
|
61
|
+
if (runtime?.mode === "client") {
|
|
62
|
+
redisCheck = {
|
|
63
|
+
status: "ok",
|
|
64
|
+
message: "Redis is not required in client mode; process-local state is active.",
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
else if (!runtime?.pingRedis) {
|
|
68
|
+
redisCheck = {
|
|
69
|
+
status: "warn",
|
|
70
|
+
message: "Runtime Redis probe is unavailable.",
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
try {
|
|
75
|
+
const reply = await runtime.pingRedis();
|
|
76
|
+
redisCheck = {
|
|
77
|
+
status: reply.trim().toUpperCase() === "PONG" ? "ok" : "warn",
|
|
78
|
+
message: `Redis probe returned ${reply.trim() || "an empty response"}.`,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
redisCheck = {
|
|
83
|
+
status: "error",
|
|
84
|
+
message: `Redis probe failed: ${this.formatDiagnosticError(error)}`,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const mode = runtime?.mode ?? "client";
|
|
89
|
+
const gatewayConfigured = Boolean(runtime?.gatewayWsUrlConfigured);
|
|
90
|
+
const gatewayConfiguration = mode === "gateway"
|
|
91
|
+
? {
|
|
92
|
+
status: "ok",
|
|
93
|
+
message: "Gateway runtime does not require an outbound gateway WebSocket URL.",
|
|
94
|
+
}
|
|
95
|
+
: gatewayConfigured
|
|
96
|
+
? {
|
|
97
|
+
status: "ok",
|
|
98
|
+
message: runtime?.gatewayAuthConfigured
|
|
99
|
+
? "Gateway WebSocket URL and authentication are configured."
|
|
100
|
+
: "Gateway WebSocket URL is configured; authentication is not configured.",
|
|
101
|
+
}
|
|
102
|
+
: {
|
|
103
|
+
status: "error",
|
|
104
|
+
message: "Gateway WebSocket URL is not configured for this client runtime.",
|
|
105
|
+
};
|
|
106
|
+
return this.finalizeDiagnostics({
|
|
107
|
+
checked_at: new Date().toISOString(),
|
|
108
|
+
session_id: sessionId,
|
|
109
|
+
runtime: {
|
|
110
|
+
mode,
|
|
111
|
+
package_version: runtime?.packageVersion ?? "unknown",
|
|
112
|
+
protocol_version: runtime?.protocolVersion ?? "unknown",
|
|
113
|
+
...(runtime?.nodeId ? { node_id: runtime.nodeId } : {}),
|
|
114
|
+
},
|
|
115
|
+
checks: {
|
|
116
|
+
configuration: runtime
|
|
117
|
+
? {
|
|
118
|
+
status: "ok",
|
|
119
|
+
message: "Normalized environment schema was accepted at startup.",
|
|
120
|
+
}
|
|
121
|
+
: {
|
|
122
|
+
status: "warn",
|
|
123
|
+
message: "Runtime configuration metadata is unavailable.",
|
|
124
|
+
},
|
|
125
|
+
redis: redisCheck,
|
|
126
|
+
session_store: sessionStoreCheck,
|
|
127
|
+
terminal: session?.terminalTarget
|
|
128
|
+
? {
|
|
129
|
+
status: "ok",
|
|
130
|
+
message: "A PTY terminal target is configured for this session.",
|
|
131
|
+
}
|
|
132
|
+
: {
|
|
133
|
+
status: "warn",
|
|
134
|
+
message: "No PTY terminal target is configured for this session.",
|
|
135
|
+
},
|
|
136
|
+
gateway_configuration: gatewayConfiguration,
|
|
137
|
+
relay: {
|
|
138
|
+
status: "ok",
|
|
139
|
+
message: "Local diagnostic action completed.",
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
async getRuntimeDiagnostics(input) {
|
|
145
|
+
const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
|
|
146
|
+
const remoteConsoleInvoker = this.getRemoteConsoleInvoker();
|
|
147
|
+
if (!remoteConsoleInvoker) {
|
|
148
|
+
return await this.collectLocalDiagnostics(resolved.sessionId);
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
const remote = await remoteConsoleInvoker.invokeForRelaySession(resolved.sessionId, "telegramMcp.sessionContext.getRuntimeDiagnosticsRemote", input);
|
|
152
|
+
return this.finalizeDiagnostics({
|
|
153
|
+
...remote,
|
|
154
|
+
checks: {
|
|
155
|
+
...remote.checks,
|
|
156
|
+
relay: {
|
|
157
|
+
status: "ok",
|
|
158
|
+
message: "Gateway-to-client relay completed successfully.",
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
const local = await this.collectLocalDiagnostics(resolved.sessionId);
|
|
165
|
+
return this.finalizeDiagnostics({
|
|
166
|
+
...local,
|
|
167
|
+
checks: {
|
|
168
|
+
...local.checks,
|
|
169
|
+
relay: {
|
|
170
|
+
status: "error",
|
|
171
|
+
message: `Gateway-to-client relay failed: ${this.formatDiagnosticError(error)}`,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
}
|
|
18
176
|
}
|
|
19
177
|
async setContext(input) {
|
|
20
178
|
const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
|
|
21
|
-
const remote = await this.
|
|
179
|
+
const remote = await this.getRemoteConsoleInvoker()?.invokeForRelaySession(resolved.sessionId, "telegramMcp.sessionContext.setContextRemote", input);
|
|
22
180
|
if (remote) {
|
|
23
181
|
return remote;
|
|
24
182
|
}
|
|
@@ -62,7 +220,9 @@ class SessionContextService {
|
|
|
62
220
|
: existing?.risks
|
|
63
221
|
? { risks: existing.risks }
|
|
64
222
|
: {}),
|
|
65
|
-
...(existing?.terminalTarget
|
|
223
|
+
...(existing?.terminalTarget
|
|
224
|
+
? { terminalTarget: existing.terminalTarget }
|
|
225
|
+
: {}),
|
|
66
226
|
...(existing?.lastTerminalNudgeAt
|
|
67
227
|
? { lastTerminalNudgeAt: existing.lastTerminalNudgeAt }
|
|
68
228
|
: {}),
|
|
@@ -98,7 +258,7 @@ class SessionContextService {
|
|
|
98
258
|
}
|
|
99
259
|
async renameSession(input) {
|
|
100
260
|
const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
|
|
101
|
-
const remote = await this.
|
|
261
|
+
const remote = await this.getRemoteConsoleInvoker()?.invokeForRelaySession(resolved.sessionId, "telegramMcp.sessionContext.renameSessionRemote", input);
|
|
102
262
|
if (remote) {
|
|
103
263
|
return remote;
|
|
104
264
|
}
|
|
@@ -120,7 +280,9 @@ class SessionContextService {
|
|
|
120
280
|
...(existing?.files ? { files: existing.files } : {}),
|
|
121
281
|
...(existing?.decisions ? { decisions: existing.decisions } : {}),
|
|
122
282
|
...(existing?.risks ? { risks: existing.risks } : {}),
|
|
123
|
-
...(existing?.terminalTarget
|
|
283
|
+
...(existing?.terminalTarget
|
|
284
|
+
? { terminalTarget: existing.terminalTarget }
|
|
285
|
+
: {}),
|
|
124
286
|
...(existing?.lastTerminalNudgeAt
|
|
125
287
|
? { lastTerminalNudgeAt: existing.lastTerminalNudgeAt }
|
|
126
288
|
: {}),
|
|
@@ -151,7 +313,7 @@ class SessionContextService {
|
|
|
151
313
|
}
|
|
152
314
|
async getContext(input) {
|
|
153
315
|
const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
|
|
154
|
-
const remote = await this.
|
|
316
|
+
const remote = await this.getRemoteConsoleInvoker()?.invokeForRelaySession(resolved.sessionId, "telegramMcp.sessionContext.getContextRemote", input);
|
|
155
317
|
if (remote) {
|
|
156
318
|
return remote;
|
|
157
319
|
}
|
|
@@ -224,7 +386,7 @@ class SessionContextService {
|
|
|
224
386
|
}
|
|
225
387
|
async clearContext(input) {
|
|
226
388
|
const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
|
|
227
|
-
const remote = await this.
|
|
389
|
+
const remote = await this.getRemoteConsoleInvoker()?.invokeForRelaySession(resolved.sessionId, "telegramMcp.sessionContext.clearContextRemote", input);
|
|
228
390
|
if (remote) {
|
|
229
391
|
return remote;
|
|
230
392
|
}
|
package/dist/services/features/telegram-mcp/src/shared/integrations/memory/processLocalStateStore.js
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ProcessLocalStateStore = void 0;
|
|
4
|
+
const redactSecrets_1 = require("../../lib/redact-secrets/redactSecrets");
|
|
5
|
+
function principalKey(principal) {
|
|
6
|
+
return `${principal.telegramChatId}:${principal.telegramUserId}`;
|
|
7
|
+
}
|
|
8
|
+
function xchangeKey(sessionId, filePath) {
|
|
9
|
+
return `${sessionId}\u0000${filePath}`;
|
|
10
|
+
}
|
|
11
|
+
class ProcessLocalStateStore {
|
|
12
|
+
pairCodes = new Map();
|
|
13
|
+
bindings = new Map();
|
|
14
|
+
principalSessions = new Map();
|
|
15
|
+
principalActiveSessions = new Map();
|
|
16
|
+
locales = new Map();
|
|
17
|
+
authorizedPrincipals = new Map();
|
|
18
|
+
requests = new Map();
|
|
19
|
+
requestQueue = [];
|
|
20
|
+
activeRequestId = null;
|
|
21
|
+
menuPayloads = new Map();
|
|
22
|
+
xchangeFileMetas = new Map();
|
|
23
|
+
browserAttachments = new Map();
|
|
24
|
+
browserRecordings = new Map();
|
|
25
|
+
projectMenuViews = new Map();
|
|
26
|
+
outgoingNotices = new Map();
|
|
27
|
+
gatewayClientUuid;
|
|
28
|
+
constructor(input = {}) {
|
|
29
|
+
this.gatewayClientUuid = input.gatewayClientUuid?.trim() || null;
|
|
30
|
+
this.onGatewayClientUuidChange = input.onGatewayClientUuidChange;
|
|
31
|
+
}
|
|
32
|
+
onGatewayClientUuidChange;
|
|
33
|
+
async createPairCode(record, ttlSeconds) {
|
|
34
|
+
const existing = this.readExpiring(this.pairCodes, record.code);
|
|
35
|
+
if (existing)
|
|
36
|
+
return false;
|
|
37
|
+
this.pairCodes.set(record.code, { value: record, expiresAt: Date.now() + ttlSeconds * 1000 });
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
async consumePairCode(code) {
|
|
41
|
+
const record = this.readExpiring(this.pairCodes, code);
|
|
42
|
+
this.pairCodes.delete(code);
|
|
43
|
+
return record;
|
|
44
|
+
}
|
|
45
|
+
async getBinding(sessionId) {
|
|
46
|
+
return this.bindings.get(sessionId) ?? null;
|
|
47
|
+
}
|
|
48
|
+
async setBinding(binding) {
|
|
49
|
+
const previous = this.bindings.get(binding.sessionId);
|
|
50
|
+
if (previous)
|
|
51
|
+
this.detachSession(previous, binding.sessionId);
|
|
52
|
+
this.bindings.set(binding.sessionId, binding);
|
|
53
|
+
const key = principalKey(binding);
|
|
54
|
+
const sessions = this.principalSessions.get(key) ?? new Set();
|
|
55
|
+
sessions.add(binding.sessionId);
|
|
56
|
+
this.principalSessions.set(key, sessions);
|
|
57
|
+
this.principalActiveSessions.set(key, binding.sessionId);
|
|
58
|
+
}
|
|
59
|
+
async clearBinding(sessionId) {
|
|
60
|
+
const binding = this.bindings.get(sessionId);
|
|
61
|
+
this.bindings.delete(sessionId);
|
|
62
|
+
if (binding)
|
|
63
|
+
this.detachSession(binding, sessionId);
|
|
64
|
+
}
|
|
65
|
+
async getActiveSessionIdForPrincipal(principal) {
|
|
66
|
+
return this.principalActiveSessions.get(principalKey(principal)) ?? null;
|
|
67
|
+
}
|
|
68
|
+
async getActiveSessionIdForTelegramUser(telegramUserId) {
|
|
69
|
+
for (const [sessionId, binding] of this.bindings) {
|
|
70
|
+
if (binding.telegramUserId === telegramUserId) {
|
|
71
|
+
return this.principalActiveSessions.get(principalKey(binding)) ?? sessionId;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
async clearActiveSessionIdForPrincipal(principal) {
|
|
77
|
+
this.principalActiveSessions.delete(principalKey(principal));
|
|
78
|
+
}
|
|
79
|
+
async setActiveSessionIdForPrincipal(principal, sessionId) {
|
|
80
|
+
const key = principalKey(principal);
|
|
81
|
+
const sessions = this.principalSessions.get(key) ?? new Set();
|
|
82
|
+
sessions.add(sessionId);
|
|
83
|
+
this.principalSessions.set(key, sessions);
|
|
84
|
+
this.principalActiveSessions.set(key, sessionId);
|
|
85
|
+
}
|
|
86
|
+
async listBoundSessionIdsForPrincipal(principal) {
|
|
87
|
+
return Array.from(this.principalSessions.get(principalKey(principal)) ?? []);
|
|
88
|
+
}
|
|
89
|
+
async listBoundPrincipals() {
|
|
90
|
+
const principals = new Map();
|
|
91
|
+
for (const binding of this.bindings.values()) {
|
|
92
|
+
principals.set(principalKey(binding), {
|
|
93
|
+
telegramChatId: binding.telegramChatId,
|
|
94
|
+
telegramUserId: binding.telegramUserId,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return Array.from(principals.values());
|
|
98
|
+
}
|
|
99
|
+
async resetRuntimeState() {
|
|
100
|
+
this.activeRequestId = null;
|
|
101
|
+
this.requestQueue.length = 0;
|
|
102
|
+
}
|
|
103
|
+
async getActive() {
|
|
104
|
+
return this.activeRequestId ? this.requests.get(this.activeRequestId) ?? null : null;
|
|
105
|
+
}
|
|
106
|
+
async createPending(request) {
|
|
107
|
+
this.requests.set(request.requestId, request);
|
|
108
|
+
this.activeRequestId = request.requestId;
|
|
109
|
+
}
|
|
110
|
+
async updatePending(request) {
|
|
111
|
+
this.requests.set(request.requestId, request);
|
|
112
|
+
}
|
|
113
|
+
async resolvePending(requestId, resolution) {
|
|
114
|
+
const current = this.requests.get(requestId);
|
|
115
|
+
if (current) {
|
|
116
|
+
this.requests.set(requestId, {
|
|
117
|
+
...current,
|
|
118
|
+
status: resolution.status,
|
|
119
|
+
...(resolution.answer ? { answer: (0, redactSecrets_1.redactSecrets)(resolution.answer) } : {}),
|
|
120
|
+
...(resolution.receivedAt ? { receivedAt: resolution.receivedAt } : {}),
|
|
121
|
+
...(resolution.fallbackUsed ? { fallbackIfTimeout: resolution.fallbackUsed } : {}),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
this.activeRequestId = null;
|
|
125
|
+
}
|
|
126
|
+
async enqueue(request) {
|
|
127
|
+
this.requests.set(request.requestId, request);
|
|
128
|
+
this.requestQueue.push(request.requestId);
|
|
129
|
+
}
|
|
130
|
+
async dequeueNext() {
|
|
131
|
+
const requestId = this.requestQueue.shift();
|
|
132
|
+
return requestId ? this.requests.get(requestId) ?? null : null;
|
|
133
|
+
}
|
|
134
|
+
async createMenuPayload(record, ttlSeconds) {
|
|
135
|
+
this.menuPayloads.set(record.key, { value: record, expiresAt: Date.now() + ttlSeconds * 1000 });
|
|
136
|
+
}
|
|
137
|
+
async getMenuPayload(key) {
|
|
138
|
+
return this.readExpiring(this.menuPayloads, key);
|
|
139
|
+
}
|
|
140
|
+
async getUserLocale(telegramUserId) {
|
|
141
|
+
return this.locales.get(telegramUserId) ?? null;
|
|
142
|
+
}
|
|
143
|
+
async setUserLocale(telegramUserId, locale) {
|
|
144
|
+
this.locales.set(telegramUserId, locale);
|
|
145
|
+
}
|
|
146
|
+
async isAdminAuthorized(principal) {
|
|
147
|
+
return this.authorizedPrincipals.has(principalKey(principal));
|
|
148
|
+
}
|
|
149
|
+
async setAdminAuthorized(principal) {
|
|
150
|
+
this.authorizedPrincipals.set(principalKey(principal), principal);
|
|
151
|
+
}
|
|
152
|
+
async clearAdminAuthorized(principal) {
|
|
153
|
+
this.authorizedPrincipals.delete(principalKey(principal));
|
|
154
|
+
}
|
|
155
|
+
async listAdminAuthorizedPrincipals() {
|
|
156
|
+
return Array.from(this.authorizedPrincipals.values());
|
|
157
|
+
}
|
|
158
|
+
async setXchangeFileMeta(meta) {
|
|
159
|
+
this.xchangeFileMetas.set(xchangeKey(meta.sessionId, meta.filePath), meta);
|
|
160
|
+
}
|
|
161
|
+
async listXchangeFileMetas(sessionId) {
|
|
162
|
+
return Array.from(this.xchangeFileMetas.values())
|
|
163
|
+
.filter((meta) => meta.sessionId === sessionId)
|
|
164
|
+
.sort((left, right) => right.uploadedAt.localeCompare(left.uploadedAt));
|
|
165
|
+
}
|
|
166
|
+
async getXchangeFileMeta(sessionId, filePath) {
|
|
167
|
+
return this.xchangeFileMetas.get(xchangeKey(sessionId, filePath)) ?? null;
|
|
168
|
+
}
|
|
169
|
+
async deleteXchangeFileMeta(sessionId, filePath) {
|
|
170
|
+
return this.xchangeFileMetas.delete(xchangeKey(sessionId, filePath));
|
|
171
|
+
}
|
|
172
|
+
async pruneAll() {
|
|
173
|
+
const deletedKeys = this.countEntries();
|
|
174
|
+
this.pairCodes.clear();
|
|
175
|
+
this.bindings.clear();
|
|
176
|
+
this.principalSessions.clear();
|
|
177
|
+
this.principalActiveSessions.clear();
|
|
178
|
+
this.locales.clear();
|
|
179
|
+
this.authorizedPrincipals.clear();
|
|
180
|
+
this.requests.clear();
|
|
181
|
+
this.requestQueue.length = 0;
|
|
182
|
+
this.activeRequestId = null;
|
|
183
|
+
this.menuPayloads.clear();
|
|
184
|
+
this.xchangeFileMetas.clear();
|
|
185
|
+
this.browserAttachments.clear();
|
|
186
|
+
this.browserRecordings.clear();
|
|
187
|
+
this.projectMenuViews.clear();
|
|
188
|
+
this.outgoingNotices.clear();
|
|
189
|
+
this.gatewayClientUuid = null;
|
|
190
|
+
return { deletedKeys };
|
|
191
|
+
}
|
|
192
|
+
async getGatewayClientUuid() { return this.gatewayClientUuid; }
|
|
193
|
+
async setGatewayClientUuid(clientUuid) {
|
|
194
|
+
this.gatewayClientUuid = clientUuid;
|
|
195
|
+
this.onGatewayClientUuidChange?.(clientUuid);
|
|
196
|
+
}
|
|
197
|
+
async getBrowserAttachment(sessionId) {
|
|
198
|
+
return this.browserAttachments.get(sessionId) ?? null;
|
|
199
|
+
}
|
|
200
|
+
async setBrowserAttachment(record) {
|
|
201
|
+
this.browserAttachments.set(record.sessionId, record);
|
|
202
|
+
}
|
|
203
|
+
async clearBrowserAttachment(sessionId) { this.browserAttachments.delete(sessionId); }
|
|
204
|
+
async getBrowserRecording(sessionId) {
|
|
205
|
+
return this.browserRecordings.get(sessionId) ?? null;
|
|
206
|
+
}
|
|
207
|
+
async setBrowserRecording(record) {
|
|
208
|
+
this.browserRecordings.set(record.sessionId, record);
|
|
209
|
+
}
|
|
210
|
+
async clearBrowserRecording(sessionId) { this.browserRecordings.delete(sessionId); }
|
|
211
|
+
async setProjectMenuViewState(state) {
|
|
212
|
+
this.projectMenuViews.set(xchangeKey(state.projectUuid, state.sessionId), state);
|
|
213
|
+
}
|
|
214
|
+
async listProjectMenuViewStates(projectUuid) {
|
|
215
|
+
return Array.from(this.projectMenuViews.values()).filter((state) => state.projectUuid === projectUuid);
|
|
216
|
+
}
|
|
217
|
+
async deleteProjectMenuViewState(sessionId, projectUuid) {
|
|
218
|
+
return this.projectMenuViews.delete(xchangeKey(projectUuid, sessionId));
|
|
219
|
+
}
|
|
220
|
+
async setOutgoingDeliveryNotice(notice) {
|
|
221
|
+
this.outgoingNotices.set(notice.deliveryUuid, notice);
|
|
222
|
+
}
|
|
223
|
+
async listOutgoingDeliveryNotices() {
|
|
224
|
+
return Array.from(this.outgoingNotices.values());
|
|
225
|
+
}
|
|
226
|
+
async deleteOutgoingDeliveryNotice(deliveryUuid) {
|
|
227
|
+
return this.outgoingNotices.delete(deliveryUuid);
|
|
228
|
+
}
|
|
229
|
+
readExpiring(store, key) {
|
|
230
|
+
const record = store.get(key);
|
|
231
|
+
if (!record)
|
|
232
|
+
return null;
|
|
233
|
+
if (record.expiresAt <= Date.now()) {
|
|
234
|
+
store.delete(key);
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
return record.value;
|
|
238
|
+
}
|
|
239
|
+
detachSession(principal, sessionId) {
|
|
240
|
+
const key = principalKey(principal);
|
|
241
|
+
const sessions = this.principalSessions.get(key);
|
|
242
|
+
sessions?.delete(sessionId);
|
|
243
|
+
if (!sessions?.size)
|
|
244
|
+
this.principalSessions.delete(key);
|
|
245
|
+
if (this.principalActiveSessions.get(key) === sessionId) {
|
|
246
|
+
const next = sessions?.values().next().value;
|
|
247
|
+
if (next)
|
|
248
|
+
this.principalActiveSessions.set(key, next);
|
|
249
|
+
else
|
|
250
|
+
this.principalActiveSessions.delete(key);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
countEntries() {
|
|
254
|
+
return this.pairCodes.size + this.bindings.size + this.locales.size +
|
|
255
|
+
this.authorizedPrincipals.size + this.requests.size + this.menuPayloads.size +
|
|
256
|
+
this.xchangeFileMetas.size + this.browserAttachments.size + this.browserRecordings.size +
|
|
257
|
+
this.projectMenuViews.size + this.outgoingNotices.size + (this.gatewayClientUuid ? 1 : 0);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
exports.ProcessLocalStateStore = ProcessLocalStateStore;
|
|
@@ -29,7 +29,7 @@ class MinioExchangeStore {
|
|
|
29
29
|
terminalConfig;
|
|
30
30
|
exchangeDirName;
|
|
31
31
|
logger;
|
|
32
|
-
constructor(
|
|
32
|
+
constructor(terminalConfig, exchangeDirName, logger) {
|
|
33
33
|
this.terminalConfig = terminalConfig;
|
|
34
34
|
this.exchangeDirName = exchangeDirName;
|
|
35
35
|
this.logger = logger;
|
|
@@ -6,6 +6,7 @@ const promises_1 = require("node:fs/promises");
|
|
|
6
6
|
const grammy_1 = require("grammy");
|
|
7
7
|
const telegramXchangeRecords_1 = require("../../lib/telegramXchangeRecords");
|
|
8
8
|
const collabSemantics_1 = require("./collabSemantics");
|
|
9
|
+
const bodyLimits_1 = require("../../lib/bodyLimits");
|
|
9
10
|
const transportFormatting_1 = require("./transportFormatting");
|
|
10
11
|
const transportConstructorWiring_1 = require("./transportConstructorWiring");
|
|
11
12
|
const transportUtils_1 = require("./transportUtils");
|
|
@@ -217,7 +218,7 @@ class TelegramTransport {
|
|
|
217
218
|
}
|
|
218
219
|
isAdminAuthEnabled() {
|
|
219
220
|
return (this.isAdminBotProfile() &&
|
|
220
|
-
Boolean(this.config.distributed.
|
|
221
|
+
Boolean(this.config.distributed.gatewayScopeToken?.trim()));
|
|
221
222
|
}
|
|
222
223
|
isAdminBotProfile() {
|
|
223
224
|
return this.config.distributed.mode === "gateway";
|
|
@@ -383,6 +384,8 @@ class TelegramTransport {
|
|
|
383
384
|
}
|
|
384
385
|
async sendDocumentToChat(telegramChatId, filePath, caption) {
|
|
385
386
|
this.ensureTelegramEnabledFor("send Telegram documents");
|
|
387
|
+
const fileStats = await (0, promises_1.stat)(filePath);
|
|
388
|
+
(0, bodyLimits_1.assertBodySize)(fileStats.size);
|
|
386
389
|
const fileBuffer = await (0, promises_1.readFile)(filePath);
|
|
387
390
|
const response = await this.bot.api.sendDocument(telegramChatId, new grammy_1.InputFile(fileBuffer, (0, node_path_1.basename)(filePath)), caption?.trim()
|
|
388
391
|
? {
|
|
@@ -26,8 +26,8 @@ class TransportConsoleRegistry {
|
|
|
26
26
|
...(input?.principal
|
|
27
27
|
? { telegram_user_id: input.principal.telegramUserId }
|
|
28
28
|
: {}),
|
|
29
|
-
...(this.host.config.distributed.
|
|
30
|
-
? { gateway_token: this.host.config.distributed.
|
|
29
|
+
...(this.host.config.distributed.gatewayScopeToken
|
|
30
|
+
? { gateway_token: this.host.config.distributed.gatewayScopeToken }
|
|
31
31
|
: {}),
|
|
32
32
|
});
|
|
33
33
|
return Array.isArray(response.sessions) ? response.sessions : [];
|
|
@@ -11,6 +11,7 @@ const xchangeRecordHints_1 = require("../../lib/xchangeRecordHints");
|
|
|
11
11
|
const sqliteRecordStore_1 = require("../xchange/sqliteRecordStore");
|
|
12
12
|
const client_1 = require("../terminal/client");
|
|
13
13
|
const transportUtils_1 = require("./transportUtils");
|
|
14
|
+
const bodyLimits_1 = require("../../lib/bodyLimits");
|
|
14
15
|
class TransportFileHandoffActions {
|
|
15
16
|
host;
|
|
16
17
|
constructor(host) {
|
|
@@ -316,12 +317,12 @@ class TransportFileHandoffActions {
|
|
|
316
317
|
...(meta?.relativePath ? { relativePath: meta.relativePath } : {}),
|
|
317
318
|
...(meta?.storageRef ? { storageRef: meta.storageRef } : {}),
|
|
318
319
|
});
|
|
319
|
-
const fileContent = await (0, client_1.readWorkspaceFile)(this.host.config.terminal, this.host.objectStore.resolveWorkspaceDir(session), localFilePath);
|
|
320
|
+
const fileContent = await (0, client_1.readWorkspaceFile)(this.host.config.terminal, this.host.objectStore.resolveWorkspaceDir(session), localFilePath, bodyLimits_1.MAX_BASE64_SOURCE_SIZE_BYTES);
|
|
320
321
|
const handoffSummary = input.description
|
|
321
322
|
.split(/\r?\n/u)
|
|
322
323
|
.map((line) => line.trim())
|
|
323
324
|
.find(Boolean) ?? `Передача файла: ${fileName}`;
|
|
324
|
-
|
|
325
|
+
const note = {
|
|
325
326
|
session_id: input.sessionId,
|
|
326
327
|
...(input.targetSessionId ? { target_session_id: input.targetSessionId } : {}),
|
|
327
328
|
...(input.projectUuid ? { project_uuid: input.projectUuid } : {}),
|
|
@@ -346,7 +347,9 @@ class TransportFileHandoffActions {
|
|
|
346
347
|
content_base64: node_buffer_1.Buffer.from(fileContent).toString("base64"),
|
|
347
348
|
},
|
|
348
349
|
],
|
|
349
|
-
}
|
|
350
|
+
};
|
|
351
|
+
(0, bodyLimits_1.assertSerializedBodySize)(note);
|
|
352
|
+
return this.host.sendPartnerNote(note);
|
|
350
353
|
}
|
|
351
354
|
}
|
|
352
355
|
exports.TransportFileHandoffActions = TransportFileHandoffActions;
|
package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMessageFlow.js
CHANGED
|
@@ -73,7 +73,7 @@ class TransportMessageFlow {
|
|
|
73
73
|
await this.host.replyText(ctx, await this.host.tForContext(ctx, "menu:admin.auth.disabled"), { sessionId: "gateway-auth", kind: "transport" });
|
|
74
74
|
return true;
|
|
75
75
|
}
|
|
76
|
-
if (authToken !== this.host.config.distributed.
|
|
76
|
+
if (authToken !== this.host.config.distributed.gatewayScopeToken) {
|
|
77
77
|
await this.host.replyText(ctx, await this.host.tForContext(ctx, "menu:admin.auth.invalid"), { sessionId: "gateway-auth", kind: "transport" });
|
|
78
78
|
return true;
|
|
79
79
|
}
|
|
@@ -46,8 +46,8 @@ class TransportProjectState {
|
|
|
46
46
|
this.host.config.telegram.botUsername ||
|
|
47
47
|
"telegram-mcp client",
|
|
48
48
|
bot_username: this.host.config.telegram.botUsername,
|
|
49
|
-
...(this.host.config.distributed.
|
|
50
|
-
? { gateway_token: this.host.config.distributed.
|
|
49
|
+
...(this.host.config.distributed.gatewayScopeToken
|
|
50
|
+
? { gateway_token: this.host.config.distributed.gatewayScopeToken }
|
|
51
51
|
: {}),
|
|
52
52
|
meta: {
|
|
53
53
|
telegram_chat_id: principal.telegramChatId,
|
|
@@ -9,6 +9,7 @@ exports.writeXchangeRelativeFile = writeXchangeRelativeFile;
|
|
|
9
9
|
exports.listXchangeFiles = listXchangeFiles;
|
|
10
10
|
exports.deleteXchangeFile = deleteXchangeFile;
|
|
11
11
|
exports.readWorkspaceFile = readWorkspaceFile;
|
|
12
|
+
exports.resolveWorkspaceFileForRead = resolveWorkspaceFileForRead;
|
|
12
13
|
exports.isTerminalUnavailableError = isTerminalUnavailableError;
|
|
13
14
|
exports.isTerminalTargetInvalidError = isTerminalTargetInvalidError;
|
|
14
15
|
exports.getConfiguredTerminalShell = getConfiguredTerminalShell;
|
|
@@ -31,6 +32,7 @@ exports.subscribeForegroundTerminal = subscribeForegroundTerminal;
|
|
|
31
32
|
exports.stopAllForegroundTerminals = stopAllForegroundTerminals;
|
|
32
33
|
const promises_1 = require("node:fs/promises");
|
|
33
34
|
const node_path_1 = __importDefault(require("node:path"));
|
|
35
|
+
const bodyLimits_1 = require("../../lib/bodyLimits");
|
|
34
36
|
const ptyRegistry_1 = require("../terminal/ptyRegistry");
|
|
35
37
|
const ENTER_AFTER_PASTE_DELAY_MS = 75;
|
|
36
38
|
function toPtyConfig(config) {
|
|
@@ -93,9 +95,7 @@ async function allocateAvailableFilePath(dir, fileName) {
|
|
|
93
95
|
const extension = node_path_1.default.extname(safeFileName);
|
|
94
96
|
const baseName = node_path_1.default.basename(safeFileName, extension);
|
|
95
97
|
for (let attempt = 0; attempt < 1000; attempt += 1) {
|
|
96
|
-
const candidateName = attempt === 0
|
|
97
|
-
? safeFileName
|
|
98
|
-
: `${baseName}--${attempt}${extension}`;
|
|
98
|
+
const candidateName = attempt === 0 ? safeFileName : `${baseName}--${attempt}${extension}`;
|
|
99
99
|
const candidatePath = node_path_1.default.join(dir, candidateName);
|
|
100
100
|
try {
|
|
101
101
|
await (0, promises_1.access)(candidatePath);
|
|
@@ -172,9 +172,32 @@ async function deleteXchangeFile(config, workspaceDir, exchangeDirName, filePath
|
|
|
172
172
|
await (0, promises_1.rm)(resolvedFilePath, { force: true });
|
|
173
173
|
return true;
|
|
174
174
|
}
|
|
175
|
-
async function readWorkspaceFile(config, workspaceDir, filePath) {
|
|
175
|
+
async function readWorkspaceFile(config, workspaceDir, filePath, maxBytes = bodyLimits_1.MAX_BODY_SIZE_BYTES) {
|
|
176
|
+
const resolved = await resolveWorkspaceFileForRead(config, workspaceDir, filePath, maxBytes);
|
|
177
|
+
return (0, promises_1.readFile)(resolved.filePath);
|
|
178
|
+
}
|
|
179
|
+
async function resolveWorkspaceFileForRead(config, workspaceDir, filePath, maxBytes = bodyLimits_1.MAX_BODY_SIZE_BYTES) {
|
|
180
|
+
void config;
|
|
176
181
|
const resolvedFilePath = resolvePathInsideWorkspace(workspaceDir, filePath);
|
|
177
|
-
|
|
182
|
+
const [realWorkspaceDir, realFilePath] = await Promise.all([
|
|
183
|
+
(0, promises_1.realpath)(node_path_1.default.resolve(workspaceDir)),
|
|
184
|
+
(0, promises_1.realpath)(resolvedFilePath),
|
|
185
|
+
]);
|
|
186
|
+
const relative = node_path_1.default.relative(realWorkspaceDir, realFilePath);
|
|
187
|
+
if (relative.startsWith("..") ||
|
|
188
|
+
node_path_1.default.isAbsolute(relative) ||
|
|
189
|
+
relative.trim() === "") {
|
|
190
|
+
throw new Error("File path is outside the workspace directory.");
|
|
191
|
+
}
|
|
192
|
+
const fileStats = await (0, promises_1.stat)(realFilePath);
|
|
193
|
+
if (!fileStats.isFile()) {
|
|
194
|
+
throw new Error("File path does not point to a regular file.");
|
|
195
|
+
}
|
|
196
|
+
(0, bodyLimits_1.assertBodySize)(fileStats.size, maxBytes);
|
|
197
|
+
return {
|
|
198
|
+
filePath: realFilePath,
|
|
199
|
+
sizeBytes: fileStats.size,
|
|
200
|
+
};
|
|
178
201
|
}
|
|
179
202
|
function isTerminalUnavailableError(error) {
|
|
180
203
|
const message = error instanceof Error ? (error.stack ?? error.message) : String(error);
|
|
@@ -202,7 +225,7 @@ function ensureTerminalTargetForSession(config, input) {
|
|
|
202
225
|
}
|
|
203
226
|
async function resolveTerminalTargetFromHint(config, hint) {
|
|
204
227
|
const target = hint.terminalTarget?.trim() || (0, ptyRegistry_1.buildPtyTarget)("default");
|
|
205
|
-
return (0, ptyRegistry_1.hasPtyTarget)(target) ? target : hint.terminalTarget?.trim() ?? null;
|
|
228
|
+
return (0, ptyRegistry_1.hasPtyTarget)(target) ? target : (hint.terminalTarget?.trim() ?? null);
|
|
206
229
|
}
|
|
207
230
|
async function getTerminalWindowHeight(config, target) {
|
|
208
231
|
return (0, ptyRegistry_1.getPtyWindowHeight)(target);
|