@deadragdoll/tellymcp 0.0.14 → 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 +19 -39
- package/.env.example.gateway +40 -64
- package/CHANGELOG.md +27 -0
- package/README-ru.md +66 -6
- package/README.md +66 -6
- package/TOOLS.md +38 -1
- package/config/templates/env.both.template +4 -5
- package/config/templates/env.client.template +4 -17
- package/config/templates/env.gateway.template +4 -29
- package/dist/cli.js +225 -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/collaboration.service.js +2 -2
- package/dist/services/features/telegram-mcp/ensuredb.service.js +1 -1
- package/dist/services/features/telegram-mcp/gateway.service.js +1 -1
- package/dist/services/features/telegram-mcp/mcp-server.service.js +2 -0
- package/dist/services/features/telegram-mcp/session-context.service.js +25 -1
- package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +70 -66
- package/dist/services/features/telegram-mcp/src/app/config/env.js +10 -36
- package/dist/services/features/telegram-mcp/src/app/config/environmentContract.js +66 -0
- package/dist/services/features/telegram-mcp/src/entities/request/model/schema.js +28 -2
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +2 -2
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/gatewaySessionsService.js +5 -5
- package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayClientAccess.js +1 -1
- 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 +0 -1
- package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/remoteConsoleActionClient.js +4 -3
- package/dist/services/features/telegram-mcp/src/features/notify/model/notifyService.js +4 -4
- 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 +1 -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/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/lib/gatewayScope.js +5 -5
- package/dist/services/features/telegram-mcp/src/shared/lib/project-identity/projectIdentity.js +10 -0
- package/docs/STANDALONE-ru.md +29 -4
- package/docs/STANDALONE.md +29 -4
- package/package.json +1 -1
- package/scripts/postinstall.js +33 -1
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;
|
|
@@ -218,7 +218,7 @@ class TelegramTransport {
|
|
|
218
218
|
}
|
|
219
219
|
isAdminAuthEnabled() {
|
|
220
220
|
return (this.isAdminBotProfile() &&
|
|
221
|
-
Boolean(this.config.distributed.
|
|
221
|
+
Boolean(this.config.distributed.gatewayScopeToken?.trim()));
|
|
222
222
|
}
|
|
223
223
|
isAdminBotProfile() {
|
|
224
224
|
return this.config.distributed.mode === "gateway";
|
|
@@ -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 : [];
|
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,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.normalizeGatewayToken = normalizeGatewayToken;
|
|
4
|
-
exports.
|
|
4
|
+
exports.gatewayScopeTokenToScopeKey = gatewayScopeTokenToScopeKey;
|
|
5
5
|
exports.resolveGatewayScopeKey = resolveGatewayScopeKey;
|
|
6
6
|
const node_crypto_1 = require("node:crypto");
|
|
7
7
|
function normalizeGatewayToken(value) {
|
|
@@ -11,13 +11,13 @@ function normalizeGatewayToken(value) {
|
|
|
11
11
|
const token = value.trim();
|
|
12
12
|
return token ? token : null;
|
|
13
13
|
}
|
|
14
|
-
function
|
|
14
|
+
function gatewayScopeTokenToScopeKey(token) {
|
|
15
15
|
return (0, node_crypto_1.createHash)("sha256").update(token).digest("hex");
|
|
16
16
|
}
|
|
17
17
|
function resolveGatewayScopeKey(input) {
|
|
18
|
-
const
|
|
19
|
-
if (
|
|
20
|
-
return
|
|
18
|
+
const gatewayScopeToken = normalizeGatewayToken(input.gateway_token);
|
|
19
|
+
if (gatewayScopeToken) {
|
|
20
|
+
return gatewayScopeTokenToScopeKey(gatewayScopeToken);
|
|
21
21
|
}
|
|
22
22
|
const scopeKey = normalizeGatewayToken(input.scope_key);
|
|
23
23
|
return scopeKey;
|
package/dist/services/features/telegram-mcp/src/shared/lib/project-identity/projectIdentity.js
CHANGED
|
@@ -117,6 +117,10 @@ function readSessionMarkerState(inputCwd, logger) {
|
|
|
117
117
|
parsed.last_notified_tools_hash.trim()
|
|
118
118
|
? { lastNotifiedToolsHash: parsed.last_notified_tools_hash.trim() }
|
|
119
119
|
: {}),
|
|
120
|
+
...(typeof parsed.gateway_client_uuid === "string" &&
|
|
121
|
+
parsed.gateway_client_uuid.trim()
|
|
122
|
+
? { gatewayClientUuid: parsed.gateway_client_uuid.trim() }
|
|
123
|
+
: {}),
|
|
120
124
|
...(typeof parsed.updated_at === "string" && parsed.updated_at.trim()
|
|
121
125
|
? { updatedAt: parsed.updated_at.trim() }
|
|
122
126
|
: {}),
|
|
@@ -166,6 +170,12 @@ function writeSessionMarkerState(input) {
|
|
|
166
170
|
: current?.lastNotifiedToolsHash
|
|
167
171
|
? { last_notified_tools_hash: current.lastNotifiedToolsHash }
|
|
168
172
|
: {}),
|
|
173
|
+
...(typeof input.gatewayClientUuid === "string" &&
|
|
174
|
+
input.gatewayClientUuid.trim()
|
|
175
|
+
? { gateway_client_uuid: input.gatewayClientUuid.trim() }
|
|
176
|
+
: current?.gatewayClientUuid
|
|
177
|
+
? { gateway_client_uuid: current.gatewayClientUuid }
|
|
178
|
+
: {}),
|
|
169
179
|
created_at: now,
|
|
170
180
|
updated_at: now,
|
|
171
181
|
...(current ? { first_seen_local_session_id: current.localSessionId } : {}),
|
package/docs/STANDALONE-ru.md
CHANGED
|
@@ -24,7 +24,17 @@
|
|
|
24
24
|
## 1. Установка
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
|
|
27
|
+
sudo apt install -y python3 make g++
|
|
28
|
+
npm config set ignore-scripts false
|
|
29
|
+
npm install -g @deadragdoll/tellymcp --foreground-scripts
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
На Linux native addon `node-pty` собирается локально; для Linux ARM64 это
|
|
33
|
+
обязательно, потому что зависимость не поставляет подходящий готовый binary.
|
|
34
|
+
Если после предыдущей установки `pty.node` отсутствует, выполни:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm rebuild -g @deadragdoll/tellymcp --foreground-scripts
|
|
28
38
|
```
|
|
29
39
|
|
|
30
40
|
Опционально:
|
|
@@ -64,7 +74,7 @@ DB_PASSWORD=
|
|
|
64
74
|
DB_NAME=
|
|
65
75
|
GATEWAY_PUBLIC_URL=https://your-domain.example/api/gateway
|
|
66
76
|
GATEWAY_WS_URL=wss://your-domain.example/api/gateway/ws
|
|
67
|
-
|
|
77
|
+
GATEWAY_SCOPE_TOKEN=change_me_scope_token
|
|
68
78
|
GATEWAY_AUTH_TOKEN=put_strong_shared_transport_token_here
|
|
69
79
|
ROOT_PREFIX=/api
|
|
70
80
|
PORT=8080
|
|
@@ -83,13 +93,16 @@ DISTRIBUTED_MODE=gateway
|
|
|
83
93
|
DISTRIBUTED_MODE=client
|
|
84
94
|
GATEWAY_PUBLIC_URL=https://your-domain.example/api/gateway
|
|
85
95
|
GATEWAY_WS_URL=wss://your-domain.example/api/gateway/ws
|
|
86
|
-
|
|
96
|
+
GATEWAY_SCOPE_TOKEN=change_me_scope_token
|
|
87
97
|
GATEWAY_AUTH_TOKEN=put_strong_shared_transport_token_here
|
|
88
98
|
GATEWAY_USER_UUID=put_owner_uuid_here
|
|
89
99
|
```
|
|
90
100
|
|
|
101
|
+
На машине agent/client Redis не нужен. Временное runtime-состояние хранится
|
|
102
|
+
локально, а стабильный gateway client UUID — в `.mcpsession.json`.
|
|
103
|
+
|
|
91
104
|
Используйте один и тот же стойкий `GATEWAY_AUTH_TOKEN` на gateway и всех клиентах.
|
|
92
|
-
Не смешивайте его с `
|
|
105
|
+
Не смешивайте его с `GATEWAY_SCOPE_TOKEN`: последний разделяет данные gateway по scope,
|
|
93
106
|
но не аутентифицирует HTTP- или WebSocket-транспорт. Сгенерируйте токен один раз,
|
|
94
107
|
например командой `openssl rand -hex 32`, и не используйте пример значения выше.
|
|
95
108
|
|
|
@@ -192,10 +205,22 @@ http://127.0.0.1:8787/mcp
|
|
|
192
205
|
|
|
193
206
|
## 9. Проверки
|
|
194
207
|
|
|
208
|
+
Настройка dotenv через локальный web-конфигуратор:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
tellymcp configure
|
|
212
|
+
```
|
|
213
|
+
|
|
195
214
|
```bash
|
|
196
215
|
tellymcp doctor --env .env
|
|
197
216
|
```
|
|
198
217
|
|
|
218
|
+
Перед запуском старый env можно нормализовать командой:
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
tellymcp migrate-env ./old.env > ./.migrated-env
|
|
222
|
+
```
|
|
223
|
+
|
|
199
224
|
Разрушительная очистка:
|
|
200
225
|
|
|
201
226
|
```bash
|
package/docs/STANDALONE.md
CHANGED
|
@@ -24,7 +24,17 @@ The recommended topology is:
|
|
|
24
24
|
## 1. Install
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
|
|
27
|
+
sudo apt install -y python3 make g++
|
|
28
|
+
npm config set ignore-scripts false
|
|
29
|
+
npm install -g @deadragdoll/tellymcp --foreground-scripts
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Linux installs compile the native `node-pty` addon locally; this is required on
|
|
33
|
+
Linux ARM64 because the dependency does not ship a matching prebuilt binary.
|
|
34
|
+
If `pty.node` is missing after an earlier install, run:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm rebuild -g @deadragdoll/tellymcp --foreground-scripts
|
|
28
38
|
```
|
|
29
39
|
|
|
30
40
|
Optional:
|
|
@@ -64,7 +74,7 @@ DB_PASSWORD=
|
|
|
64
74
|
DB_NAME=
|
|
65
75
|
GATEWAY_PUBLIC_URL=https://your-domain.example/api/gateway
|
|
66
76
|
GATEWAY_WS_URL=wss://your-domain.example/api/gateway/ws
|
|
67
|
-
|
|
77
|
+
GATEWAY_SCOPE_TOKEN=change_me_scope_token
|
|
68
78
|
GATEWAY_AUTH_TOKEN=put_strong_shared_transport_token_here
|
|
69
79
|
ROOT_PREFIX=/api
|
|
70
80
|
PORT=8080
|
|
@@ -83,13 +93,16 @@ Minimum client settings:
|
|
|
83
93
|
DISTRIBUTED_MODE=client
|
|
84
94
|
GATEWAY_PUBLIC_URL=https://your-domain.example/api/gateway
|
|
85
95
|
GATEWAY_WS_URL=wss://your-domain.example/api/gateway/ws
|
|
86
|
-
|
|
96
|
+
GATEWAY_SCOPE_TOKEN=change_me_scope_token
|
|
87
97
|
GATEWAY_AUTH_TOKEN=put_strong_shared_transport_token_here
|
|
88
98
|
GATEWAY_USER_UUID=put_owner_uuid_here
|
|
89
99
|
```
|
|
90
100
|
|
|
101
|
+
The agent/client machine does not need Redis. Its transient runtime state is
|
|
102
|
+
local, while the stable gateway client UUID is stored in `.mcpsession.json`.
|
|
103
|
+
|
|
91
104
|
Use the same strong `GATEWAY_AUTH_TOKEN` on the gateway and every client. Keep it
|
|
92
|
-
separate from `
|
|
105
|
+
separate from `GATEWAY_SCOPE_TOKEN`, which scopes gateway data but does not authenticate
|
|
93
106
|
the HTTP or WebSocket transport. Generate it once, for example with
|
|
94
107
|
`openssl rand -hex 32`, and do not use the illustrative value above.
|
|
95
108
|
|
|
@@ -192,10 +205,22 @@ Use the MCP HTTP endpoint exposed by `tellymcp run`.
|
|
|
192
205
|
|
|
193
206
|
## 9. Health Checks
|
|
194
207
|
|
|
208
|
+
Guided dotenv setup:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
tellymcp configure
|
|
212
|
+
```
|
|
213
|
+
|
|
195
214
|
```bash
|
|
196
215
|
tellymcp doctor --env .env
|
|
197
216
|
```
|
|
198
217
|
|
|
218
|
+
Normalize a legacy env before startup:
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
tellymcp migrate-env ./old.env > ./.migrated-env
|
|
222
|
+
```
|
|
223
|
+
|
|
199
224
|
Destructive cleanup:
|
|
200
225
|
|
|
201
226
|
```bash
|
package/package.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -15,11 +15,43 @@ function line(value = "") {
|
|
|
15
15
|
process.stdout.write(`${value}\n`);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
function checkNativePty() {
|
|
19
|
+
try {
|
|
20
|
+
const nodePty = require("node-pty");
|
|
21
|
+
return typeof nodePty.spawn === "function"
|
|
22
|
+
? { available: true }
|
|
23
|
+
: {
|
|
24
|
+
available: false,
|
|
25
|
+
message: "node-pty loaded without a spawn function.",
|
|
26
|
+
};
|
|
27
|
+
} catch (error) {
|
|
28
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
29
|
+
return {
|
|
30
|
+
available: false,
|
|
31
|
+
message: message.split("\n", 1)[0] || "node-pty failed to load.",
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
18
36
|
line();
|
|
19
37
|
line(`${pc.bold(pc.cyan("TellyMCP"))} ${pc.dim("installed")}`);
|
|
20
38
|
line();
|
|
21
39
|
|
|
22
|
-
|
|
40
|
+
const nativePty = checkNativePty();
|
|
41
|
+
if (nativePty.available) {
|
|
42
|
+
line(`${pc.green("OK")} Built-in PTY native module loaded.`);
|
|
43
|
+
} else {
|
|
44
|
+
line(`${pc.red("ERROR")} Built-in PTY native module is unavailable.`);
|
|
45
|
+
line(` ${nativePty.message}`);
|
|
46
|
+
line(
|
|
47
|
+
` Platform: ${process.platform}-${process.arch}, Node ${process.versions.node}`,
|
|
48
|
+
);
|
|
49
|
+
if (process.platform === "linux") {
|
|
50
|
+
line(" Debian/Ubuntu prerequisites: sudo apt install -y python3 make g++");
|
|
51
|
+
}
|
|
52
|
+
line(" Ensure scripts are enabled: npm config set ignore-scripts false");
|
|
53
|
+
line(" Rebuild: npm rebuild -g @deadragdoll/tellymcp --foreground-scripts");
|
|
54
|
+
}
|
|
23
55
|
|
|
24
56
|
line();
|
|
25
57
|
line(`${pc.yellow("INFO")} Browser tools need Playwright browser binaries.`);
|