feat(@messenger): ✨ add status constants for send-queue tools
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
0e0259010c
commit
d2df7331e3
4 changed files with 43 additions and 14 deletions
25
imessage-mcp/src/send-queue-status.ts
Normal file
25
imessage-mcp/src/send-queue-status.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* Lifecycle states for a `macsync.send_queue` row.
|
||||
*
|
||||
* Single source of truth for status strings shared across the send-queue
|
||||
* tools (send / cancel / check-status). These MUST match the mac-sync server
|
||||
* contract (`SEND_QUEUE_CONTRACTS.md` in the mac-sync server repo):
|
||||
*
|
||||
* - a freshly enqueued row is `'queued'` — the drain endpoint fetches
|
||||
* `WHERE status = 'queued'`. It is NOT `'pending'`.
|
||||
* - the device reports results back as `'sent'` or `'failed'`.
|
||||
* - `cancel` moves a still-`'queued'` row to `'cancelled'`.
|
||||
*
|
||||
* Referencing these constants instead of bare string literals makes a typo
|
||||
* (`SEND_QUEUE_STATUS.PENDING`) a compile error, and lets the enqueue value
|
||||
* flow through a typed query parameter rather than being inlined into SQL.
|
||||
*/
|
||||
export const SEND_QUEUE_STATUS = {
|
||||
QUEUED: 'queued',
|
||||
SENT: 'sent',
|
||||
FAILED: 'failed',
|
||||
CANCELLED: 'cancelled',
|
||||
} as const;
|
||||
|
||||
export type SendQueueStatus =
|
||||
(typeof SEND_QUEUE_STATUS)[keyof typeof SEND_QUEUE_STATUS];
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
import { query } from '../db';
|
||||
import { SEND_QUEUE_STATUS, type SendQueueStatus } from '../send-queue-status';
|
||||
|
||||
interface CancelMessageParams {
|
||||
pending_message_id: string;
|
||||
}
|
||||
|
||||
// Schema: macsync.send_queue. Only rows still in 'pending' state can be
|
||||
// Schema: macsync.send_queue. Only rows still in 'queued' state can be
|
||||
// cancelled; anything 'sent' or already 'cancelled' is reported back as-is.
|
||||
export async function cancelMessage(params: CancelMessageParams): Promise<string> {
|
||||
const { pending_message_id } = params;
|
||||
|
|
@ -31,22 +32,22 @@ export async function cancelMessage(params: CancelMessageParams): Promise<string
|
|||
}
|
||||
|
||||
const row = lookup.rows[0]!;
|
||||
const status = row.status as string;
|
||||
const status = row.status as SendQueueStatus;
|
||||
|
||||
if (status === 'cancelled') {
|
||||
if (status === SEND_QUEUE_STATUS.CANCELLED) {
|
||||
return `Message ${pending_message_id} is already cancelled.`;
|
||||
}
|
||||
|
||||
if (status !== 'pending') {
|
||||
return `Cannot cancel: message has status "${status}". Only pending messages can be cancelled.`;
|
||||
if (status !== SEND_QUEUE_STATUS.QUEUED) {
|
||||
return `Cannot cancel: message has status "${status}". Only queued messages can be cancelled.`;
|
||||
}
|
||||
|
||||
try {
|
||||
await query(
|
||||
`UPDATE macsync.send_queue
|
||||
SET status = 'cancelled', updated_at = NOW()
|
||||
WHERE id = $1::uuid AND status = 'pending'`,
|
||||
[pending_message_id],
|
||||
SET status = $2, updated_at = NOW()
|
||||
WHERE id = $1::uuid AND status = $3`,
|
||||
[pending_message_id, SEND_QUEUE_STATUS.CANCELLED, SEND_QUEUE_STATUS.QUEUED],
|
||||
);
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { query } from '../db';
|
||||
import { SEND_QUEUE_STATUS, type SendQueueStatus } from '../send-queue-status';
|
||||
|
||||
interface CheckSendStatusParams {
|
||||
pending_message_id: string;
|
||||
}
|
||||
|
||||
// Schema: macsync.send_queue. Status values observed in the wild: 'pending',
|
||||
// Schema: macsync.send_queue. Status values observed in the wild: 'queued',
|
||||
// 'sent', 'cancelled'. `failure_reason` is a text column despite the schema
|
||||
// note saying timestamptz — we render it as a string when present.
|
||||
export async function checkSendStatus(params: CheckSendStatusParams): Promise<string> {
|
||||
|
|
@ -44,7 +45,7 @@ export async function checkSendStatus(params: CheckSendStatusParams): Promise<st
|
|||
}
|
||||
|
||||
const row = result.rows[0]!;
|
||||
const status = row.status as string;
|
||||
const status = row.status as SendQueueStatus;
|
||||
const contactName = row.contact_name as string | null;
|
||||
const toHandle = row.to_handle as string;
|
||||
const recipient = contactName ? `${contactName} (${toHandle})` : toHandle;
|
||||
|
|
@ -75,12 +76,12 @@ export async function checkSendStatus(params: CheckSendStatusParams): Promise<st
|
|||
lines.push(`Failure reason: ${row.failure_reason}`);
|
||||
}
|
||||
|
||||
if (status === 'pending') {
|
||||
if (status === SEND_QUEUE_STATUS.QUEUED) {
|
||||
lines.push('');
|
||||
lines.push(`Use cancel_message with id "${row.id}" to cancel before it sends.`);
|
||||
}
|
||||
|
||||
if (status === 'cancelled') {
|
||||
if (status === SEND_QUEUE_STATUS.CANCELLED) {
|
||||
lines.push('This message was cancelled and will not be sent.');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { query } from '../db';
|
||||
import { encode as encodeSourceMarker } from '../source-marker';
|
||||
import { SEND_QUEUE_STATUS, type SendQueueStatus } from '../send-queue-status';
|
||||
|
||||
interface SendMessageParams {
|
||||
recipient?: string;
|
||||
|
|
@ -127,11 +128,12 @@ export async function sendMessage(params: SendMessageParams): Promise<string> {
|
|||
|
||||
let insertResult;
|
||||
try {
|
||||
const enqueueStatus: SendQueueStatus = SEND_QUEUE_STATUS.QUEUED;
|
||||
insertResult = await query(
|
||||
`INSERT INTO macsync.send_queue (device_id, to_handle, body, status)
|
||||
VALUES ($1::uuid, $2, $3, 'pending')
|
||||
VALUES ($1::uuid, $2, $3, $4)
|
||||
RETURNING id, created_at`,
|
||||
[deviceId, recipient, markedBody],
|
||||
[deviceId, recipient, markedBody, enqueueStatus],
|
||||
);
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue