@@ -217,6 +217,15 @@ function buildIMessageCliJsonArgs(args: readonly string[], dbPath?: string): str
217217 return [ ...args , ...( trimmedDbPath ? [ "--db" , trimmedDbPath ] : [ ] ) , "--json" ] ;
218218}
219219
220+ function resolveIMessageCliFailure ( result : Record < string , unknown > ) : string | null {
221+ if ( result . success !== false ) {
222+ return null ;
223+ }
224+ return typeof result . error === "string" && result . error . trim ( )
225+ ? result . error . trim ( )
226+ : "iMessage action failed" ;
227+ }
228+
220229async function runIMessageCliJson (
221230 cliPath : string ,
222231 dbPath : string | undefined ,
@@ -285,6 +294,11 @@ async function runIMessageCliJson(
285294 }
286295 }
287296 if ( code === 0 && parsed ) {
297+ const failure = resolveIMessageCliFailure ( parsed ) ;
298+ if ( failure ) {
299+ reject ( new Error ( failure ) ) ;
300+ return ;
301+ }
288302 resolve ( parsed ) ;
289303 return ;
290304 }
@@ -302,6 +316,13 @@ function stringValue(value: unknown): string | undefined {
302316 return typeof value === "string" && value . trim ( ) ? value . trim ( ) : undefined ;
303317}
304318
319+ function isAttachmentCommandFallbackError ( error : unknown ) : boolean {
320+ const message = error instanceof Error ? error . message : String ( error ) ;
321+ return / (?: u n k n o w n | u n r e c o g n i z e d | i n v a l i d | u n s u p p o r t e d ) \s + (?: c o m m a n d | s u b c o m m a n d ) | n o t a r e c o g n i z e d c o m m a n d | s e n d - a t t a c h m e n t .* (?: n o t f o u n d | u n s u p p o r t e d | u n a v a i l a b l e ) | p r i v a t e a p i b r i d g e .* u n a v a i l a b l e | r e q u i r e s t h e i m s g p r i v a t e a p i b r i d g e | r u n i m s g l a u n c h / iu. test (
322+ message ,
323+ ) ;
324+ }
325+
305326async function resolveAttachmentChatGuid ( params : {
306327 target : ReturnType < typeof parseIMessageTarget > ;
307328 runCliJson : ( args : readonly string [ ] ) => Promise < Record < string , unknown > > ;
@@ -316,6 +337,92 @@ async function resolveAttachmentChatGuid(params: {
316337 return stringValue ( result . guid ) ?? stringValue ( result . chat_guid ) ?? null ;
317338}
318339
340+ async function trySendAttachmentForExplicitChat ( params : {
341+ accountId : string ;
342+ target : ReturnType < typeof parseIMessageTarget > ;
343+ filePath : string ;
344+ echoText ?: string ;
345+ runCliJson : ( args : readonly string [ ] ) => Promise < Record < string , unknown > > ;
346+ } ) : Promise < IMessageSendResult | null > {
347+ let attachmentChatGuid : string | null = null ;
348+ try {
349+ attachmentChatGuid = await resolveAttachmentChatGuid ( {
350+ target : params . target ,
351+ runCliJson : params . runCliJson ,
352+ } ) ;
353+ } catch ( error ) {
354+ if ( isAttachmentCommandFallbackError ( error ) ) {
355+ return null ;
356+ }
357+ throw error ;
358+ }
359+ if ( ! attachmentChatGuid ) {
360+ return null ;
361+ }
362+
363+ let result : Record < string , unknown > ;
364+ try {
365+ result = await params . runCliJson ( [
366+ "send-attachment" ,
367+ "--chat" ,
368+ attachmentChatGuid ,
369+ "--file" ,
370+ params . filePath ,
371+ "--transport" ,
372+ "auto" ,
373+ ] ) ;
374+ } catch ( error ) {
375+ if ( isAttachmentCommandFallbackError ( error ) ) {
376+ return null ;
377+ }
378+ throw error ;
379+ }
380+ const failure = resolveIMessageCliFailure ( result ) ;
381+ if ( failure ) {
382+ const error = new Error ( failure ) ;
383+ if ( isAttachmentCommandFallbackError ( error ) ) {
384+ return null ;
385+ }
386+ throw error ;
387+ }
388+
389+ const resolvedId = resolveMessageId ( result ) ;
390+ const approvalBindingMessageId = resolveOutboundMessageGuid ( result ) ;
391+ const messageId = resolvedId ?? ( result . ok || result . success ? "ok" : "unknown" ) ;
392+ const echoScope = resolveOutboundEchoScope ( {
393+ accountId : params . accountId ,
394+ target : params . target ,
395+ } ) ;
396+ if ( echoScope ) {
397+ rememberPersistedIMessageEcho ( {
398+ scope : echoScope ,
399+ text : params . echoText ,
400+ messageId : resolvedId ?? undefined ,
401+ } ) ;
402+ }
403+ if ( resolvedId ) {
404+ rememberIMessageReplyCache ( {
405+ accountId : params . accountId ,
406+ messageId : resolvedId ,
407+ chatGuid : params . target . kind === "chat_guid" ? params . target . chatGuid : attachmentChatGuid ,
408+ chatId : params . target . kind === "chat_id" ? params . target . chatId : undefined ,
409+ timestamp : Date . now ( ) ,
410+ isFromMe : true ,
411+ } ) ;
412+ }
413+ return {
414+ messageId,
415+ ...( approvalBindingMessageId ? { guid : approvalBindingMessageId } : { } ) ,
416+ sentText : "" ,
417+ ...( params . echoText ? { echoText : params . echoText } : { } ) ,
418+ receipt : createIMessageSendReceipt ( {
419+ messageId,
420+ target : params . target ,
421+ kind : "media" ,
422+ } ) ,
423+ } ;
424+ }
425+
319426export async function sendMessageIMessage (
320427 to : string ,
321428 text : string ,
@@ -389,49 +496,15 @@ export async function sendMessageIMessage(
389496 ( ( args : readonly string [ ] ) => runIMessageCliJson ( cliPath , dbPath , args , opts . timeoutMs ) ) ;
390497
391498 if ( filePath && ! message . trim ( ) && ! resolvedReplyToId ) {
392- const attachmentChatGuid = await resolveAttachmentChatGuid ( { target, runCliJson } ) ;
393- if ( attachmentChatGuid ) {
394- const result = await runCliJson ( [
395- "send-attachment" ,
396- "--chat" ,
397- attachmentChatGuid ,
398- "--file" ,
399- filePath ,
400- "--transport" ,
401- "auto" ,
402- ] ) ;
403- const resolvedId = resolveMessageId ( result ) ;
404- const approvalBindingMessageId = resolveOutboundMessageGuid ( result ) ;
405- const messageId = resolvedId ?? ( result ?. ok || result ?. success ? "ok" : "unknown" ) ;
406- const echoScope = resolveOutboundEchoScope ( { accountId : account . accountId , target } ) ;
407- if ( echoScope ) {
408- rememberPersistedIMessageEcho ( {
409- scope : echoScope ,
410- text : echoText ,
411- messageId : resolvedId ?? undefined ,
412- } ) ;
413- }
414- if ( resolvedId ) {
415- rememberIMessageReplyCache ( {
416- accountId : account . accountId ,
417- messageId : resolvedId ,
418- chatGuid : target . kind === "chat_guid" ? target . chatGuid : attachmentChatGuid ,
419- chatId : target . kind === "chat_id" ? target . chatId : undefined ,
420- timestamp : Date . now ( ) ,
421- isFromMe : true ,
422- } ) ;
423- }
424- return {
425- messageId,
426- ...( approvalBindingMessageId ? { guid : approvalBindingMessageId } : { } ) ,
427- sentText : message ,
428- ...( echoText ? { echoText } : { } ) ,
429- receipt : createIMessageSendReceipt ( {
430- messageId,
431- target,
432- kind : "media" ,
433- } ) ,
434- } ;
499+ const attachmentResult = await trySendAttachmentForExplicitChat ( {
500+ accountId : account . accountId ,
501+ target,
502+ filePath,
503+ echoText,
504+ runCliJson,
505+ } ) ;
506+ if ( attachmentResult ) {
507+ return attachmentResult ;
435508 }
436509 }
437510 const params : Record < string , unknown > = {
0 commit comments