@deadragdoll/tellymcp 0.0.9 → 0.0.11
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 +42 -50
- package/.env.example.gateway +49 -61
- package/CHANGELOG.md +3 -3
- package/README-ru.md +205 -384
- package/README.md +195 -1194
- package/TOOLS.md +294 -377
- package/VERSION.md +36 -10
- package/config/codex/plugins/telly-workflows/.codex-plugin/plugin.json +18 -0
- package/config/codex/plugins/telly-workflows/references/invariants.md +10 -0
- package/config/codex/plugins/telly-workflows/skills/telly-browser-screenshot/SKILL.md +27 -0
- package/config/codex/plugins/telly-workflows/skills/telly-collab-artifact/SKILL.md +22 -0
- package/config/codex/plugins/telly-workflows/skills/telly-human-telegram/SKILL.md +31 -0
- package/config/codex/plugins/telly-workflows/skills/telly-partner-note/SKILL.md +36 -0
- package/config/templates/env.both.template +40 -20
- package/config/templates/env.client.template +34 -18
- package/config/templates/env.gateway.template +38 -22
- package/dist/cli.js +322 -75
- package/dist/codexPluginInstaller.js +215 -0
- package/dist/lib/mixins/session.errors.js +34 -1
- package/dist/lib/pinoTargets.js +2 -2
- package/dist/moleculer.config.js +7 -5
- package/dist/services/features/telegram-mcp/approval.service.js +1 -1
- package/dist/services/features/telegram-mcp/browser.service.js +95 -2
- package/dist/services/features/telegram-mcp/collaboration.service.js +41 -4
- package/dist/services/features/telegram-mcp/ensuredb.service.js +145 -26
- package/dist/services/features/telegram-mcp/gateway-delivery.service.js +285 -103
- package/dist/services/features/telegram-mcp/gateway-rmq.service.js +3 -2
- package/dist/services/features/telegram-mcp/gateway-socket.service.js +905 -87
- package/dist/services/features/telegram-mcp/gateway.service.js +876 -81
- package/dist/services/features/telegram-mcp/mcp-http.service.js +9 -1
- package/dist/services/features/telegram-mcp/mcp-server.service.js +17 -26
- package/dist/services/features/telegram-mcp/notify.service.js +128 -2
- package/dist/services/features/telegram-mcp/runtime.service.js +27 -7
- package/dist/services/features/telegram-mcp/session-context.service.js +29 -2
- package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +133 -10
- package/dist/services/features/telegram-mcp/src/app/config/env.js +161 -51
- package/dist/services/features/telegram-mcp/src/app/http.js +375 -42
- package/dist/services/features/telegram-mcp/src/app/webapp/assets.js +386 -58
- package/dist/services/features/telegram-mcp/src/app/webapp/auth.js +3 -0
- package/dist/services/features/telegram-mcp/src/app/webapp/terminal.js +12 -0
- package/dist/services/features/telegram-mcp/src/entities/request/model/schema.js +116 -104
- package/dist/services/features/telegram-mcp/src/entities/xchange/model/types.js +2 -0
- package/dist/services/features/telegram-mcp/src/features/ask-user/model/askUserTelegram.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserScreenshotTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +249 -19
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/gatewaySessionsService.js +71 -0
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/listGatewaySessionsTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/localCollaborationBackend.js +93 -82
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileService.js +47 -5
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerNoteTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayClientAccess.js +82 -0
- package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayCollaborationBackend.js +22 -14
- package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayHttpService.js +649 -75
- package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/remoteConsoleActionClient.js +76 -0
- package/dist/services/features/telegram-mcp/src/features/embedded-runtime/model/embeddedRuntimeBroker.js +92 -0
- package/dist/services/features/telegram-mcp/src/features/foreground-terminal/model/foregroundTerminalRuntime.js +192 -0
- package/dist/services/features/telegram-mcp/src/features/notify/model/notifyService.js +382 -3
- package/dist/services/features/telegram-mcp/src/features/notify/model/notifyTelegramTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/notify/model/sendFileToTelegramTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/session-context/model/clearSessionContextTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/session-context/model/getSessionContextTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/session-context/model/renameSessionTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/session-context/model/sessionContextService.js +42 -200
- package/dist/services/features/telegram-mcp/src/features/session-context/model/setSessionContextTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/terminal-buffer/model/terminalBufferService.js +96 -0
- package/dist/services/features/telegram-mcp/src/features/terminal-input/model/terminalInputService.js +97 -0
- package/dist/services/features/telegram-mcp/src/features/tools-sync/model/refreshToolsMarkdownService.js +107 -58
- package/dist/services/features/telegram-mcp/src/features/tools-sync/model/refreshToolsMarkdownTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/xchange/model/getXchangeRecordTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/xchange/model/listXchangeRecordsTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/xchange/model/markXchangeRecordReadTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/xchange/model/xchangeService.js +169 -0
- package/dist/services/features/telegram-mcp/src/processes/human-approval/model/orchestrator.js +5 -4
- package/dist/services/features/telegram-mcp/src/shared/i18n/index.js +46 -0
- package/dist/services/features/telegram-mcp/src/shared/i18n/resources/en.js +652 -0
- package/dist/services/features/telegram-mcp/src/shared/i18n/resources/ru.js +652 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/memory/processLocalSessionStore.js +27 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/object-storage/minioExchangeStore.js +11 -8
- package/dist/services/features/telegram-mcp/src/shared/integrations/redis/stateStore.js +51 -71
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/proxyFetch.js +21 -22
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transport.js +411 -6524
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportAttachmentStore.js +93 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportBroadcastActions.js +385 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportConsoleRegistry.js +143 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportConstructorWiring.js +633 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportContent.js +84 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportContext.js +78 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportDocumentActions.js +36 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportEventActions.js +292 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportFileHandoffActions.js +352 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportFormatting.js +75 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportGatewayActions.js +44 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportLifecycleActions.js +161 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportLive.js +56 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportLiveActions.js +77 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuCallbacks.js +254 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuFactories.js +538 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuFingerprints.js +93 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuFlow.js +344 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuShell.js +62 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuState.js +408 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMenuText.js +216 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportMessageFlow.js +452 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportOutputActions.js +189 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportPartnerActions.js +286 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportPayloadState.js +101 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectActions.js +463 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectEntryActions.js +202 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectEvents.js +99 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectMenus.js +138 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectState.js +308 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportProjectView.js +426 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportRequestFlow.js +278 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportSessionActions.js +143 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportTerminalActions.js +468 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportTerminalRuntime.js +171 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportTypes.js +2 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportUtils.js +330 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportXchangeState.js +107 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/client.js +255 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/ptyRegistry.js +543 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/xchange/sqliteRecordStore.js +223 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/gatewayScope.js +24 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/logger/logger.js +6 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/project-identity/projectIdentity.js +147 -76
- package/dist/services/features/telegram-mcp/src/shared/lib/telegramXchangeRecords.js +72 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/terminalPromptDetection.js +237 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/version/versionHandshake.js +129 -1
- package/dist/services/features/telegram-mcp/src/shared/lib/xchangeRecordHints.js +98 -0
- package/dist/services/features/telegram-mcp/standalone-http.service.js +13 -1
- package/dist/services/features/telegram-mcp/terminal-buffer.service.js +42 -0
- package/dist/services/features/telegram-mcp/terminal-input.service.js +41 -0
- package/dist/services/features/telegram-mcp/tools-sync.service.js +16 -2
- package/dist/services/features/telegram-mcp/xchange.service.js +123 -0
- package/docs/STANDALONE-ru.md +172 -0
- package/docs/STANDALONE.md +172 -0
- package/package.json +11 -5
- package/scripts/build-package-artifact.sh +27 -0
- package/scripts/deploy-gateway.sh +64 -0
- package/scripts/deploy-onebot-branch.sh +27 -0
- package/scripts/postinstall.js +11 -36
- package/STANDALONE-ru.md +0 -274
- package/STANDALONE.md +0 -274
- package/dist/services/features/telegram-mcp/inbox.service.js +0 -33
- package/dist/services/features/telegram-mcp/pair.service.js +0 -33
- package/dist/services/features/telegram-mcp/src/app/webapp/tmux.js +0 -10
- package/dist/services/features/telegram-mcp/src/features/inbox/model/deleteTelegramInboxMessageTool.js +0 -33
- package/dist/services/features/telegram-mcp/src/features/inbox/model/getTelegramInboxCountTool.js +0 -33
- package/dist/services/features/telegram-mcp/src/features/inbox/model/getTelegramInboxTool.js +0 -33
- package/dist/services/features/telegram-mcp/src/features/inbox/model/inboxService.js +0 -77
- package/dist/services/features/telegram-mcp/src/features/pair-session/model/clearSessionPairingTool.js +0 -33
- package/dist/services/features/telegram-mcp/src/features/pair-session/model/createSessionPairCodeTool.js +0 -33
- package/dist/services/features/telegram-mcp/src/features/pair-session/model/generatePairCode.js +0 -202
- package/dist/services/features/telegram-mcp/src/features/session-context/model/getTmuxTargetTool.js +0 -33
- package/dist/services/features/telegram-mcp/src/features/session-context/model/setTmuxTargetTool.js +0 -33
- package/dist/services/features/telegram-mcp/src/shared/integrations/tmux/client.js +0 -363
|
@@ -8,6 +8,7 @@ const node_crypto_1 = require("node:crypto");
|
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
9
|
const db_1 = require("../../../lib/mixins/db");
|
|
10
10
|
const gatewayReplyResolution_1 = require("./src/features/distributed-gateway/model/gatewayReplyResolution");
|
|
11
|
+
const gatewayScope_1 = require("./src/shared/lib/gatewayScope");
|
|
11
12
|
const ensuredb_service_1 = require("./ensuredb.service");
|
|
12
13
|
exports.TELEGRAM_MCP_GATEWAY_SERVICE_NAME = "telegramMcp.gateway";
|
|
13
14
|
const MCP_SCHEMA = process.env.DB_SCHEME || "mcp";
|
|
@@ -63,12 +64,124 @@ const TelegramMcpGatewayService = {
|
|
|
63
64
|
}
|
|
64
65
|
return text;
|
|
65
66
|
},
|
|
67
|
+
async resolveOwnerUserUuidFilter(input) {
|
|
68
|
+
const explicitOwnerUserUuid = this.normalizeOptionalText?.(input.owner_user_uuid);
|
|
69
|
+
if (explicitOwnerUserUuid) {
|
|
70
|
+
return explicitOwnerUserUuid;
|
|
71
|
+
}
|
|
72
|
+
const telegramUserId = this.normalizeOptionalText?.(input.telegram_user_id);
|
|
73
|
+
if (!telegramUserId) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const user = await this.db
|
|
77
|
+
.withSchema(MCP_SCHEMA)
|
|
78
|
+
.table("gateway_users")
|
|
79
|
+
.where({ telegram_user_id: telegramUserId })
|
|
80
|
+
.first("gateway_user_uuid");
|
|
81
|
+
return user?.gateway_user_uuid
|
|
82
|
+
? String(user.gateway_user_uuid)
|
|
83
|
+
: "__missing_gateway_user__";
|
|
84
|
+
},
|
|
85
|
+
async upsertGatewayUserRecord(input) {
|
|
86
|
+
const telegramUserId = this.requireText?.(input.telegram_user_id, "telegram_user_id");
|
|
87
|
+
const telegramChatId = this.normalizeOptionalText?.(input.telegram_chat_id);
|
|
88
|
+
const telegramUsername = this.normalizeOptionalText?.(input.telegram_username);
|
|
89
|
+
const telegramDisplayName = this.normalizeOptionalText?.(input.telegram_display_name);
|
|
90
|
+
const now = new Date().toISOString();
|
|
91
|
+
const existing = await this.db
|
|
92
|
+
.withSchema(MCP_SCHEMA)
|
|
93
|
+
.table("gateway_users")
|
|
94
|
+
.where({ telegram_user_id: telegramUserId })
|
|
95
|
+
.first();
|
|
96
|
+
if (existing) {
|
|
97
|
+
await this.db
|
|
98
|
+
.withSchema(MCP_SCHEMA)
|
|
99
|
+
.table("gateway_users")
|
|
100
|
+
.where({ gateway_user_uuid: existing.gateway_user_uuid })
|
|
101
|
+
.update({
|
|
102
|
+
...(telegramChatId ? { telegram_chat_id: telegramChatId } : {}),
|
|
103
|
+
...(telegramUsername ? { telegram_username: telegramUsername } : {}),
|
|
104
|
+
...(telegramDisplayName ? { telegram_display_name: telegramDisplayName } : {}),
|
|
105
|
+
updated_at: now,
|
|
106
|
+
last_auth_at: now,
|
|
107
|
+
});
|
|
108
|
+
return {
|
|
109
|
+
gateway_user_uuid: String(existing.gateway_user_uuid),
|
|
110
|
+
created: false,
|
|
111
|
+
updated_at: now,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const gatewayUserUuid = (0, node_crypto_1.randomUUID)();
|
|
115
|
+
await this.db.withSchema(MCP_SCHEMA).table("gateway_users").insert({
|
|
116
|
+
gateway_user_uuid: gatewayUserUuid,
|
|
117
|
+
telegram_user_id: telegramUserId,
|
|
118
|
+
...(telegramChatId ? { telegram_chat_id: telegramChatId } : {}),
|
|
119
|
+
...(telegramUsername ? { telegram_username: telegramUsername } : {}),
|
|
120
|
+
...(telegramDisplayName ? { telegram_display_name: telegramDisplayName } : {}),
|
|
121
|
+
created_at: now,
|
|
122
|
+
updated_at: now,
|
|
123
|
+
last_auth_at: now,
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
gateway_user_uuid: gatewayUserUuid,
|
|
127
|
+
created: true,
|
|
128
|
+
updated_at: now,
|
|
129
|
+
};
|
|
130
|
+
},
|
|
131
|
+
async resolveGatewayUserRouteRecord(input) {
|
|
132
|
+
const explicitGatewayUserUuid = this.normalizeOptionalText?.(input.gateway_user_uuid);
|
|
133
|
+
const clientUuid = this.normalizeOptionalText?.(input.client_uuid);
|
|
134
|
+
let gatewayUserUuid = explicitGatewayUserUuid;
|
|
135
|
+
if (!gatewayUserUuid && clientUuid) {
|
|
136
|
+
const client = await this.db
|
|
137
|
+
.withSchema(MCP_SCHEMA)
|
|
138
|
+
.table("gateway_clients")
|
|
139
|
+
.where({ client_uuid: clientUuid })
|
|
140
|
+
.first("owner_user_uuid");
|
|
141
|
+
gatewayUserUuid =
|
|
142
|
+
client?.owner_user_uuid ? String(client.owner_user_uuid) : null;
|
|
143
|
+
}
|
|
144
|
+
if (!gatewayUserUuid) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
const user = await this.db
|
|
148
|
+
.withSchema(MCP_SCHEMA)
|
|
149
|
+
.table("gateway_users")
|
|
150
|
+
.where({ gateway_user_uuid: gatewayUserUuid })
|
|
151
|
+
.first("gateway_user_uuid", "telegram_user_id", "telegram_chat_id", "telegram_username", "telegram_display_name");
|
|
152
|
+
if (!user?.gateway_user_uuid || !user.telegram_user_id) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
gateway_user_uuid: String(user.gateway_user_uuid),
|
|
157
|
+
telegram_user_id: Number(user.telegram_user_id),
|
|
158
|
+
telegram_chat_id: typeof user.telegram_chat_id === "number"
|
|
159
|
+
? user.telegram_chat_id
|
|
160
|
+
: user.telegram_chat_id
|
|
161
|
+
? Number(user.telegram_chat_id)
|
|
162
|
+
: null,
|
|
163
|
+
...(user.telegram_username
|
|
164
|
+
? { telegram_username: String(user.telegram_username) }
|
|
165
|
+
: {}),
|
|
166
|
+
...(user.telegram_display_name
|
|
167
|
+
? { telegram_display_name: String(user.telegram_display_name) }
|
|
168
|
+
: {}),
|
|
169
|
+
};
|
|
170
|
+
},
|
|
66
171
|
async registerClientRecord(input) {
|
|
67
172
|
const clientUuid = this.normalizeOptionalText?.(input.client_uuid) || (0, node_crypto_1.randomUUID)();
|
|
68
173
|
const now = new Date().toISOString();
|
|
174
|
+
const scopeKey = (0, gatewayScope_1.resolveGatewayScopeKey)(input);
|
|
69
175
|
const clientLabel = this.normalizeOptionalText?.(input.client_label);
|
|
70
176
|
const botUsername = this.normalizeOptionalText?.(input.bot_username);
|
|
71
177
|
const tokenFingerprint = this.normalizeOptionalText?.(input.bot_token_fingerprint);
|
|
178
|
+
const ownerUserUuid = this.normalizeOptionalText?.(input.owner_user_uuid) ||
|
|
179
|
+
(input.meta &&
|
|
180
|
+
typeof input.meta === "object" &&
|
|
181
|
+
!Array.isArray(input.meta) &&
|
|
182
|
+
typeof input.meta.gateway_user_uuid === "string"
|
|
183
|
+
? this.normalizeOptionalText?.(input.meta.gateway_user_uuid)
|
|
184
|
+
: null);
|
|
72
185
|
const meta = input.meta && typeof input.meta === "object" && !Array.isArray(input.meta)
|
|
73
186
|
? input.meta
|
|
74
187
|
: {};
|
|
@@ -78,11 +191,17 @@ const TelegramMcpGatewayService = {
|
|
|
78
191
|
.where({ client_uuid: clientUuid })
|
|
79
192
|
.first();
|
|
80
193
|
if (existing) {
|
|
194
|
+
const existingScopeKey = this.normalizeOptionalText?.(existing.scope_key);
|
|
195
|
+
if (existingScopeKey && scopeKey && existingScopeKey !== scopeKey) {
|
|
196
|
+
throw new Error("Client is already bound to a different gateway scope.");
|
|
197
|
+
}
|
|
81
198
|
await this.db
|
|
82
199
|
.withSchema(MCP_SCHEMA)
|
|
83
200
|
.table("gateway_clients")
|
|
84
201
|
.where({ client_uuid: clientUuid })
|
|
85
202
|
.update({
|
|
203
|
+
...(ownerUserUuid ? { owner_user_uuid: ownerUserUuid } : {}),
|
|
204
|
+
...(scopeKey ? { scope_key: scopeKey } : {}),
|
|
86
205
|
...(clientLabel ? { client_label: clientLabel } : {}),
|
|
87
206
|
...(botUsername ? { bot_username: botUsername } : {}),
|
|
88
207
|
...(tokenFingerprint ? { bot_token_fingerprint: tokenFingerprint } : {}),
|
|
@@ -98,6 +217,8 @@ const TelegramMcpGatewayService = {
|
|
|
98
217
|
}
|
|
99
218
|
await this.db.withSchema(MCP_SCHEMA).table("gateway_clients").insert({
|
|
100
219
|
client_uuid: clientUuid,
|
|
220
|
+
...(ownerUserUuid ? { owner_user_uuid: ownerUserUuid } : {}),
|
|
221
|
+
...(scopeKey ? { scope_key: scopeKey } : {}),
|
|
101
222
|
...(clientLabel ? { client_label: clientLabel } : {}),
|
|
102
223
|
...(botUsername ? { bot_username: botUsername } : {}),
|
|
103
224
|
...(tokenFingerprint ? { bot_token_fingerprint: tokenFingerprint } : {}),
|
|
@@ -112,9 +233,334 @@ const TelegramMcpGatewayService = {
|
|
|
112
233
|
updated_at: now,
|
|
113
234
|
};
|
|
114
235
|
},
|
|
236
|
+
async clearLiveConsolesRecord(input = {}) {
|
|
237
|
+
const connectionId = this.normalizeOptionalText?.(input.connection_id);
|
|
238
|
+
const clientUuid = this.normalizeOptionalText?.(input.client_uuid);
|
|
239
|
+
const query = this.db.withSchema(MCP_SCHEMA).table("gateway_live_consoles");
|
|
240
|
+
if (connectionId) {
|
|
241
|
+
query.where({ connection_id: connectionId });
|
|
242
|
+
}
|
|
243
|
+
if (clientUuid) {
|
|
244
|
+
query.where({ client_uuid: clientUuid });
|
|
245
|
+
}
|
|
246
|
+
const deleted = await query.del();
|
|
247
|
+
return { deleted };
|
|
248
|
+
},
|
|
249
|
+
async removeLiveConsolesRecord(input) {
|
|
250
|
+
const clientUuid = this.normalizeOptionalText?.(input.client_uuid);
|
|
251
|
+
const connectionId = this.normalizeOptionalText?.(input.connection_id);
|
|
252
|
+
if (!clientUuid && !connectionId) {
|
|
253
|
+
throw new Error("client_uuid or connection_id is required");
|
|
254
|
+
}
|
|
255
|
+
const query = this.db.withSchema(MCP_SCHEMA).table("gateway_live_consoles");
|
|
256
|
+
if (clientUuid) {
|
|
257
|
+
query.where({ client_uuid: clientUuid });
|
|
258
|
+
}
|
|
259
|
+
if (connectionId) {
|
|
260
|
+
query.where({ connection_id: connectionId });
|
|
261
|
+
}
|
|
262
|
+
const deleted = await query.del();
|
|
263
|
+
return { deleted };
|
|
264
|
+
},
|
|
265
|
+
async pruneGatewayStateRecord() {
|
|
266
|
+
const deleted = {};
|
|
267
|
+
const truncate = async (tableName) => {
|
|
268
|
+
deleted[tableName] = await this.db.withSchema(MCP_SCHEMA).table(tableName).del();
|
|
269
|
+
};
|
|
270
|
+
await truncate("gateway_live_consoles");
|
|
271
|
+
await truncate("gateway_session_links");
|
|
272
|
+
await truncate("gateway_deliveries");
|
|
273
|
+
await truncate("gateway_message_artifacts");
|
|
274
|
+
await truncate("gateway_messages");
|
|
275
|
+
await truncate("gateway_project_consoles");
|
|
276
|
+
await truncate("gateway_sessions");
|
|
277
|
+
await truncate("gateway_project_members");
|
|
278
|
+
await truncate("gateway_projects");
|
|
279
|
+
await truncate("gateway_clients");
|
|
280
|
+
await truncate("gateway_users");
|
|
281
|
+
return { deleted };
|
|
282
|
+
},
|
|
283
|
+
async syncLiveConsolesRecord(input) {
|
|
284
|
+
const clientUuid = this.requireText?.(input.client_uuid, "client_uuid");
|
|
285
|
+
const connectionId = this.requireText?.(input.connection_id, "connection_id");
|
|
286
|
+
const now = new Date().toISOString();
|
|
287
|
+
const gatewayUserUuid = this.normalizeOptionalText?.(input.gateway_user_uuid);
|
|
288
|
+
const clientLabel = this.normalizeOptionalText?.(input.client_label);
|
|
289
|
+
const systemUsername = this.normalizeOptionalText?.(input.system_username);
|
|
290
|
+
const namespace = this.normalizeOptionalText?.(input.namespace);
|
|
291
|
+
const nodeId = this.normalizeOptionalText?.(input.node_id);
|
|
292
|
+
const packageVersion = this.normalizeOptionalText?.(input.package_version);
|
|
293
|
+
const protocolVersion = this.normalizeOptionalText?.(input.protocol_version);
|
|
294
|
+
const sessionTools = Array.isArray(input.session_tools)
|
|
295
|
+
? input.session_tools.flatMap((item) => {
|
|
296
|
+
if (!item || typeof item !== "object") {
|
|
297
|
+
return [];
|
|
298
|
+
}
|
|
299
|
+
const record = item;
|
|
300
|
+
const localSessionId = this.normalizeOptionalText?.(record.local_session_id);
|
|
301
|
+
if (!localSessionId) {
|
|
302
|
+
return [];
|
|
303
|
+
}
|
|
304
|
+
return [
|
|
305
|
+
{
|
|
306
|
+
local_session_id: localSessionId,
|
|
307
|
+
session_label: this.normalizeOptionalText?.(record.session_label) ?? null,
|
|
308
|
+
tools_hash: this.normalizeOptionalText?.(record.tools_hash) ?? null,
|
|
309
|
+
cwd: this.normalizeOptionalText?.(record.cwd) ?? null,
|
|
310
|
+
},
|
|
311
|
+
];
|
|
312
|
+
})
|
|
313
|
+
: [];
|
|
314
|
+
await this.db
|
|
315
|
+
.withSchema(MCP_SCHEMA)
|
|
316
|
+
.table("gateway_clients")
|
|
317
|
+
.where({ client_uuid: clientUuid })
|
|
318
|
+
.update({
|
|
319
|
+
...(gatewayUserUuid ? { owner_user_uuid: gatewayUserUuid } : {}),
|
|
320
|
+
...(clientLabel ? { client_label: clientLabel } : {}),
|
|
321
|
+
updated_at: now,
|
|
322
|
+
last_seen_at: now,
|
|
323
|
+
});
|
|
324
|
+
await this.db
|
|
325
|
+
.withSchema(MCP_SCHEMA)
|
|
326
|
+
.table("gateway_live_consoles")
|
|
327
|
+
.where({ connection_id: connectionId })
|
|
328
|
+
.del();
|
|
329
|
+
for (const sessionTool of sessionTools) {
|
|
330
|
+
const existing = await this.db
|
|
331
|
+
.withSchema(MCP_SCHEMA)
|
|
332
|
+
.table("gateway_live_consoles")
|
|
333
|
+
.where({
|
|
334
|
+
client_uuid: clientUuid,
|
|
335
|
+
local_session_id: sessionTool.local_session_id,
|
|
336
|
+
})
|
|
337
|
+
.first("live_console_uuid", "connected_at");
|
|
338
|
+
const payload = {
|
|
339
|
+
connection_id: connectionId,
|
|
340
|
+
client_uuid: clientUuid,
|
|
341
|
+
local_session_id: sessionTool.local_session_id,
|
|
342
|
+
...(sessionTool.session_label
|
|
343
|
+
? { session_label: sessionTool.session_label }
|
|
344
|
+
: {}),
|
|
345
|
+
...(sessionTool.cwd ? { cwd: sessionTool.cwd } : {}),
|
|
346
|
+
...(sessionTool.tools_hash ? { tools_hash: sessionTool.tools_hash } : {}),
|
|
347
|
+
...(gatewayUserUuid ? { gateway_user_uuid: gatewayUserUuid } : {}),
|
|
348
|
+
...(clientLabel ? { client_label: clientLabel } : {}),
|
|
349
|
+
...(systemUsername ? { system_username: systemUsername } : {}),
|
|
350
|
+
...(namespace ? { namespace } : {}),
|
|
351
|
+
...(nodeId ? { node_id: nodeId } : {}),
|
|
352
|
+
...(packageVersion ? { package_version: packageVersion } : {}),
|
|
353
|
+
...(protocolVersion ? { protocol_version: protocolVersion } : {}),
|
|
354
|
+
meta: this.db.raw(`?::jsonb`, [
|
|
355
|
+
JSON.stringify({
|
|
356
|
+
...(sessionTool.cwd ? { cwd: sessionTool.cwd } : {}),
|
|
357
|
+
}),
|
|
358
|
+
]),
|
|
359
|
+
last_seen_at: now,
|
|
360
|
+
};
|
|
361
|
+
if (existing?.live_console_uuid) {
|
|
362
|
+
await this.db
|
|
363
|
+
.withSchema(MCP_SCHEMA)
|
|
364
|
+
.table("gateway_live_consoles")
|
|
365
|
+
.where({ live_console_uuid: existing.live_console_uuid })
|
|
366
|
+
.update(payload);
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
await this.db.withSchema(MCP_SCHEMA).table("gateway_live_consoles").insert({
|
|
370
|
+
live_console_uuid: (0, node_crypto_1.randomUUID)(),
|
|
371
|
+
...payload,
|
|
372
|
+
connected_at: now,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
return {
|
|
376
|
+
client_uuid: clientUuid,
|
|
377
|
+
connection_id: connectionId,
|
|
378
|
+
total: sessionTools.length,
|
|
379
|
+
};
|
|
380
|
+
},
|
|
381
|
+
async listLiveClientsRecord(input = {}) {
|
|
382
|
+
const scopeKey = (0, gatewayScope_1.resolveGatewayScopeKey)(input);
|
|
383
|
+
const ownerUserUuid = await this.resolveOwnerUserUuidFilter?.(input);
|
|
384
|
+
const rows = await this.db
|
|
385
|
+
.withSchema(MCP_SCHEMA)
|
|
386
|
+
.table("gateway_live_consoles as lc")
|
|
387
|
+
.join("gateway_clients as c", "c.client_uuid", "lc.client_uuid")
|
|
388
|
+
.modify((query) => {
|
|
389
|
+
if (scopeKey) {
|
|
390
|
+
query.where("c.scope_key", scopeKey);
|
|
391
|
+
}
|
|
392
|
+
if (ownerUserUuid) {
|
|
393
|
+
query.where("c.owner_user_uuid", ownerUserUuid);
|
|
394
|
+
}
|
|
395
|
+
})
|
|
396
|
+
.select("lc.client_uuid", "lc.local_session_id", "lc.session_label", "lc.tools_hash", "lc.gateway_user_uuid", "lc.client_label", "lc.system_username", "lc.namespace", "lc.node_id", "lc.package_version", "lc.protocol_version", "lc.last_seen_at")
|
|
397
|
+
.orderBy("lc.client_uuid", "asc")
|
|
398
|
+
.orderBy("lc.local_session_id", "asc");
|
|
399
|
+
const grouped = new Map();
|
|
400
|
+
for (const row of rows) {
|
|
401
|
+
const current = grouped.get(String(row.client_uuid)) ?? {
|
|
402
|
+
client_uuid: String(row.client_uuid),
|
|
403
|
+
...(row.gateway_user_uuid
|
|
404
|
+
? { gateway_user_uuid: String(row.gateway_user_uuid) }
|
|
405
|
+
: {}),
|
|
406
|
+
...(row.client_label ? { client_label: String(row.client_label) } : {}),
|
|
407
|
+
...(row.system_username
|
|
408
|
+
? { system_username: String(row.system_username) }
|
|
409
|
+
: {}),
|
|
410
|
+
...(row.namespace ? { namespace: String(row.namespace) } : {}),
|
|
411
|
+
...(row.node_id ? { node_id: String(row.node_id) } : {}),
|
|
412
|
+
...(row.package_version
|
|
413
|
+
? { package_version: String(row.package_version) }
|
|
414
|
+
: {}),
|
|
415
|
+
...(row.protocol_version
|
|
416
|
+
? { protocol_version: String(row.protocol_version) }
|
|
417
|
+
: {}),
|
|
418
|
+
session_tools: [],
|
|
419
|
+
};
|
|
420
|
+
current.session_tools.push({
|
|
421
|
+
local_session_id: String(row.local_session_id),
|
|
422
|
+
...(row.session_label ? { session_label: String(row.session_label) } : {}),
|
|
423
|
+
...(row.tools_hash ? { tools_hash: String(row.tools_hash) } : {}),
|
|
424
|
+
});
|
|
425
|
+
grouped.set(current.client_uuid, current);
|
|
426
|
+
}
|
|
427
|
+
return {
|
|
428
|
+
clients: Array.from(grouped.values()).sort((left, right) => left.client_uuid.localeCompare(right.client_uuid)),
|
|
429
|
+
};
|
|
430
|
+
},
|
|
431
|
+
async listLiveConsolesRecord(input = {}) {
|
|
432
|
+
const scopeKey = (0, gatewayScope_1.resolveGatewayScopeKey)(input);
|
|
433
|
+
const ownerUserUuid = await this.resolveOwnerUserUuidFilter?.(input);
|
|
434
|
+
const filterClientUuid = this.normalizeOptionalText?.(input.client_uuid);
|
|
435
|
+
const rows = await this.db
|
|
436
|
+
.withSchema(MCP_SCHEMA)
|
|
437
|
+
.table("gateway_live_consoles as lc")
|
|
438
|
+
.join("gateway_clients as c", "c.client_uuid", "lc.client_uuid")
|
|
439
|
+
.leftJoin("gateway_users as u", "u.gateway_user_uuid", "c.owner_user_uuid")
|
|
440
|
+
.leftJoin("gateway_project_consoles as pc", function joinProjectConsole() {
|
|
441
|
+
this.on("pc.client_uuid", "=", "lc.client_uuid")
|
|
442
|
+
.andOn("pc.local_session_id", "=", "lc.local_session_id")
|
|
443
|
+
.andOnVal("pc.status", "=", "active");
|
|
444
|
+
})
|
|
445
|
+
.leftJoin("gateway_projects as p", "p.project_uuid", "pc.project_uuid")
|
|
446
|
+
.modify((query) => {
|
|
447
|
+
if (scopeKey) {
|
|
448
|
+
query.where("c.scope_key", scopeKey);
|
|
449
|
+
}
|
|
450
|
+
if (ownerUserUuid) {
|
|
451
|
+
query.where("c.owner_user_uuid", ownerUserUuid);
|
|
452
|
+
}
|
|
453
|
+
if (filterClientUuid) {
|
|
454
|
+
query.where("lc.client_uuid", filterClientUuid);
|
|
455
|
+
}
|
|
456
|
+
})
|
|
457
|
+
.select("lc.client_uuid", "lc.local_session_id", "lc.cwd", "lc.session_label", "lc.client_label", "lc.system_username", "lc.node_id", "lc.package_version", "lc.protocol_version", "c.bot_username", "pc.project_uuid", "p.name as project_name", this.db.raw("nullif(u.telegram_username, '') as telegram_username"), this.db.raw("nullif(u.telegram_display_name, '') as telegram_display_name"))
|
|
458
|
+
.orderBy("lc.client_uuid", "asc")
|
|
459
|
+
.orderBy("lc.local_session_id", "asc");
|
|
460
|
+
const merged = new Map();
|
|
461
|
+
for (const row of rows) {
|
|
462
|
+
const key = `${row.client_uuid}:${row.local_session_id}`;
|
|
463
|
+
const current = merged.get(key);
|
|
464
|
+
const projectUuids = new Set(current?.project_uuids ?? []);
|
|
465
|
+
const projectNames = new Set(current?.project_names ?? []);
|
|
466
|
+
if (row.project_uuid) {
|
|
467
|
+
projectUuids.add(String(row.project_uuid));
|
|
468
|
+
}
|
|
469
|
+
if (row.project_name) {
|
|
470
|
+
projectNames.add(String(row.project_name));
|
|
471
|
+
}
|
|
472
|
+
merged.set(key, {
|
|
473
|
+
session_id: key,
|
|
474
|
+
client_uuid: String(row.client_uuid),
|
|
475
|
+
local_session_id: String(row.local_session_id),
|
|
476
|
+
cwd: current?.cwd ?? (row.cwd ? String(row.cwd) : null),
|
|
477
|
+
session_label: current?.session_label ??
|
|
478
|
+
(row.session_label ? String(row.session_label) : null),
|
|
479
|
+
client_label: current?.client_label ??
|
|
480
|
+
(row.client_label ? String(row.client_label) : null),
|
|
481
|
+
system_username: current?.system_username ??
|
|
482
|
+
(row.system_username ? String(row.system_username) : null),
|
|
483
|
+
telegram_username: current?.telegram_username ??
|
|
484
|
+
(row.telegram_username ? String(row.telegram_username) : null),
|
|
485
|
+
telegram_display_name: current?.telegram_display_name ??
|
|
486
|
+
(row.telegram_display_name ? String(row.telegram_display_name) : null),
|
|
487
|
+
bot_username: current?.bot_username ??
|
|
488
|
+
(row.bot_username ? String(row.bot_username) : null),
|
|
489
|
+
node_id: current?.node_id ?? (row.node_id ? String(row.node_id) : null),
|
|
490
|
+
package_version: current?.package_version ??
|
|
491
|
+
(row.package_version ? String(row.package_version) : null),
|
|
492
|
+
protocol_version: current?.protocol_version ??
|
|
493
|
+
(row.protocol_version ? String(row.protocol_version) : null),
|
|
494
|
+
project_uuids: Array.from(projectUuids),
|
|
495
|
+
project_names: Array.from(projectNames),
|
|
496
|
+
connected: true,
|
|
497
|
+
registered: projectUuids.size > 0,
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
return {
|
|
501
|
+
sessions: Array.from(merged.values()).sort((left, right) => left.session_id.localeCompare(right.session_id)),
|
|
502
|
+
};
|
|
503
|
+
},
|
|
504
|
+
async resolveLiveConsoleRecord(input) {
|
|
505
|
+
const sessionId = this.requireText?.(input.sessionId ?? input.session_id, "sessionId");
|
|
506
|
+
if (!sessionId) {
|
|
507
|
+
throw new Error("sessionId is required");
|
|
508
|
+
}
|
|
509
|
+
const ownerUserUuid = await this.resolveOwnerUserUuidFilter?.(input);
|
|
510
|
+
const compositeSeparatorIndex = sessionId.indexOf(":");
|
|
511
|
+
const clientUuid = compositeSeparatorIndex > 0 ? sessionId.slice(0, compositeSeparatorIndex).trim() : "";
|
|
512
|
+
const localSessionId = compositeSeparatorIndex > 0
|
|
513
|
+
? sessionId.slice(compositeSeparatorIndex + 1).trim()
|
|
514
|
+
: sessionId;
|
|
515
|
+
if (clientUuid && localSessionId) {
|
|
516
|
+
const row = await this.db
|
|
517
|
+
.withSchema(MCP_SCHEMA)
|
|
518
|
+
.table("gateway_live_consoles as lc")
|
|
519
|
+
.join("gateway_clients as c", "c.client_uuid", "lc.client_uuid")
|
|
520
|
+
.where("lc.client_uuid", clientUuid)
|
|
521
|
+
.where("lc.local_session_id", localSessionId)
|
|
522
|
+
.modify((query) => {
|
|
523
|
+
if (ownerUserUuid) {
|
|
524
|
+
query.where("c.owner_user_uuid", ownerUserUuid);
|
|
525
|
+
}
|
|
526
|
+
})
|
|
527
|
+
.first("lc.client_uuid", "lc.local_session_id", "lc.session_label");
|
|
528
|
+
return row
|
|
529
|
+
? {
|
|
530
|
+
client_uuid: String(row.client_uuid),
|
|
531
|
+
local_session_id: String(row.local_session_id),
|
|
532
|
+
...(row.session_label ? { session_label: String(row.session_label) } : {}),
|
|
533
|
+
}
|
|
534
|
+
: null;
|
|
535
|
+
}
|
|
536
|
+
const rows = await this.db
|
|
537
|
+
.withSchema(MCP_SCHEMA)
|
|
538
|
+
.table("gateway_live_consoles as lc")
|
|
539
|
+
.join("gateway_clients as c", "c.client_uuid", "lc.client_uuid")
|
|
540
|
+
.where("lc.local_session_id", localSessionId)
|
|
541
|
+
.modify((query) => {
|
|
542
|
+
if (ownerUserUuid) {
|
|
543
|
+
query.where("c.owner_user_uuid", ownerUserUuid);
|
|
544
|
+
}
|
|
545
|
+
})
|
|
546
|
+
.select("lc.client_uuid", "lc.local_session_id", "lc.session_label");
|
|
547
|
+
if (rows.length === 0) {
|
|
548
|
+
return null;
|
|
549
|
+
}
|
|
550
|
+
if (rows.length > 1) {
|
|
551
|
+
throw new Error(`Console session_id '${localSessionId}' is ambiguous across live clients. Use the canonical gateway session_id format client_uuid:local_session_id.`);
|
|
552
|
+
}
|
|
553
|
+
const row = rows[0];
|
|
554
|
+
return {
|
|
555
|
+
client_uuid: String(row.client_uuid),
|
|
556
|
+
local_session_id: String(row.local_session_id),
|
|
557
|
+
...(row.session_label ? { session_label: String(row.session_label) } : {}),
|
|
558
|
+
};
|
|
559
|
+
},
|
|
115
560
|
async createProjectRecord(input) {
|
|
116
561
|
const clientUuid = this.requireText?.(input.client_uuid, "client_uuid");
|
|
117
562
|
const name = this.requireText?.(input.name, "name");
|
|
563
|
+
const localSessionId = this.requireText?.(input.local_session_id, "local_session_id");
|
|
118
564
|
const client = await this.db
|
|
119
565
|
.withSchema(MCP_SCHEMA)
|
|
120
566
|
.table("gateway_clients")
|
|
@@ -130,31 +576,68 @@ const TelegramMcpGatewayService = {
|
|
|
130
576
|
const projectUuid = (0, node_crypto_1.randomUUID)();
|
|
131
577
|
const inviteToken = (0, node_crypto_1.randomUUID)();
|
|
132
578
|
const now = new Date().toISOString();
|
|
133
|
-
await this.db.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
579
|
+
await this.db.transaction(async (trx) => {
|
|
580
|
+
await trx.withSchema(MCP_SCHEMA).table("gateway_projects").insert({
|
|
581
|
+
project_uuid: projectUuid,
|
|
582
|
+
name,
|
|
583
|
+
invite_token: inviteToken,
|
|
584
|
+
created_by_client_uuid: clientUuid,
|
|
585
|
+
...(ownerTelegramUserId
|
|
586
|
+
? { owner_telegram_user_id: ownerTelegramUserId }
|
|
587
|
+
: {}),
|
|
588
|
+
...(ownerTelegramUsername
|
|
589
|
+
? { owner_telegram_username: ownerTelegramUsername }
|
|
590
|
+
: {}),
|
|
591
|
+
...(ownerDisplayName ? { owner_display_name: ownerDisplayName } : {}),
|
|
592
|
+
is_active: true,
|
|
593
|
+
created_at: now,
|
|
594
|
+
updated_at: now,
|
|
595
|
+
});
|
|
596
|
+
await trx.withSchema(MCP_SCHEMA).table("gateway_project_members").insert({
|
|
597
|
+
project_uuid: projectUuid,
|
|
598
|
+
client_uuid: clientUuid,
|
|
599
|
+
role: "owner",
|
|
600
|
+
status: "active",
|
|
601
|
+
...(ownerTelegramUserId ? { telegram_user_id: ownerTelegramUserId } : {}),
|
|
602
|
+
...(ownerTelegramUsername ? { telegram_username: ownerTelegramUsername } : {}),
|
|
603
|
+
...(ownerDisplayName ? { display_name: ownerDisplayName } : {}),
|
|
604
|
+
joined_at: now,
|
|
605
|
+
});
|
|
148
606
|
});
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
607
|
+
let registration;
|
|
608
|
+
try {
|
|
609
|
+
registration = await this.registerProjectConsoleForSession?.({
|
|
610
|
+
client_uuid: clientUuid,
|
|
611
|
+
project_uuid: projectUuid,
|
|
612
|
+
local_session_id: localSessionId,
|
|
613
|
+
...(this.normalizeOptionalText?.(input.label)
|
|
614
|
+
? { label: this.normalizeOptionalText?.(input.label) }
|
|
615
|
+
: {}),
|
|
616
|
+
...(this.normalizeOptionalText?.(input.cwd)
|
|
617
|
+
? { cwd: this.normalizeOptionalText?.(input.cwd) }
|
|
618
|
+
: {}),
|
|
619
|
+
status: "active",
|
|
620
|
+
...(input.meta && typeof input.meta === "object" && !Array.isArray(input.meta)
|
|
621
|
+
? { meta: input.meta }
|
|
622
|
+
: {}),
|
|
623
|
+
});
|
|
624
|
+
if (!registration?.session_uuid) {
|
|
625
|
+
throw new Error(`Project ${projectUuid} was created, but console registration for ${clientUuid}:${localSessionId} failed.`);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
catch (error) {
|
|
629
|
+
await this.db
|
|
630
|
+
.withSchema(MCP_SCHEMA)
|
|
631
|
+
.table("gateway_projects")
|
|
632
|
+
.where({ project_uuid: projectUuid })
|
|
633
|
+
.del();
|
|
634
|
+
throw error;
|
|
635
|
+
}
|
|
636
|
+
this.logger.info("Gateway project created and console bound", {
|
|
637
|
+
projectUuid,
|
|
638
|
+
clientUuid,
|
|
639
|
+
localSessionId,
|
|
640
|
+
sessionUuid: registration.session_uuid,
|
|
158
641
|
});
|
|
159
642
|
return {
|
|
160
643
|
project_uuid: projectUuid,
|
|
@@ -166,6 +649,7 @@ const TelegramMcpGatewayService = {
|
|
|
166
649
|
async joinProjectRecord(input) {
|
|
167
650
|
const clientUuid = this.requireText?.(input.client_uuid, "client_uuid");
|
|
168
651
|
const inviteToken = this.requireText?.(input.invite_token, "invite_token");
|
|
652
|
+
const localSessionId = this.requireText?.(input.local_session_id, "local_session_id");
|
|
169
653
|
const client = await this.db
|
|
170
654
|
.withSchema(MCP_SCHEMA)
|
|
171
655
|
.table("gateway_clients")
|
|
@@ -178,14 +662,21 @@ const TelegramMcpGatewayService = {
|
|
|
178
662
|
const memberTelegramUserId = this.normalizeOptionalText?.(clientMeta.telegram_user_id);
|
|
179
663
|
const memberTelegramUsername = this.normalizeOptionalText?.(clientMeta.telegram_username);
|
|
180
664
|
const memberDisplayName = this.normalizeOptionalText?.(clientMeta.telegram_display_name);
|
|
665
|
+
const memberScopeKey = this.normalizeOptionalText?.(client.scope_key);
|
|
181
666
|
const project = await this.db
|
|
182
667
|
.withSchema(MCP_SCHEMA)
|
|
183
|
-
.table("gateway_projects")
|
|
184
|
-
.
|
|
668
|
+
.table("gateway_projects as p")
|
|
669
|
+
.leftJoin("gateway_clients as owner_client", "owner_client.client_uuid", "p.created_by_client_uuid")
|
|
670
|
+
.where({ "p.invite_token": inviteToken, "p.is_active": true })
|
|
671
|
+
.select("p.*", "owner_client.scope_key as owner_scope_key")
|
|
185
672
|
.first();
|
|
186
673
|
if (!project) {
|
|
187
674
|
throw new Error("Project invite token is invalid or inactive");
|
|
188
675
|
}
|
|
676
|
+
const ownerScopeKey = this.normalizeOptionalText?.(project.owner_scope_key);
|
|
677
|
+
if (memberScopeKey && ownerScopeKey && memberScopeKey !== ownerScopeKey) {
|
|
678
|
+
throw new Error("Project belongs to a different gateway scope.");
|
|
679
|
+
}
|
|
189
680
|
const existing = await this.db
|
|
190
681
|
.withSchema(MCP_SCHEMA)
|
|
191
682
|
.table("gateway_project_members")
|
|
@@ -228,6 +719,42 @@ const TelegramMcpGatewayService = {
|
|
|
228
719
|
joined_at: new Date().toISOString(),
|
|
229
720
|
});
|
|
230
721
|
}
|
|
722
|
+
try {
|
|
723
|
+
const registration = await this.registerProjectConsoleForSession?.({
|
|
724
|
+
client_uuid: clientUuid,
|
|
725
|
+
project_uuid: String(project.project_uuid),
|
|
726
|
+
local_session_id: localSessionId,
|
|
727
|
+
...(this.normalizeOptionalText?.(input.label)
|
|
728
|
+
? { label: this.normalizeOptionalText?.(input.label) }
|
|
729
|
+
: {}),
|
|
730
|
+
...(this.normalizeOptionalText?.(input.cwd)
|
|
731
|
+
? { cwd: this.normalizeOptionalText?.(input.cwd) }
|
|
732
|
+
: {}),
|
|
733
|
+
status: "active",
|
|
734
|
+
...(input.meta && typeof input.meta === "object" && !Array.isArray(input.meta)
|
|
735
|
+
? { meta: input.meta }
|
|
736
|
+
: {}),
|
|
737
|
+
});
|
|
738
|
+
if (!registration?.session_uuid) {
|
|
739
|
+
throw new Error(`Project ${String(project.project_uuid)} membership exists, but console registration for ${clientUuid}:${localSessionId} failed.`);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
catch (error) {
|
|
743
|
+
if (existing.status !== "active") {
|
|
744
|
+
await this.db
|
|
745
|
+
.withSchema(MCP_SCHEMA)
|
|
746
|
+
.table("gateway_project_members")
|
|
747
|
+
.where({
|
|
748
|
+
project_uuid: project.project_uuid,
|
|
749
|
+
client_uuid: clientUuid,
|
|
750
|
+
})
|
|
751
|
+
.update({
|
|
752
|
+
status: existing.status,
|
|
753
|
+
joined_at: existing.joined_at,
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
throw error;
|
|
757
|
+
}
|
|
231
758
|
return {
|
|
232
759
|
project_uuid: project.project_uuid,
|
|
233
760
|
invite_token: project.invite_token,
|
|
@@ -254,6 +781,37 @@ const TelegramMcpGatewayService = {
|
|
|
254
781
|
...(memberDisplayName ? { display_name: memberDisplayName } : {}),
|
|
255
782
|
joined_at: new Date().toISOString(),
|
|
256
783
|
});
|
|
784
|
+
try {
|
|
785
|
+
const registration = await this.registerProjectConsoleForSession?.({
|
|
786
|
+
client_uuid: clientUuid,
|
|
787
|
+
project_uuid: String(project.project_uuid),
|
|
788
|
+
local_session_id: localSessionId,
|
|
789
|
+
...(this.normalizeOptionalText?.(input.label)
|
|
790
|
+
? { label: this.normalizeOptionalText?.(input.label) }
|
|
791
|
+
: {}),
|
|
792
|
+
...(this.normalizeOptionalText?.(input.cwd)
|
|
793
|
+
? { cwd: this.normalizeOptionalText?.(input.cwd) }
|
|
794
|
+
: {}),
|
|
795
|
+
status: "active",
|
|
796
|
+
...(input.meta && typeof input.meta === "object" && !Array.isArray(input.meta)
|
|
797
|
+
? { meta: input.meta }
|
|
798
|
+
: {}),
|
|
799
|
+
});
|
|
800
|
+
if (!registration?.session_uuid) {
|
|
801
|
+
throw new Error(`Project ${String(project.project_uuid)} was joined, but console registration for ${clientUuid}:${localSessionId} failed.`);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
catch (error) {
|
|
805
|
+
await this.db
|
|
806
|
+
.withSchema(MCP_SCHEMA)
|
|
807
|
+
.table("gateway_project_members")
|
|
808
|
+
.where({
|
|
809
|
+
project_uuid: project.project_uuid,
|
|
810
|
+
client_uuid: clientUuid,
|
|
811
|
+
})
|
|
812
|
+
.del();
|
|
813
|
+
throw error;
|
|
814
|
+
}
|
|
257
815
|
const notifyRows = await this.db
|
|
258
816
|
.withSchema(MCP_SCHEMA)
|
|
259
817
|
.table("gateway_project_members")
|
|
@@ -302,38 +860,30 @@ const TelegramMcpGatewayService = {
|
|
|
302
860
|
.withSchema(MCP_SCHEMA)
|
|
303
861
|
.table("gateway_sessions")
|
|
304
862
|
.where({
|
|
305
|
-
project_uuid: projectUuid,
|
|
306
863
|
client_uuid: clientUuid,
|
|
307
864
|
local_session_id: localSessionId,
|
|
308
865
|
})
|
|
309
866
|
.first();
|
|
310
|
-
const
|
|
867
|
+
const existingProjectConsole = await this.db
|
|
868
|
+
.withSchema(MCP_SCHEMA)
|
|
869
|
+
.table("gateway_project_consoles")
|
|
870
|
+
.where({
|
|
311
871
|
project_uuid: projectUuid,
|
|
312
872
|
client_uuid: clientUuid,
|
|
313
873
|
local_session_id: localSessionId,
|
|
874
|
+
})
|
|
875
|
+
.first("project_console_uuid");
|
|
876
|
+
const payload = {
|
|
877
|
+
client_uuid: clientUuid,
|
|
878
|
+
local_session_id: localSessionId,
|
|
314
879
|
...(this.normalizeOptionalText?.(input.label)
|
|
315
880
|
? { label: this.normalizeOptionalText?.(input.label) }
|
|
316
881
|
: {}),
|
|
317
882
|
...(this.normalizeOptionalText?.(input.cwd)
|
|
318
883
|
? { cwd: this.normalizeOptionalText?.(input.cwd) }
|
|
319
884
|
: {}),
|
|
320
|
-
...(this.normalizeOptionalText?.(input.
|
|
321
|
-
? {
|
|
322
|
-
: {}),
|
|
323
|
-
...(this.normalizeOptionalText?.(input.tmux_window_name)
|
|
324
|
-
? { tmux_window_name: this.normalizeOptionalText?.(input.tmux_window_name) }
|
|
325
|
-
: {}),
|
|
326
|
-
...(Number.isInteger(input.tmux_window_index)
|
|
327
|
-
? { tmux_window_index: input.tmux_window_index }
|
|
328
|
-
: {}),
|
|
329
|
-
...(this.normalizeOptionalText?.(input.tmux_pane_id)
|
|
330
|
-
? { tmux_pane_id: this.normalizeOptionalText?.(input.tmux_pane_id) }
|
|
331
|
-
: {}),
|
|
332
|
-
...(Number.isInteger(input.tmux_pane_index)
|
|
333
|
-
? { tmux_pane_index: input.tmux_pane_index }
|
|
334
|
-
: {}),
|
|
335
|
-
...(this.normalizeOptionalText?.(input.tmux_target)
|
|
336
|
-
? { tmux_target: this.normalizeOptionalText?.(input.tmux_target) }
|
|
885
|
+
...(this.normalizeOptionalText?.(input.terminal_target)
|
|
886
|
+
? { terminal_target: this.normalizeOptionalText?.(input.terminal_target) }
|
|
337
887
|
: {}),
|
|
338
888
|
status: this.normalizeOptionalText?.(input.status) || "active",
|
|
339
889
|
meta: this.db.raw(`?::jsonb`, [
|
|
@@ -349,6 +899,36 @@ const TelegramMcpGatewayService = {
|
|
|
349
899
|
.table("gateway_sessions")
|
|
350
900
|
.where({ session_uuid: existing.session_uuid })
|
|
351
901
|
.update(payload);
|
|
902
|
+
if (existingProjectConsole?.project_console_uuid) {
|
|
903
|
+
await this.db
|
|
904
|
+
.withSchema(MCP_SCHEMA)
|
|
905
|
+
.table("gateway_project_consoles")
|
|
906
|
+
.where({ project_console_uuid: existingProjectConsole.project_console_uuid })
|
|
907
|
+
.update({
|
|
908
|
+
gateway_session_uuid: existing.session_uuid,
|
|
909
|
+
status: "active",
|
|
910
|
+
updated_at: now,
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
else {
|
|
914
|
+
await this.db.withSchema(MCP_SCHEMA).table("gateway_project_consoles").insert({
|
|
915
|
+
project_console_uuid: (0, node_crypto_1.randomUUID)(),
|
|
916
|
+
project_uuid: projectUuid,
|
|
917
|
+
client_uuid: clientUuid,
|
|
918
|
+
local_session_id: localSessionId,
|
|
919
|
+
gateway_session_uuid: existing.session_uuid,
|
|
920
|
+
status: "active",
|
|
921
|
+
joined_at: now,
|
|
922
|
+
updated_at: now,
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
this.logger.info("Gateway project console session registered", {
|
|
926
|
+
projectUuid,
|
|
927
|
+
clientUuid,
|
|
928
|
+
localSessionId,
|
|
929
|
+
sessionUuid: existing.session_uuid,
|
|
930
|
+
created: false,
|
|
931
|
+
});
|
|
352
932
|
return {
|
|
353
933
|
session_uuid: existing.session_uuid,
|
|
354
934
|
created: false,
|
|
@@ -361,37 +941,118 @@ const TelegramMcpGatewayService = {
|
|
|
361
941
|
...payload,
|
|
362
942
|
created_at: now,
|
|
363
943
|
});
|
|
944
|
+
if (existingProjectConsole?.project_console_uuid) {
|
|
945
|
+
await this.db
|
|
946
|
+
.withSchema(MCP_SCHEMA)
|
|
947
|
+
.table("gateway_project_consoles")
|
|
948
|
+
.where({ project_console_uuid: existingProjectConsole.project_console_uuid })
|
|
949
|
+
.update({
|
|
950
|
+
gateway_session_uuid: sessionUuid,
|
|
951
|
+
status: "active",
|
|
952
|
+
updated_at: now,
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
else {
|
|
956
|
+
await this.db.withSchema(MCP_SCHEMA).table("gateway_project_consoles").insert({
|
|
957
|
+
project_console_uuid: (0, node_crypto_1.randomUUID)(),
|
|
958
|
+
project_uuid: projectUuid,
|
|
959
|
+
client_uuid: clientUuid,
|
|
960
|
+
local_session_id: localSessionId,
|
|
961
|
+
gateway_session_uuid: sessionUuid,
|
|
962
|
+
status: "active",
|
|
963
|
+
joined_at: now,
|
|
964
|
+
updated_at: now,
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
this.logger.info("Gateway project console session registered", {
|
|
968
|
+
projectUuid,
|
|
969
|
+
clientUuid,
|
|
970
|
+
localSessionId,
|
|
971
|
+
sessionUuid,
|
|
972
|
+
created: true,
|
|
973
|
+
});
|
|
364
974
|
return {
|
|
365
975
|
session_uuid: sessionUuid,
|
|
366
976
|
created: true,
|
|
367
977
|
updated_at: now,
|
|
368
978
|
};
|
|
369
979
|
},
|
|
370
|
-
async
|
|
371
|
-
const
|
|
372
|
-
|
|
373
|
-
|
|
980
|
+
async registerProjectConsoleForSession(input) {
|
|
981
|
+
const result = await this.registerSessionRecord?.(input);
|
|
982
|
+
if (!result?.session_uuid) {
|
|
983
|
+
throw new Error("Console registration returned no session_uuid.");
|
|
984
|
+
}
|
|
985
|
+
return result;
|
|
986
|
+
},
|
|
987
|
+
async listClientsRecord(input = {}) {
|
|
988
|
+
const scopeKey = (0, gatewayScope_1.resolveGatewayScopeKey)(input);
|
|
989
|
+
const ownerUserUuid = await this.resolveOwnerUserUuidFilter?.(input);
|
|
990
|
+
const rows = await this.db
|
|
374
991
|
.withSchema(MCP_SCHEMA)
|
|
375
|
-
.table("
|
|
376
|
-
.
|
|
377
|
-
|
|
378
|
-
|
|
992
|
+
.table("gateway_clients as c")
|
|
993
|
+
.leftJoin("gateway_live_consoles as lc", "c.client_uuid", "lc.client_uuid")
|
|
994
|
+
.modify((query) => {
|
|
995
|
+
if (scopeKey) {
|
|
996
|
+
query.where("c.scope_key", scopeKey);
|
|
997
|
+
}
|
|
998
|
+
if (ownerUserUuid) {
|
|
999
|
+
query.where("c.owner_user_uuid", ownerUserUuid);
|
|
1000
|
+
}
|
|
379
1001
|
})
|
|
380
|
-
.
|
|
1002
|
+
.groupBy("c.client_uuid", "c.client_label", this.db.raw("nullif(c.meta->>'namespace', '')"), this.db.raw("nullif(c.meta->>'node_id', '')"), this.db.raw("nullif(c.meta->>'system_username', '')"), this.db.raw("nullif(c.meta->>'telegram_username', '')"), this.db.raw("nullif(c.meta->>'telegram_display_name', '')"), "c.bot_username", "c.last_seen_at", "c.updated_at")
|
|
1003
|
+
.select("c.client_uuid", "c.client_label", this.db.raw("nullif(c.meta->>'namespace', '') as namespace"), this.db.raw("nullif(c.meta->>'node_id', '') as node_id"), this.db.raw("nullif(c.meta->>'system_username', '') as system_username"), this.db.raw("nullif(c.meta->>'telegram_username', '') as telegram_username"), this.db.raw("nullif(c.meta->>'telegram_display_name', '') as telegram_display_name"), "c.bot_username", "c.last_seen_at", "c.updated_at", this.db.raw("count(lc.live_console_uuid) as session_count"))
|
|
1004
|
+
.orderBy("c.last_seen_at", "desc")
|
|
1005
|
+
.orderBy("c.client_uuid", "asc");
|
|
1006
|
+
this.logger.info("Gateway clients list queried", {
|
|
1007
|
+
count: rows.length,
|
|
1008
|
+
clientUuids: rows
|
|
1009
|
+
.map((row) => String(row.client_uuid ?? ""))
|
|
1010
|
+
.filter(Boolean),
|
|
1011
|
+
...(scopeKey ? { scopeKey } : {}),
|
|
1012
|
+
});
|
|
381
1013
|
return {
|
|
382
|
-
|
|
383
|
-
|
|
1014
|
+
clients: rows.map((row) => ({
|
|
1015
|
+
client_uuid: String(row.client_uuid),
|
|
1016
|
+
client_label: row.client_label ? String(row.client_label) : null,
|
|
1017
|
+
namespace: row.namespace ? String(row.namespace) : null,
|
|
1018
|
+
node_id: row.node_id ? String(row.node_id) : null,
|
|
1019
|
+
system_username: row.system_username ? String(row.system_username) : null,
|
|
1020
|
+
telegram_username: row.telegram_username ? String(row.telegram_username) : null,
|
|
1021
|
+
telegram_display_name: row.telegram_display_name ? String(row.telegram_display_name) : null,
|
|
1022
|
+
bot_username: row.bot_username ? String(row.bot_username) : null,
|
|
1023
|
+
...(row.last_seen_at ? { last_seen_at: String(row.last_seen_at) } : {}),
|
|
1024
|
+
...(row.updated_at ? { updated_at: String(row.updated_at) } : {}),
|
|
1025
|
+
session_count: Number(row.session_count || 0),
|
|
1026
|
+
})),
|
|
384
1027
|
};
|
|
385
1028
|
},
|
|
386
1029
|
async listProjectsRecord(input) {
|
|
387
1030
|
const clientUuid = this.requireText?.(input.client_uuid, "client_uuid");
|
|
1031
|
+
const localSessionId = this.normalizeOptionalText?.(input.local_session_id);
|
|
1032
|
+
const scopeKey = (0, gatewayScope_1.resolveGatewayScopeKey)(input);
|
|
388
1033
|
const rows = await this.db
|
|
389
1034
|
.withSchema(MCP_SCHEMA)
|
|
390
1035
|
.table("gateway_project_members as m")
|
|
391
1036
|
.join("gateway_projects as p", "p.project_uuid", "m.project_uuid")
|
|
1037
|
+
.join("gateway_clients as c", "c.client_uuid", "m.client_uuid")
|
|
392
1038
|
.where("m.client_uuid", clientUuid)
|
|
393
1039
|
.where("m.status", "active")
|
|
394
1040
|
.where("p.is_active", true)
|
|
1041
|
+
.modify((query) => {
|
|
1042
|
+
if (localSessionId) {
|
|
1043
|
+
query.whereExists(function matchBoundConsole() {
|
|
1044
|
+
this.select(this.client.raw("1"))
|
|
1045
|
+
.from("mcp.gateway_project_consoles as pc")
|
|
1046
|
+
.whereRaw("pc.project_uuid = m.project_uuid")
|
|
1047
|
+
.whereRaw("pc.client_uuid = m.client_uuid")
|
|
1048
|
+
.where("pc.status", "active")
|
|
1049
|
+
.where("pc.local_session_id", localSessionId);
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
if (scopeKey) {
|
|
1053
|
+
query.where("c.scope_key", scopeKey);
|
|
1054
|
+
}
|
|
1055
|
+
})
|
|
395
1056
|
.select("p.project_uuid", "p.name", "p.invite_token", "m.role", "m.status", "m.joined_at")
|
|
396
1057
|
.orderBy("p.name", "asc");
|
|
397
1058
|
return {
|
|
@@ -443,6 +1104,19 @@ const TelegramMcpGatewayService = {
|
|
|
443
1104
|
.update({
|
|
444
1105
|
status: "left",
|
|
445
1106
|
});
|
|
1107
|
+
if (updated > 0) {
|
|
1108
|
+
await this.db
|
|
1109
|
+
.withSchema(MCP_SCHEMA)
|
|
1110
|
+
.table("gateway_project_consoles")
|
|
1111
|
+
.where({
|
|
1112
|
+
client_uuid: clientUuid,
|
|
1113
|
+
project_uuid: projectUuid,
|
|
1114
|
+
})
|
|
1115
|
+
.update({
|
|
1116
|
+
status: "inactive",
|
|
1117
|
+
updated_at: new Date().toISOString(),
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
446
1120
|
const notifyRows = updated > 0
|
|
447
1121
|
? await this.db
|
|
448
1122
|
.withSchema(MCP_SCHEMA)
|
|
@@ -456,6 +1130,16 @@ const TelegramMcpGatewayService = {
|
|
|
456
1130
|
})
|
|
457
1131
|
.distinct("client_uuid")
|
|
458
1132
|
: [];
|
|
1133
|
+
if (updated > 0 && notifyRows.length === 0) {
|
|
1134
|
+
await this.db
|
|
1135
|
+
.withSchema(MCP_SCHEMA)
|
|
1136
|
+
.table("gateway_projects")
|
|
1137
|
+
.where({ project_uuid: projectUuid })
|
|
1138
|
+
.update({
|
|
1139
|
+
is_active: false,
|
|
1140
|
+
updated_at: new Date().toISOString(),
|
|
1141
|
+
});
|
|
1142
|
+
}
|
|
459
1143
|
return {
|
|
460
1144
|
project_uuid: projectUuid,
|
|
461
1145
|
left: updated > 0,
|
|
@@ -510,6 +1194,14 @@ const TelegramMcpGatewayService = {
|
|
|
510
1194
|
.table("gateway_projects")
|
|
511
1195
|
.where({ project_uuid: projectUuid })
|
|
512
1196
|
.del();
|
|
1197
|
+
await this.db
|
|
1198
|
+
.withSchema(MCP_SCHEMA)
|
|
1199
|
+
.table("gateway_project_consoles")
|
|
1200
|
+
.where({ project_uuid: projectUuid })
|
|
1201
|
+
.update({
|
|
1202
|
+
status: "inactive",
|
|
1203
|
+
updated_at: new Date().toISOString(),
|
|
1204
|
+
});
|
|
513
1205
|
return {
|
|
514
1206
|
project_uuid: projectUuid,
|
|
515
1207
|
deleted: deleted > 0,
|
|
@@ -540,27 +1232,37 @@ const TelegramMcpGatewayService = {
|
|
|
540
1232
|
}
|
|
541
1233
|
const rows = await this.db
|
|
542
1234
|
.withSchema(MCP_SCHEMA)
|
|
543
|
-
.table("
|
|
544
|
-
.leftJoin("
|
|
1235
|
+
.table("gateway_project_consoles as pc")
|
|
1236
|
+
.leftJoin("gateway_live_consoles as lc", function joinLiveConsole() {
|
|
1237
|
+
this.on("lc.client_uuid", "=", "pc.client_uuid").andOn("lc.local_session_id", "=", "pc.local_session_id");
|
|
1238
|
+
})
|
|
1239
|
+
.leftJoin("gateway_clients as c", "c.client_uuid", "pc.client_uuid")
|
|
1240
|
+
.leftJoin("gateway_users as u", "u.gateway_user_uuid", "c.owner_user_uuid")
|
|
545
1241
|
.leftJoin("gateway_project_members as m", function joinMember() {
|
|
546
|
-
this.on("m.project_uuid", "=", "
|
|
1242
|
+
this.on("m.project_uuid", "=", "pc.project_uuid").andOn("m.client_uuid", "=", "pc.client_uuid");
|
|
547
1243
|
})
|
|
548
|
-
.where("
|
|
549
|
-
.where("
|
|
1244
|
+
.where("pc.project_uuid", projectUuid)
|
|
1245
|
+
.where("pc.status", "active")
|
|
550
1246
|
.where("m.status", "active")
|
|
551
|
-
.
|
|
552
|
-
.
|
|
553
|
-
.orderBy("
|
|
1247
|
+
.orderBy("pc.client_uuid", "asc")
|
|
1248
|
+
.orderBy("pc.joined_at", "asc")
|
|
1249
|
+
.orderBy("pc.updated_at", "desc")
|
|
1250
|
+
.select("pc.gateway_session_uuid as session_uuid", "pc.project_uuid", "pc.client_uuid", "pc.local_session_id", "pc.status", "pc.updated_at", "lc.session_label as label", "c.client_label", this.db.raw("coalesce(nullif(u.telegram_display_name, ''), nullif(m.display_name, ''), nullif(c.meta->>'telegram_display_name', '')) as display_name"), this.db.raw("coalesce(nullif(u.telegram_username, ''), nullif(m.telegram_username, ''), nullif(c.meta->>'telegram_username', '')) as telegram_username"), this.db.raw("nullif(c.meta->>'system_username', '') as system_username"), "c.bot_username", "m.joined_at")
|
|
1251
|
+
.orderByRaw("coalesce(lc.session_label, pc.local_session_id) asc")
|
|
1252
|
+
.orderBy("pc.updated_at", "desc");
|
|
554
1253
|
return {
|
|
555
1254
|
sessions: rows.map((row) => ({
|
|
556
|
-
session_uuid: row.session_uuid
|
|
1255
|
+
session_uuid: row.session_uuid ??
|
|
1256
|
+
`${String(row.client_uuid)}:${String(row.local_session_id)}`,
|
|
557
1257
|
project_uuid: row.project_uuid,
|
|
558
1258
|
client_uuid: row.client_uuid,
|
|
559
1259
|
local_session_id: row.local_session_id,
|
|
560
1260
|
label: row.label ?? null,
|
|
561
1261
|
status: row.status,
|
|
562
1262
|
client_label: row.client_label ?? null,
|
|
1263
|
+
display_name: row.display_name ?? null,
|
|
563
1264
|
telegram_username: row.telegram_username ?? null,
|
|
1265
|
+
system_username: row.system_username ?? null,
|
|
564
1266
|
bot_username: row.bot_username ?? null,
|
|
565
1267
|
...(row.joined_at ? { joined_at: String(row.joined_at) } : {}),
|
|
566
1268
|
...(row.updated_at ? { updated_at: String(row.updated_at) } : {}),
|
|
@@ -582,14 +1284,18 @@ const TelegramMcpGatewayService = {
|
|
|
582
1284
|
: kind === "question" || kind === "request";
|
|
583
1285
|
const targetSession = await this.db
|
|
584
1286
|
.withSchema(MCP_SCHEMA)
|
|
585
|
-
.table("
|
|
586
|
-
.
|
|
587
|
-
.leftJoin("
|
|
588
|
-
.
|
|
589
|
-
"
|
|
590
|
-
|
|
1287
|
+
.table("gateway_project_consoles as pc")
|
|
1288
|
+
.join("gateway_sessions as s", "s.session_uuid", "pc.gateway_session_uuid")
|
|
1289
|
+
.leftJoin("gateway_clients as c", "c.client_uuid", "pc.client_uuid")
|
|
1290
|
+
.leftJoin("gateway_projects as p", "p.project_uuid", "pc.project_uuid")
|
|
1291
|
+
.where("pc.gateway_session_uuid", targetSessionId)
|
|
1292
|
+
.modify((query) => {
|
|
1293
|
+
if (requestedProjectUuid) {
|
|
1294
|
+
query.where("pc.project_uuid", requestedProjectUuid);
|
|
1295
|
+
}
|
|
591
1296
|
})
|
|
592
|
-
.
|
|
1297
|
+
.where("pc.status", "active")
|
|
1298
|
+
.select("s.*", "pc.project_uuid", "p.name as project_name", this.db.raw("coalesce(nullif(c.meta->>'telegram_display_name', ''), nullif(c.meta->>'telegram_username', ''), c.client_label, c.bot_username) as target_actor_label"))
|
|
593
1299
|
.first();
|
|
594
1300
|
if (!targetSession) {
|
|
595
1301
|
throw new Error(`Target project session ${targetSessionId} was not found.`);
|
|
@@ -598,18 +1304,26 @@ const TelegramMcpGatewayService = {
|
|
|
598
1304
|
requestedProjectUuid !== String(targetSession.project_uuid)) {
|
|
599
1305
|
throw new Error("Target session does not belong to the requested project.");
|
|
600
1306
|
}
|
|
601
|
-
const
|
|
1307
|
+
const sourceProjectConsole = await this.db
|
|
602
1308
|
.withSchema(MCP_SCHEMA)
|
|
603
|
-
.table("
|
|
1309
|
+
.table("gateway_project_consoles")
|
|
604
1310
|
.where({
|
|
605
1311
|
project_uuid: String(targetSession.project_uuid),
|
|
606
1312
|
client_uuid: clientUuid,
|
|
607
1313
|
local_session_id: localSessionId,
|
|
608
1314
|
status: "active",
|
|
609
1315
|
})
|
|
1316
|
+
.first("gateway_session_uuid");
|
|
1317
|
+
if (!sourceProjectConsole?.gateway_session_uuid) {
|
|
1318
|
+
throw new Error(`Active project session '${localSessionId}' is not registered for client ${clientUuid} in project ${targetSession.project_uuid}.`);
|
|
1319
|
+
}
|
|
1320
|
+
const sourceSession = await this.db
|
|
1321
|
+
.withSchema(MCP_SCHEMA)
|
|
1322
|
+
.table("gateway_sessions")
|
|
1323
|
+
.where({ session_uuid: String(sourceProjectConsole.gateway_session_uuid) })
|
|
610
1324
|
.first();
|
|
611
1325
|
if (!sourceSession) {
|
|
612
|
-
throw new Error(`
|
|
1326
|
+
throw new Error(`Gateway session anchor for '${localSessionId}' was not found in project ${targetSession.project_uuid}.`);
|
|
613
1327
|
}
|
|
614
1328
|
const shareId = (0, node_crypto_1.randomUUID)();
|
|
615
1329
|
const messageUuid = (0, node_crypto_1.randomUUID)();
|
|
@@ -642,7 +1356,7 @@ const TelegramMcpGatewayService = {
|
|
|
642
1356
|
});
|
|
643
1357
|
await this.db.withSchema(MCP_SCHEMA).table("gateway_messages").insert({
|
|
644
1358
|
message_uuid: messageUuid,
|
|
645
|
-
project_uuid:
|
|
1359
|
+
project_uuid: String(targetSession.project_uuid),
|
|
646
1360
|
from_session_uuid: sourceSession.session_uuid,
|
|
647
1361
|
to_session_uuid: targetSession.session_uuid,
|
|
648
1362
|
kind,
|
|
@@ -720,6 +1434,7 @@ const TelegramMcpGatewayService = {
|
|
|
720
1434
|
...(targetSession.project_name
|
|
721
1435
|
? { project_name: targetSession.project_name }
|
|
722
1436
|
: {}),
|
|
1437
|
+
project_uuid: String(targetSession.project_uuid),
|
|
723
1438
|
...(targetSession.target_actor_label
|
|
724
1439
|
? { target_actor_label: targetSession.target_actor_label }
|
|
725
1440
|
: {}),
|
|
@@ -730,7 +1445,7 @@ const TelegramMcpGatewayService = {
|
|
|
730
1445
|
share_id: shareId,
|
|
731
1446
|
delivery_status: "queued",
|
|
732
1447
|
note_path: `gateway://shares/${shareId}.md`,
|
|
733
|
-
|
|
1448
|
+
xchange_record_id: shareId,
|
|
734
1449
|
copied_artifacts: artifactRefs.map((artifact) => trimOptionalText(artifact.original_name) ||
|
|
735
1450
|
trimOptionalText(artifact.relative_path) ||
|
|
736
1451
|
trimOptionalText(artifact.file_path) ||
|
|
@@ -746,6 +1461,7 @@ const TelegramMcpGatewayService = {
|
|
|
746
1461
|
...(targetSession.project_name
|
|
747
1462
|
? { project_name: targetSession.project_name }
|
|
748
1463
|
: {}),
|
|
1464
|
+
project_uuid: String(targetSession.project_uuid),
|
|
749
1465
|
source_actor_label: sourceSession.label ?? sourceSession.local_session_id,
|
|
750
1466
|
kind,
|
|
751
1467
|
summary,
|
|
@@ -761,7 +1477,6 @@ const TelegramMcpGatewayService = {
|
|
|
761
1477
|
target_session_label: targetSession.label ?? targetSession.local_session_id,
|
|
762
1478
|
created_at: now,
|
|
763
1479
|
note_relative_path: `shares/${shareId}.md`,
|
|
764
|
-
share_index_file_name: "SHARED_INDEX.md",
|
|
765
1480
|
artifacts: queuedArtifacts,
|
|
766
1481
|
},
|
|
767
1482
|
};
|
|
@@ -844,7 +1559,6 @@ const TelegramMcpGatewayService = {
|
|
|
844
1559
|
target_session_label: row.target_session_label ?? row.target_local_session_id,
|
|
845
1560
|
created_at: String(row.created_at),
|
|
846
1561
|
note_relative_path: `shares/${shareId}.md`,
|
|
847
|
-
share_index_file_name: "SHARED_INDEX.md",
|
|
848
1562
|
artifacts: artifactsByMessage.get(row.message_uuid) ?? [],
|
|
849
1563
|
};
|
|
850
1564
|
}),
|
|
@@ -1016,6 +1730,22 @@ const TelegramMcpGatewayService = {
|
|
|
1016
1730
|
},
|
|
1017
1731
|
},
|
|
1018
1732
|
actions: {
|
|
1733
|
+
upsertGatewayUser: {
|
|
1734
|
+
async handler(ctx) {
|
|
1735
|
+
if (!GATEWAY_ENABLED) {
|
|
1736
|
+
throw new Error("Gateway service is disabled in client mode");
|
|
1737
|
+
}
|
|
1738
|
+
return this.upsertGatewayUserRecord?.(ctx.params);
|
|
1739
|
+
},
|
|
1740
|
+
},
|
|
1741
|
+
resolveGatewayUserRoute: {
|
|
1742
|
+
async handler(ctx) {
|
|
1743
|
+
if (!GATEWAY_ENABLED) {
|
|
1744
|
+
throw new Error("Gateway service is disabled in client mode");
|
|
1745
|
+
}
|
|
1746
|
+
return this.resolveGatewayUserRouteRecord?.(ctx.params);
|
|
1747
|
+
},
|
|
1748
|
+
},
|
|
1019
1749
|
registerClient: {
|
|
1020
1750
|
async handler(ctx) {
|
|
1021
1751
|
if (!GATEWAY_ENABLED) {
|
|
@@ -1024,6 +1754,62 @@ const TelegramMcpGatewayService = {
|
|
|
1024
1754
|
return this.registerClientRecord?.(ctx.params);
|
|
1025
1755
|
},
|
|
1026
1756
|
},
|
|
1757
|
+
syncLiveConsoles: {
|
|
1758
|
+
async handler(ctx) {
|
|
1759
|
+
if (!GATEWAY_ENABLED) {
|
|
1760
|
+
throw new Error("Gateway service is disabled in client mode");
|
|
1761
|
+
}
|
|
1762
|
+
return this.syncLiveConsolesRecord?.(ctx.params);
|
|
1763
|
+
},
|
|
1764
|
+
},
|
|
1765
|
+
clearLiveConsoles: {
|
|
1766
|
+
async handler(ctx) {
|
|
1767
|
+
if (!GATEWAY_ENABLED) {
|
|
1768
|
+
throw new Error("Gateway service is disabled in client mode");
|
|
1769
|
+
}
|
|
1770
|
+
return this.clearLiveConsolesRecord?.(ctx.params);
|
|
1771
|
+
},
|
|
1772
|
+
},
|
|
1773
|
+
removeLiveConsoles: {
|
|
1774
|
+
async handler(ctx) {
|
|
1775
|
+
if (!GATEWAY_ENABLED) {
|
|
1776
|
+
throw new Error("Gateway service is disabled in client mode");
|
|
1777
|
+
}
|
|
1778
|
+
return this.removeLiveConsolesRecord?.(ctx.params);
|
|
1779
|
+
},
|
|
1780
|
+
},
|
|
1781
|
+
pruneGatewayState: {
|
|
1782
|
+
async handler() {
|
|
1783
|
+
if (!GATEWAY_ENABLED) {
|
|
1784
|
+
throw new Error("Gateway service is disabled in client mode");
|
|
1785
|
+
}
|
|
1786
|
+
return this.pruneGatewayStateRecord?.();
|
|
1787
|
+
},
|
|
1788
|
+
},
|
|
1789
|
+
listLiveClients: {
|
|
1790
|
+
async handler(ctx) {
|
|
1791
|
+
if (!GATEWAY_ENABLED) {
|
|
1792
|
+
throw new Error("Gateway service is disabled in client mode");
|
|
1793
|
+
}
|
|
1794
|
+
return this.listLiveClientsRecord?.(ctx.params);
|
|
1795
|
+
},
|
|
1796
|
+
},
|
|
1797
|
+
listLiveConsoles: {
|
|
1798
|
+
async handler(ctx) {
|
|
1799
|
+
if (!GATEWAY_ENABLED) {
|
|
1800
|
+
throw new Error("Gateway service is disabled in client mode");
|
|
1801
|
+
}
|
|
1802
|
+
return this.listLiveConsolesRecord?.(ctx.params);
|
|
1803
|
+
},
|
|
1804
|
+
},
|
|
1805
|
+
resolveLiveConsole: {
|
|
1806
|
+
async handler(ctx) {
|
|
1807
|
+
if (!GATEWAY_ENABLED) {
|
|
1808
|
+
throw new Error("Gateway service is disabled in client mode");
|
|
1809
|
+
}
|
|
1810
|
+
return this.resolveLiveConsoleRecord?.(ctx.params);
|
|
1811
|
+
},
|
|
1812
|
+
},
|
|
1027
1813
|
createProject: {
|
|
1028
1814
|
async handler(ctx) {
|
|
1029
1815
|
if (!GATEWAY_ENABLED) {
|
|
@@ -1048,12 +1834,12 @@ const TelegramMcpGatewayService = {
|
|
|
1048
1834
|
return this.registerSessionRecord?.(ctx.params);
|
|
1049
1835
|
},
|
|
1050
1836
|
},
|
|
1051
|
-
|
|
1837
|
+
listClients: {
|
|
1052
1838
|
async handler(ctx) {
|
|
1053
1839
|
if (!GATEWAY_ENABLED) {
|
|
1054
1840
|
throw new Error("Gateway service is disabled in client mode");
|
|
1055
1841
|
}
|
|
1056
|
-
return this.
|
|
1842
|
+
return this.listClientsRecord?.(ctx.params);
|
|
1057
1843
|
},
|
|
1058
1844
|
},
|
|
1059
1845
|
listProjects: {
|
|
@@ -1137,5 +1923,14 @@ const TelegramMcpGatewayService = {
|
|
|
1137
1923
|
},
|
|
1138
1924
|
},
|
|
1139
1925
|
},
|
|
1926
|
+
async started() {
|
|
1927
|
+
if (!GATEWAY_ENABLED) {
|
|
1928
|
+
return;
|
|
1929
|
+
}
|
|
1930
|
+
const cleared = await this.clearLiveConsolesRecord?.();
|
|
1931
|
+
this.logger.info("Gateway live consoles registry cleared on startup", {
|
|
1932
|
+
deleted: cleared?.deleted ?? 0,
|
|
1933
|
+
});
|
|
1934
|
+
},
|
|
1140
1935
|
};
|
|
1141
1936
|
exports.default = TelegramMcpGatewayService;
|