feat(sdk): add multipart image send support#46
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📜 Recent review details⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
📝 WalkthroughWalkthroughAdds multipart message support: new types, a MessageModule ChangesMultipart Message Sending
Sequence DiagramsequenceDiagram
participant User as User/Script
participant SDK as SDK (MessageModule)
participant FS as File System
participant AttachAPI as Attachment API
participant MessageAPI as Message API
User->>SDK: sendMultipartMessage(chatGuid, parts[])
loop each part
alt file part
SDK->>FS: readFile(filePath)
FS-->>SDK: file bytes
SDK->>AttachAPI: POST /api/v1/attachment/upload (FormData)
AttachAPI-->>SDK: { attachment: path }
else text part
SDK-->SDK: prepare text part
end
end
SDK->>MessageAPI: POST /api/v1/message/multipart (assembled parts)
MessageAPI-->>SDK: MessageResponse
SDK->>MessageAPI: fetchMessage(with attachments)
MessageAPI-->>SDK: Message with attachments
SDK-->>User: return MessageResponse
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Review rate limit: 3/5 reviews remaining, refill in 20 minutes and 17 seconds. Comment |
There was a problem hiding this comment.
Pull request overview
Adds SDK support for sending multiple images (and other parts) in a single multipart iMessage request, including new SDK API surface, request types, documentation, and an example script.
Changes:
- Adds
messages.sendMultipartMessage()with local file reading + upload +/api/v1/message/multipartsend flow. - Introduces multipart message option/part types in the SDK type layer.
- Updates README with remote server file-path clarification and multipart usage example.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| types/message.ts | Adds new multipart send option/part types for SDK consumers. |
| modules/message.ts | Implements multipart upload + send logic in MessageModule. |
| README.md | Documents multipart sending and clarifies local-vs-remote file upload behavior. |
| examples/message-multipart-images.ts | Adds a runnable example for sending multiple images in one multipart request. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
modules/message.ts (1)
69-91: 💤 Low valueUploads may be wasted if any single upload fails.
Promise.allwill reject immediately when any upload fails, but other uploads continue running in the background. For multipart messages with many large files, consider:
- Using
Promise.allSettledwith error aggregation, or- Sequential uploads with early exit
For typical use cases (a few images), the current approach is acceptable.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@modules/message.ts` around lines 69 - 91, The current uploadParts function uses Promise.all which causes other uploads to keep running in the background if one fails; replace Promise.all with Promise.allSettled and aggregate results from options.parts to detect any rejected uploads (using the existing uploadMultipartAttachment and resolvedPartIndex logic), then: 1) collect all settled results, build the array of successful part objects (text or attachment/name) from fulfilled entries, and 2) if any promise was rejected, throw an aggregated Error containing all failure reasons (or return an error structure) so callers can abort/cleanup; alternatively implement a sequential loop over options.parts that calls uploadMultipartAttachment and exits early on the first failure using the same partIndex/name resolution.examples/message-multipart-images.ts (1)
1-1: 💤 Low valueConsider using Bun APIs for file existence checks.
Per coding guidelines, Bun APIs are preferred. For async existence checking:
const exists = await Bun.file(filePath).exists();However, since this is example code demonstrating the SDK (not the SDK itself), and
existsSyncis simple and clear, this is acceptable.As per coding guidelines: "Prefer
Bun.fileovernode:fs's readFile/writeFile for file operations".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/message-multipart-images.ts` at line 1, The example currently imports node:fs; replace synchronous Node APIs with Bun file APIs per guidelines: remove or stop using import fs from "node:fs" and instead use Bun.file(filePath).exists() for existence checks (await Bun.file(filePath).exists()) and use Bun.file(filePath).text() or .arrayBuffer() for reading where the example used fs.readFileSync/existsSync; update any occurrences of fs.existsSync, fs.readFileSync, or fs.readFile to the corresponding Bun.file(...) methods in examples/message-multipart-images.ts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@examples/message-multipart-images.ts`:
- Around line 11-14: The DEFAULT_IMAGE_PATHS array formatting in
examples/message-multipart-images.ts doesn't match Biome style; run the Biome
formatter (bun biome format --write examples/message-multipart-images.ts) or
apply equivalent formatting so the DEFAULT_IMAGE_PATHS constant
(DEFAULT_IMAGE_PATHS) conforms to the project's formatter rules, then commit the
formatted changes.
In `@modules/message.ts`:
- Around line 82-88: The returned attachment name currently uses
path.basename(uploadedPath) which takes the server-side upload path rather than
the user's original filename; change the return to prefer the original filename
from the incoming multipart part (use part.fileName if present, otherwise derive
basename from the part's original path property such as part.path /
part.originalPath / part.originalname) and only fall back to
path.basename(uploadedPath) as a last resort; update the code around the
uploadMultipartAttachment call and the object returned (the block that
constructs { partIndex, attachment, name }) so the name is derived from the
original part metadata instead of the server upload path.
---
Nitpick comments:
In `@examples/message-multipart-images.ts`:
- Line 1: The example currently imports node:fs; replace synchronous Node APIs
with Bun file APIs per guidelines: remove or stop using import fs from "node:fs"
and instead use Bun.file(filePath).exists() for existence checks (await
Bun.file(filePath).exists()) and use Bun.file(filePath).text() or .arrayBuffer()
for reading where the example used fs.readFileSync/existsSync; update any
occurrences of fs.existsSync, fs.readFileSync, or fs.readFile to the
corresponding Bun.file(...) methods in examples/message-multipart-images.ts.
In `@modules/message.ts`:
- Around line 69-91: The current uploadParts function uses Promise.all which
causes other uploads to keep running in the background if one fails; replace
Promise.all with Promise.allSettled and aggregate results from options.parts to
detect any rejected uploads (using the existing uploadMultipartAttachment and
resolvedPartIndex logic), then: 1) collect all settled results, build the array
of successful part objects (text or attachment/name) from fulfilled entries, and
2) if any promise was rejected, throw an aggregated Error containing all failure
reasons (or return an error structure) so callers can abort/cleanup;
alternatively implement a sequential loop over options.parts that calls
uploadMultipartAttachment and exits early on the first failure using the same
partIndex/name resolution.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 0cfd3fbe-355e-4909-bec9-83d9d97ce709
📒 Files selected for processing (4)
README.mdexamples/message-multipart-images.tsmodules/message.tstypes/message.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Agent
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)
**/*.{ts,tsx,js,jsx}: Usebun <file>instead ofnode <file>orts-node <file>for running TypeScript and JavaScript files
Bun automatically loads .env, so don't use dotenv library
UseBun.serve()with WebSockets, HTTPS, and routes instead ofexpress
Usebun:sqlitefor SQLite database operations instead ofbetter-sqlite3
UseBun.redisfor Redis operations instead ofioredis
UseBun.sqlfor Postgres database operations instead ofpgorpostgres.js
Use built-inWebSocketinstead ofwslibrary
PreferBun.fileovernode:fs's readFile/writeFile for file operations
UseBun.$template tag for shell commands instead of execa library
CSS files can be imported directly in TypeScript/JavaScript/JSX files and will be bundled automatically by Bun
Files:
types/message.tsexamples/message-multipart-images.tsmodules/message.ts
**/*.{html,ts,tsx,css}
📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)
Use
bun build <file.html|file.ts|file.css>instead ofwebpackoresbuildfor bundling
Files:
types/message.tsexamples/message-multipart-images.tsmodules/message.ts
**/*.{html,ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)
Use HTML imports with
Bun.serve()for frontend instead ofvite
Files:
types/message.tsexamples/message-multipart-images.tsmodules/message.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)
Run TypeScript server files with
bun --hot <file.ts>for hot module reloading during development
Files:
types/message.tsexamples/message-multipart-images.tsmodules/message.ts
🪛 GitHub Actions: CI
examples/message-multipart-images.ts
[error] 11-15: Biome formatter check failed: file content is not formatted as expected. Reported expected change for DEFAULT_IMAGE_PATHS array formatting.
🔇 Additional comments (7)
types/message.ts (1)
44-66: LGTM!The type definitions are well-structured. The discriminated union for
SendMultipartMessagePartcorrectly distinguishes text parts (viatextproperty) from file parts (viafilePathproperty), which allows TypeScript to properly narrow types in the implementation.modules/message.ts (3)
20-31: LGTM with minor note.The implementation correctly handles file upload. The type assertion
as stringon line 30 assumes the API response structure - this is consistent with patterns elsewhere in the codebase. Consider adding error context if file read fails, but this matches the existing pattern insendAttachment.
111-121: Inconsistent error handling compared tosendAttachment.Per the relevant code snippet from
modules/attachment.ts,sendAttachmenthandlesisChatNotExistErrorby callingensureChatExists()and retrying. This method throws a custom error instead.This appears intentional based on the PR requirements ("Requires... an existing chatGuid"), but the inconsistency may surprise users. Consider documenting this difference prominently or aligning the behavior in a future iteration.
2-5: 💤 Low valueNo changes needed. This library explicitly targets Node.js >= 18.0.0 (per package.json
"engines"), sonode:fs/promisesis the correct choice. TheBun.fileguideline applies to Bun-native projects, not npm-published libraries requiring cross-runtime compatibility.> Likely an incorrect or invalid review comment.examples/message-multipart-images.ts (1)
16-66: LGTM!The example clearly demonstrates the multipart message API:
- Proper environment variable handling with fallback defaults
- File existence validation before sending
- Clean error handling with
handleError- Fetches and logs the sent message with attachments
README.md (2)
122-130: LGTM!Clear and concise explanation of remote server setup semantics. This helps users understand the file path behavior when SDK and server are on different machines.
624-654: LGTM!Comprehensive documentation for the new multipart feature:
- Clear code example showing the API usage
- Important notes about Private API requirement, chat existence, and cross-machine file handling
- Correctly links to the example file
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@examples/message-multipart-images.ts`:
- Around line 52-58: The script currently always calls process.exit(0) even
after failures; update the error paths so failures return a non-zero exit code:
when catching errors where handleError(error, "Failed to send multipart images")
is called, follow that with process.exit(1) (or propagate a non-zero code)
instead of falling through to process.exit(0); also ensure the top-level failure
handler that currently logs errors uses process.exit(1) after logging, and
consider moving sdk.close() into a finally block so sdk.close() runs before
exiting.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 5351e620-9af6-49bb-b33b-66d390e6f80b
📒 Files selected for processing (2)
examples/message-multipart-images.tsmodules/message.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- modules/message.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)
**/*.{ts,tsx,js,jsx}: Usebun <file>instead ofnode <file>orts-node <file>for running TypeScript and JavaScript files
Bun automatically loads .env, so don't use dotenv library
UseBun.serve()with WebSockets, HTTPS, and routes instead ofexpress
Usebun:sqlitefor SQLite database operations instead ofbetter-sqlite3
UseBun.redisfor Redis operations instead ofioredis
UseBun.sqlfor Postgres database operations instead ofpgorpostgres.js
Use built-inWebSocketinstead ofwslibrary
PreferBun.fileovernode:fs's readFile/writeFile for file operations
UseBun.$template tag for shell commands instead of execa library
CSS files can be imported directly in TypeScript/JavaScript/JSX files and will be bundled automatically by Bun
Files:
examples/message-multipart-images.ts
**/*.{html,ts,tsx,css}
📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)
Use
bun build <file.html|file.ts|file.css>instead ofwebpackoresbuildfor bundling
Files:
examples/message-multipart-images.ts
**/*.{html,ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)
Use HTML imports with
Bun.serve()for frontend instead ofvite
Files:
examples/message-multipart-images.ts
**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)
Run TypeScript server files with
bun --hot <file.ts>for hot module reloading during development
Files:
examples/message-multipart-images.ts
🔇 Additional comments (1)
examples/message-multipart-images.ts (1)
35-43: Multipart part construction looks correct.Using
partIndex,filePath, andfileNameper part cleanly matches the multipart SDK contract and preserves deterministic ordering.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
Add SDK support for sending multiple images in a single multipart iMessage request.
Changes
messages.sendMultipartMessage()to the SDKDetails
sendMultipartMessage()reads local files on the SDK host, uploads them to the server, and then sends them together in a single/api/v1/message/multipartrequest.This flow requires:
chatGuidExample
Validation
tsc --noEmitNeed help on this PR? Tag
@codesmithwith what you need.Summary by CodeRabbit
New Features
Documentation
Examples