# @kentcdodds/dropbox — agent notes Human setup and intent live in [`README.md`](./README.md). This file is for agents: imports, positive playbooks, and copy-paste `execute` snippets. Auth is the saved OAuth integration named **`dropbox`** — never paste tokens. Do not disable live webhooks or jobs. **Do not use the Dropbox website UI** — use these MCP package exports. ## Auth / integration | Item | Value | | --- | --- | | Integration name | `dropbox` | | Reconnect | `https://kody.codes/connect/oauth?provider=dropbox` | ## Import paths | Export | Import | | --- | --- | | overview | `kody:@kentcdodds/dropbox` | | smoke-test | `kody:@kentcdodds/dropbox/smoke-test` | | get-current-account | `kody:@kentcdodds/dropbox/get-current-account` | | get-space-usage | `kody:@kentcdodds/dropbox/get-space-usage` | | list-folder | `kody:@kentcdodds/dropbox/list-folder` | | get-metadata | `kody:@kentcdodds/dropbox/get-metadata` | | search | `kody:@kentcdodds/dropbox/search` | | download | `kody:@kentcdodds/dropbox/download` | | upload | `kody:@kentcdodds/dropbox/upload` | | upload-large | `kody:@kentcdodds/dropbox/upload-large` | | upload-from-url | `kody:@kentcdodds/dropbox/upload-from-url` | | delete | `kody:@kentcdodds/dropbox/delete` | | create-shared-link | `kody:@kentcdodds/dropbox/create-shared-link` | | ensure-shared-direct-url | `kody:@kentcdodds/dropbox/ensure-shared-direct-url` | | paths | `kody:@kentcdodds/dropbox/paths` | | request | `kody:@kentcdodds/dropbox/request` | Prefer static `kody:@kentcdodds/dropbox/...` imports from `execute`. Do not lead with `packages.invoke`. ## App-folder path primer 1. Call `listFolder({ path: '', limit: 100 })` first so you see the real root. 2. Kody-writable files live under **`/Apps/Kody/…`** (constant `APP_FOLDER_ROOT`), not `/Kody/…`. 3. Use `underAppFolder('notes/a.txt')` → `/Apps/Kody/notes/a.txt`. 4. A **409** / `path/not_found` on a write often means the path is missing the `/Apps/Kody` prefix — helpers suggest the corrected path. ```js import listFolder from 'kody:@kentcdodds/dropbox/list-folder' import { underAppFolder } from 'kody:@kentcdodds/dropbox/paths' export default async function main() { const root = await listFolder({ path: '', limit: 50 }) return { rootNames: root.entries.map((e) => e.path_display ?? e.name), exampleDest: underAppFolder('notes/hello.txt'), } } ``` ## Upload a file (small vs large) **Hard cutoff:** **120 KiB binary** (`UPLOAD_SINGLE_MAX_BYTES = 122880`). Base64 in MCP `params` bloats faster than raw bytes (~200KB+ MCP payloads hurt). Put `bytesBase64` **only in `params`**, never megabytes inside the `code` string. | Size | Export | Notes | | --- | --- | --- | | Tiny text / < 120 KiB binary | `./upload` | `text` or small `bytesBase64` in **params** | | ≥ 120 KiB binary | `./upload-large` | Session: start → append (~**48 KiB** chunks) → **finish** (closes) | | Either | `./upload` | **Auto-delegates** to `./upload-large` when over threshold | ### Small upload (params) ```js import upload from 'kody:@kentcdodds/dropbox/upload' export default async function main() { return await upload({ path: '/Apps/Kody/notes/hello.txt', text: 'Hello from Kody', mode: 'overwrite', confirm: true, }) } ``` ### Large upload (bytesBase64 in params — never in code) ```js import uploadLarge from 'kody:@kentcdodds/dropbox/upload-large' export default async function main(params) { // Pass { path, bytesBase64, mode, confirm } via execute params. return await uploadLarge({ path: params.path, // e.g. '/Apps/Kody/inbox/report.bin' bytesBase64: params.bytesBase64, mode: params.mode ?? 'overwrite', confirm: true, }) } ``` `mode: 'overwrite'` maps to Dropbox commit `mode: "overwrite"` (string tag). Finish closes the session — do not call append after finish. dryRun plan: ```js import uploadLarge from 'kody:@kentcdodds/dropbox/upload-large' export default async function main(params) { return await uploadLarge({ path: params.path ?? '/Apps/Kody/notes/large.bin', bytesBase64: params.bytesBase64 ?? '', mode: 'overwrite', dryRun: true, }) } ``` ### Computer agents (local file → Dropbox) Read the local file on the computer, base64-encode it, then call `./upload` or `./upload-large` with `bytesBase64` in **params**: ```js // Pseudocode on the computer agent — not inside Kody Workers: // const bytesBase64 = Buffer.from(await fs.readFile(localPath)).toString('base64') import upload from 'kody:@kentcdodds/dropbox/upload' export default async function main(params) { return await upload({ path: params.path, // '/Apps/Kody/...' bytesBase64: params.bytesBase64, mode: 'overwrite', confirm: true, }) } ``` ### MCP-only / phone (no large base64) Chat cannot carry large base64. Options: 1. Compress / use a smaller WebP (or similar) under ~120 KiB binary, then `./upload`. 2. If you have a public HTTP(S) URL Dropbox can fetch, use `./upload-from-url` (`files/save_url`): ```js import uploadFromUrl from 'kody:@kentcdodds/dropbox/upload-from-url' export default async function main(params) { return await uploadFromUrl({ path: params.path, // '/Apps/Kody/inbox/photo.webp' url: params.url, // public https://... confirm: true, }) } ``` ### Do not use - **`files/get_temporary_upload_link`** — dead end for agents; prefer `./upload` / `./upload-large`. - **Dropbox website login / UI** — use these package exports over MCP instead. ## Share → direct download URL (Fable / curl) After upload, call `./ensure-shared-direct-url` for a browser/curl-friendly content URL on `dl.dropboxusercontent.com` with `raw=1`. Reuses an existing link via `list_shared_links` when create returns “already exists”. ```js import ensureSharedDirectUrl from 'kody:@kentcdodds/dropbox/ensure-shared-direct-url' export default async function main(params) { return await ensureSharedDirectUrl({ path: params.path, // '/Apps/Kody/notes/guide.pdf' confirm: true, }) // => { path, url, directUrl, reused, link } } ``` dryRun: ```js import ensureSharedDirectUrl from 'kody:@kentcdodds/dropbox/ensure-shared-direct-url' export default async function main() { return await ensureSharedDirectUrl({ path: '/Apps/Kody/notes/guide.pdf', dryRun: true, }) } ``` ## Smoke test (read-only) ```js import smokeTest from 'kody:@kentcdodds/dropbox/smoke-test' export default async function main() { return await smokeTest() // => { ok: true, hasAccountId, hasDisplayName, accountType } — no PII fields } ``` ## dryRun / confirm (writes) `upload`, `upload-large`, `upload-from-url`, `delete`, `create-shared-link`, `ensure-shared-direct-url`, and unknown/mutating `./request` endpoints require `confirm: true` after explicit user approval. Use `dryRun: true` to preview without calling Dropbox. Documented read-only `./request` endpoints do not need confirmation. ```js import deleteDropboxPath from 'kody:@kentcdodds/dropbox/delete' export default async function main() { return await deleteDropboxPath({ path: '/Apps/Kody/notes/old.txt', dryRun: true, }) } ``` Generic request (known read-only endpoint — no confirm): ```js import request from 'kody:@kentcdodds/dropbox/request' export default async function main() { return await request({ endpoint: 'files/get_metadata', body: { path: '/Apps/Kody/notes/todo.txt' }, }) } ``` ## Edge cases - Paths start with `/`; use `''` for the account root on list/search. - Prefer `/Apps/Kody/...` for writes; list root before inventing paths. - `download` returns `{ metadata, bytesBase64, byteLength, contentType }`. - `upload`: pass exactly one of `text` or `bytesBase64`; `mode` is `add` (default) or `overwrite`. Over 120 KiB binary → auto `./upload-large`. - `upload-large`: always pass `bytesBase64` in **params**; session chunks ~48 KiB; finish closes the session. - `list-folder` / `search`: continue with returned `cursor` while `has_more`. - Prefer dedicated helpers over assembling `./request` when one exists. - Never paste Dropbox OAuth tokens into chat or logs.