opencode: notifier wrapper gains the packet id (id <n>)
This commit is contained in:
@@ -699,7 +699,9 @@ describe('MeshService opencode notifier', () => {
|
||||
let notifyMock: ReturnType<typeof vi.fn>;
|
||||
|
||||
beforeEach(() => {
|
||||
notifyMock = vi.fn(async (_text: string, _from?: number) => {});
|
||||
notifyMock = vi.fn(
|
||||
async (_id: number, _text: string, _from?: number) => {},
|
||||
);
|
||||
});
|
||||
|
||||
// myInfo frame as streamed during the initial sync — sets svc.myNodeNum
|
||||
@@ -737,7 +739,7 @@ describe('MeshService opencode notifier', () => {
|
||||
|
||||
await waitFor(() => notifyMock.mock.calls.length === 1);
|
||||
expect(notifyMock).toHaveBeenCalledTimes(1);
|
||||
expect(notifyMock).toHaveBeenCalledWith('ping', KAMIL_NUM);
|
||||
expect(notifyMock).toHaveBeenCalledWith(1, 'ping', KAMIL_NUM); // packet id threaded through
|
||||
} finally {
|
||||
delete process.env.MESH_NOTIFY_CHANNEL;
|
||||
svc.onModuleDestroy();
|
||||
@@ -773,10 +775,10 @@ describe('MeshService opencode notifier', () => {
|
||||
process.env.MESH_NOTIFY_CHANNEL = '0';
|
||||
const svc = makeSvc();
|
||||
try {
|
||||
svc.injectMessage('hi', 42);
|
||||
const r = svc.injectMessage('hi', 42);
|
||||
|
||||
expect(notifyMock).toHaveBeenCalledTimes(1);
|
||||
expect(notifyMock).toHaveBeenCalledWith('hi', 42);
|
||||
expect(notifyMock).toHaveBeenCalledWith(r.id, 'hi', 42); // generated record id threaded through
|
||||
} finally {
|
||||
delete process.env.MESH_NOTIFY_CHANNEL;
|
||||
}
|
||||
@@ -786,10 +788,10 @@ describe('MeshService opencode notifier', () => {
|
||||
process.env.MESH_NOTIFY_CHANNEL = '0';
|
||||
const svc = makeSvc();
|
||||
try {
|
||||
svc.injectMessage('hi');
|
||||
const r = svc.injectMessage('hi');
|
||||
|
||||
expect(notifyMock).toHaveBeenCalledTimes(1);
|
||||
expect(notifyMock).toHaveBeenCalledWith('hi', undefined);
|
||||
expect(notifyMock).toHaveBeenCalledWith(r.id, 'hi', undefined);
|
||||
} finally {
|
||||
delete process.env.MESH_NOTIFY_CHANNEL;
|
||||
}
|
||||
|
||||
@@ -433,19 +433,24 @@ export class MeshService implements OnModuleInit, OnModuleDestroy {
|
||||
};
|
||||
this.pushHistory(rec);
|
||||
// injected records ride channel 0 — the notifier fires only when MESH_NOTIFY_CHANNEL is set to match
|
||||
this.notifyRx(0, text, from);
|
||||
this.notifyRx(0, text, from, rec.id);
|
||||
return { id: rec.id };
|
||||
}
|
||||
|
||||
/** Fire-and-forget opencode notifier push for an rx text on `channel` (no-op unless channels match and a notifier is wired). */
|
||||
private notifyRx(channel: number, text: string, from?: number): void {
|
||||
/** Fire-and-forget opencode notifier push for an rx text on `channel` (no-op unless channels match and a notifier is wired). Both call sites pass the packet id. */
|
||||
private notifyRx(
|
||||
channel: number,
|
||||
text: string,
|
||||
from?: number,
|
||||
id?: number,
|
||||
): void {
|
||||
if (!this.opencode || channel !== this.notifyChannel) return;
|
||||
// self-echo filter: our own node must never trigger a notification of itself
|
||||
if (from !== undefined && from === this.myNodeNum) {
|
||||
this.logger.debug(`notify skipped: rx from our own node ${from}`);
|
||||
return;
|
||||
}
|
||||
void this.opencode.notify(text, from);
|
||||
void this.opencode.notify(id!, text, from); // both call sites always pass the packet id
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -631,7 +636,7 @@ export class MeshService implements OnModuleInit, OnModuleDestroy {
|
||||
channel: pkt.channel ?? 0,
|
||||
at: Date.now(),
|
||||
});
|
||||
this.notifyRx(pkt.channel ?? 0, text, pkt.from);
|
||||
this.notifyRx(pkt.channel ?? 0, text, pkt.from, pkt.id);
|
||||
} else if (decoded && decoded.portnum === PORT_POSITION_APP) {
|
||||
// live position update: the Position is serialized in Data.payload
|
||||
let pos: Proto.Mesh.Position | undefined;
|
||||
|
||||
@@ -68,7 +68,7 @@ describe('OpencodeService.notify', () => {
|
||||
.mockResolvedValueOnce(json([WORKSPACE]))
|
||||
.mockResolvedValueOnce(json({}));
|
||||
|
||||
await svc.notify('hi', 42);
|
||||
await svc.notify(7, 'hi', 42);
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledTimes(2);
|
||||
const [url, init] = fetchMock.mock.calls[1];
|
||||
@@ -89,7 +89,7 @@ describe('OpencodeService.notify', () => {
|
||||
parts: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'New mesh message from node 42: "hi" — please react to it.',
|
||||
text: 'New mesh message (id 7) from node 42: "hi" — please react to it.',
|
||||
},
|
||||
],
|
||||
}),
|
||||
@@ -106,7 +106,7 @@ describe('OpencodeService.notify', () => {
|
||||
.mockResolvedValueOnce(json([WORKSPACE]))
|
||||
.mockResolvedValueOnce(json({}));
|
||||
|
||||
await svc.notify('hi');
|
||||
await svc.notify(7, 'hi');
|
||||
|
||||
const [, init] = fetchMock.mock.calls[1];
|
||||
expect(String((init as { body?: string }).body)).toBe(
|
||||
@@ -114,7 +114,7 @@ describe('OpencodeService.notify', () => {
|
||||
parts: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'New mesh message: "hi" — please react to it.',
|
||||
text: 'New mesh message (id 7): "hi" — please react to it.',
|
||||
},
|
||||
],
|
||||
}),
|
||||
@@ -146,7 +146,7 @@ describe('OpencodeService.notify', () => {
|
||||
);
|
||||
fetchMock.mockResolvedValueOnce(json({}));
|
||||
|
||||
await svc.notify('hi');
|
||||
await svc.notify(7, 'hi');
|
||||
|
||||
expect(fetchMock.mock.calls[1][0]).toBe(
|
||||
`${API}/proxy/abc/session/ses_pinned/message`,
|
||||
@@ -179,7 +179,7 @@ describe('OpencodeService.notify', () => {
|
||||
);
|
||||
fetchMock.mockResolvedValueOnce(json({}));
|
||||
|
||||
await svc.notify('hi');
|
||||
await svc.notify(7, 'hi');
|
||||
|
||||
expect(fetchMock.mock.calls[1][0]).toBe(
|
||||
`${API}/proxy/b/session/ses_pinned/message`,
|
||||
@@ -198,7 +198,7 @@ describe('OpencodeService.notify', () => {
|
||||
)
|
||||
.mockResolvedValueOnce(json({}));
|
||||
|
||||
await svc.notify('hi');
|
||||
await svc.notify(7, 'hi');
|
||||
|
||||
expect(fetchMock.mock.calls[1][0]).toBe(`${API}/proxy/abc/session`);
|
||||
expect(fetchMock.mock.calls[2][0]).toBe(
|
||||
@@ -213,7 +213,7 @@ describe('OpencodeService.notify', () => {
|
||||
.mockResolvedValueOnce(json([{ id: 'ses_a' }, { id: 'ses_b', time: {} }]))
|
||||
.mockResolvedValueOnce(json({}));
|
||||
|
||||
await svc.notify('hi');
|
||||
await svc.notify(7, 'hi');
|
||||
|
||||
expect(fetchMock.mock.calls[2][0]).toBe(
|
||||
`${API}/proxy/abc/session/ses_a/message`,
|
||||
@@ -230,7 +230,7 @@ describe('OpencodeService.notify', () => {
|
||||
.mockResolvedValueOnce(json([WORKSPACE]))
|
||||
.mockResolvedValueOnce(json({}));
|
||||
|
||||
await svc.notify('hi');
|
||||
await svc.notify(7, 'hi');
|
||||
|
||||
// workspaces GET + message POST only — no /session discovery GET
|
||||
expect(fetchMock).toHaveBeenCalledTimes(2);
|
||||
@@ -309,7 +309,7 @@ describe('OpencodeService.notify', () => {
|
||||
string
|
||||
>[]) {
|
||||
const svc = makeService(env);
|
||||
await expect(svc.notify('hi', 42)).resolves.toBeUndefined();
|
||||
await expect(svc.notify(7, 'hi', 42)).resolves.toBeUndefined();
|
||||
expect(fetchMock).not.toHaveBeenCalled();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -49,10 +49,10 @@ export class OpencodeService {
|
||||
* up to RETRY_DELAYS_MS.length + 1 attempts total; the last failure logs `opencode notify dropped
|
||||
* after N attempts`. No-op when disabled (no credentials).
|
||||
*/
|
||||
async notify(text: string, from?: number): Promise<void> {
|
||||
async notify(id: number, text: string, from?: number): Promise<void> {
|
||||
if (!this.enabled) return;
|
||||
const msg =
|
||||
'New mesh message' +
|
||||
`New mesh message (id ${id})` +
|
||||
(from != null ? ` from node ${from}` : '') +
|
||||
`: "${text}" — please react to it.`;
|
||||
const maxAttempts = RETRY_DELAYS_MS.length + 1;
|
||||
|
||||
Reference in New Issue
Block a user