Skip to content

feat: warm-backend thin client — channels-list & group-invite via shared op handlers (#45, B1)#47

Merged
kfastov merged 1 commit into
mainfrom
feat/warm-backend-invoke-b1
Jun 4, 2026
Merged

feat: warm-backend thin client — channels-list & group-invite via shared op handlers (#45, B1)#47
kfastov merged 1 commit into
mainfrom
feat/warm-backend-invoke-b1

Conversation

@kfastov
Copy link
Copy Markdown
Owner

@kfastov kfastov commented Jun 3, 2026

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 — shared OPERATIONS registry (single source of truth): listChannels, getGroupInviteLink. Each is async (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), runs ensureLogin then OPERATIONS[op] against the server's warm services. mcp-server.js now wires { telegramClient, messageSyncService } into the control handler.
  • MCP tools rewired: listChannels and groupsInviteLinkGet call the same OPERATIONS functions → one handler serves both the MCP tool and /control/invoke (groupsInviteLinkGet keeps its presentation wrapper around the shared op's raw result).
  • core/control-client.js invoke()POST /control/invoke (reuses controlFetch + token).
  • Seam runOperation (command-context.js): if a server is reachable (pingServer) → invoke; else withCommand({ need, lock }) + the same OPERATIONS[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.
  • Converted 2 commands: 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 test312 passing (300 + 12 new): /control/invoke (known op runs the registry fn with warm services; unknown → 400; no token → 401); invoke client; the seam routes to the server when ping succeeds and to local withCommand+op when it fails, enforces the local auth check, and produces identical results both ways; per-op registry behavior; MCP tools reference the shared OPERATIONS. No node_modules in the commit; no autobiographical comments.

…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.
@kfastov kfastov merged commit 5348c3f into main Jun 4, 2026
@kfastov kfastov deleted the feat/warm-backend-invoke-b1 branch June 4, 2026 08:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant