Route command replies through MeshService.respond() — same channel, threaded quote (v0.18.1)
CI / build + unit (pull_request) Successful in 42s

This commit is contained in:
2026-09-25 17:30:28 +00:00
parent d375f2a943
commit 9d4c9594af
8 changed files with 142 additions and 39 deletions
+16 -8
View File
@@ -42,8 +42,10 @@ export class CommandService {
/**
* Event-driven entry point called by MeshService for every decoded rx text.
* No-op unless on COMMAND_CHANNEL with an exact `#command` match (case-sensitive).
* Replies ride COMMAND_CHANNEL: directed DM back to the sender when present, else broadcast.
* Never throws — a failed command must not crash the app.
* Replies go through MeshService.respond(id, reply): auto-routed to the original message's
* destination and channel as a threaded quote (replyId = id). Only when the trigger has no packet
* id does it fall back to legacy sendText routing on COMMAND_CHANNEL. Never throws — a failed
* command must not crash the app.
*/
async onRx(
channel: number,
@@ -59,12 +61,18 @@ export class CommandService {
try {
const reply = await handler({ text: trimmed, from, id });
if (reply === undefined || reply === null) return; // void = no reply
await this.mesh?.sendText(
from ?? BROADCAST_ADDR,
reply,
undefined,
this.commandChannel,
);
if (id !== undefined && this.mesh) {
// respond() auto-routes the reply: rides the original record's channel with replyId = id (threaded quote)
await this.mesh.respond(id, reply);
} else {
// defensive fallback when the trigger has no packet id: legacy routing on COMMAND_CHANNEL
await this.mesh?.sendText(
from ?? BROADCAST_ADDR,
reply,
undefined,
this.commandChannel,
);
}
} catch (err) {
this.logger.warn(
`[commands] '${trimmed}' failed: ${(err as Error).message}`,