diff --git a/src/commands/openclaw.ts b/src/commands/openclaw.ts index 868e9e5..9d04c90 100644 --- a/src/commands/openclaw.ts +++ b/src/commands/openclaw.ts @@ -467,15 +467,20 @@ openmail send --to "recipient@example.com" --subject "Report" --body "

See att \`--body\` accepts plain text or HTML — HTML is detected and rendered automatically. -Add \`--attach \` to attach files (repeatable). The response includes -\`messageId\` and \`threadId\` — store \`threadId\` to continue the conversation -later. Subject is ignored when replying in a thread. +Add \`--attach \` to attach files (repeatable). Add \`--cc \` (repeatable) +to copy other recipients. The response includes \`messageId\` and \`threadId\` — store +\`threadId\` to continue the conversation later. \`--subject\` is optional when +\`--thread-id\` is set — the thread subject is used automatically. **Always reply in the existing thread.** When the user asks you to reply to an email, look up the thread with \`openmail inbox\` or \`openmail threads list\` first, then use \`--thread-id\`. Never create a new thread unless the user explicitly asks for one. +**When you were CC'd:** check \`deliveryRole\` and \`headerTo\` on the inbound +message (via \`threads get\`). If \`deliveryRole\` is \`cc\`, reply to \`headerTo\` +— not to \`fromAddr\`. OpenMail auto-CCs \`fromAddr\` on thread replies. + ## Checking for new mail **Always use \`threads list --is-read false\` to check for new mail.** @@ -527,6 +532,10 @@ Each message has: | \`id\` | Message identifier | | \`threadId\` | Conversation thread | | \`fromAddr\` | Sender address | +| \`toAddr\` | Envelope recipient (your inbox address on inbound) | +| \`headerTo\` | Who the sender addressed in the To header (use for CC replies) | +| \`deliveryRole\` | \`to\` or \`cc\` — primary recipient vs CC'd | +| \`cc\` | CC recipients | | \`subject\` | Subject line | | \`bodyText\` | Plain text body (use this) | | \`attachments\` | Array with \`filename\`, \`url\`, \`sizeBytes\` | diff --git a/src/commands/send.ts b/src/commands/send.ts index 6720398..1a39922 100644 --- a/src/commands/send.ts +++ b/src/commands/send.ts @@ -48,11 +48,12 @@ export async function runSendCommand( const noQuote = getBooleanFlag(parsed.flags, "no-quote"); const idempotencyKey = getStringFlag(parsed.flags, "idempotency-key"); const replyTo = getStringFlag(parsed.flags, "reply-to"); + const cc = getRepeatedStringFlag("cc"); const attachPaths = getRepeatedStringFlag("attach"); if (!inboxId) throw new Error("missing inbox id; run `openmail init` or pass --inbox-id"); if (!to) throw new Error("missing --to"); - if (!subject) throw new Error("missing --subject"); + if (!threadId && !subject) throw new Error("missing --subject (optional when --thread-id is set)"); if (!body) throw new Error("missing --body"); let attachments: { path: string; filename: string; contentType: string }[] | undefined; @@ -80,13 +81,14 @@ export async function runSendCommand( return client.sendEmail({ inboxId, to, - subject, + subject: subject ?? "", body, bodyHtml, threadId, includeQuote: noQuote ? false : undefined, idempotencyKey, replyTo, + cc: cc.length > 0 ? cc : undefined, attachments, }); } diff --git a/src/index.ts b/src/index.ts index 12ebfd7..144d6c4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -361,14 +361,16 @@ function printHelp(topic?: string) { "openmail send", "", "Usage:", - " send --to --subject --body [--inbox-id ]", + " send --to --body [--subject ] [--inbox-id ]", " [--thread-id ] [--no-quote] [--idempotency-key ]", - " [--reply-to ] [--attach ]", + " [--reply-to ] [--cc ] [--attach ]", "", "Options:", + " --subject Required for new threads; optional with --thread-id", " --body Plain text or HTML (HTML is auto-detected and rendered)", " --thread-id Reply in a thread; quotes the previous message by default", " --no-quote Send only your reply text (skip auto-quoted history)", + " --cc CC recipient (repeatable)", " --reply-to Address replies should go to. Free plan: must be the", " address of an inbox you own. Developer+: any address.", " --attach Attach a file (repeatable for multiple files)", diff --git a/src/lib/http.ts b/src/lib/http.ts index 8026a21..651c8fa 100644 --- a/src/lib/http.ts +++ b/src/lib/http.ts @@ -65,6 +65,7 @@ export class OpenMailHttpClient { includeQuote?: boolean; idempotencyKey?: string; replyTo?: string; + cc?: string[]; attachments?: { path: string; filename: string; contentType: string }[]; }) { const idempotencyKey = params.idempotencyKey ?? crypto.randomUUID(); @@ -82,6 +83,9 @@ export class OpenMailHttpClient { formData.append("includeQuote", "false"); } if (params.replyTo) formData.append("replyTo", params.replyTo); + if (params.cc?.length) { + for (const addr of params.cc) formData.append("cc", addr); + } for (const att of params.attachments) { const data = await readFile(att.path); @@ -96,7 +100,7 @@ export class OpenMailHttpClient { }); } - const payload: Record = { + const payload: Record = { to: params.to, subject: params.subject, body: params.body, @@ -105,6 +109,7 @@ export class OpenMailHttpClient { if (params.threadId) payload.threadId = params.threadId; if (params.includeQuote === false) payload.includeQuote = false; if (params.replyTo) payload.replyTo = params.replyTo; + if (params.cc?.length) payload.cc = params.cc; return this.post(url, payload, { "Idempotency-Key": idempotencyKey,