feat: warm-backend thin client — channels-list & group-invite via shared op handlers (#45, B1)#47
Merged
Merged
Conversation
…a shared op handlers (#45) Introduce a shared operation registry so CLI commands and MCP tools execute one handler, optionally served by the warm always-on server. - core/operations.js: OPERATIONS map { listChannels, getGroupInviteLink } — pure functions of { telegramClient, messageSyncService } + args returning the structured data callers render. Single source of truth for both operations. - POST /control/invoke { op, args }: token-authed control endpoint that looks up op in the fixed OPERATIONS allowlist (unknown -> 400), ensures the warm client is logged in, runs the handler against the server's warm services, and returns { result }. mcp-server.js now wires warmServices { telegramClient, messageSyncService } into the control handler. - control-client.invoke(storeDir, { op, args }): thin POST to /control/invoke over the existing controlFetch + token, returning result. - runOperation seam (server-first, local fallback): if a server is already running, invoke it; otherwise run the same OPERATIONS[op] locally inside withCommand with the requested need/lock and the local auth check. Both paths run the identical handler, so output — including --json — is byte-identical. - channels list and group invite-link get converted to the seam; the two MCP tools call the same OPERATIONS functions. Opportunistic only: reads use the server if it is up but never auto-start one.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Variant B, slice B1 of #45 — the vertical proof that CLI commands can run as thin clients over the warm server (which holds a live MTProto connection + open DB), via operation handlers shared with the MCP tools.
What
core/operations.js— sharedOPERATIONSregistry (single source of truth):listChannels,getGroupInviteLink. Each isasync (ctx, args) => result,ctx = { telegramClient, messageSyncService }. The same handler runs whether served warm or locally, so the result shape is identical.POST /control/invoke { op, args }(control server): token-authed like the other/control/*endpoints, fixed allowlist (unknown op → 400), runsensureLoginthenOPERATIONS[op]against the server's warm services.mcp-server.jsnow wires{ telegramClient, messageSyncService }into the control handler.listChannelsandgroupsInviteLinkGetcall the sameOPERATIONSfunctions → one handler serves both the MCP tool and/control/invoke(groupsInviteLinkGetkeeps its presentation wrapper around the shared op's raw result).core/control-client.jsinvoke()→POST /control/invoke(reusescontrolFetch+ token).runOperation(command-context.js): if a server is reachable (pingServer) →invoke; elsewithCommand({ need, lock })+ the sameOPERATIONS[op]. Both paths run the identical op, so output — including--json— is byte-identical whether served warm or by the local fallback. Opportunistic only — no auto-start for reads, and no store-lock contention when served warm.runChannelsList,runGroupInviteLinkGet(arg validation + formatting unchanged; local path keeps the auth check).Scope
Proof slice only — part of #45, not a close. Later B slices migrate more operations + the rest of the MCP tool surface into
OPERATIONS, and decide auto-start/write-op policy.Tests
npm test→ 312 passing (300 + 12 new):/control/invoke(known op runs the registry fn with warm services; unknown → 400; no token → 401);invokeclient; the seam routes to the server when ping succeeds and to localwithCommand+op when it fails, enforces the local auth check, and produces identical results both ways; per-op registry behavior; MCP tools reference the sharedOPERATIONS. Nonode_modulesin the commit; no autobiographical comments.