# @kody/bluesky — agent notes Human setup and intent live in [`README.md`](./README.md). This file is for agents: imports, smoke/dryRun execute snippets, and edge cases. Auth is the user secret **`blueskyAppPassword`** plus a handle in `packageStorage` (or per-call). Never paste the app password. Do not disable live webhooks or jobs. ## Secrets / storage | Kind | Name / key | Notes | | --- | --- | --- | | Credential slot (raw) | `blueskyAppPassword` | User secret, hosts `bsky.social`, `*.bsky.network`. Read with `packageSecrets.get` because `createSession` needs it in the JSON body; the session `accessJwt` goes in `Authorization`. Raw reads work only in Kody-run code (cloud execute, jobs, webhooks), not `--local`. | | Package storage | `blueskyHandle` | Default actor after fork; override with `identifier` / `actor` / `handle` | Public AppView GETs use `https://public.api.bsky.app` and do not need the secret. Authenticated calls create a session against `https://bsky.social` (override with `service`). ## Import paths | Export | Import | | --- | --- | | overview / safety metadata | `kody:@kody/bluesky` | | smoke-test | `kody:@kody/bluesky/smoke-test` | | request | `kody:@kody/bluesky/request` | | resolve-handle | `kody:@kody/bluesky/resolve-handle` | | get-profile | `kody:@kody/bluesky/get-profile` | | search-posts | `kody:@kody/bluesky/search-posts` | | get-author-feed | `kody:@kody/bluesky/get-author-feed` | | get-post-thread | `kody:@kody/bluesky/get-post-thread` | | list-notifications | `kody:@kody/bluesky/list-notifications` | | create-post | `kody:@kody/bluesky/create-post` | | delete-post | `kody:@kody/bluesky/delete-post` | | like-post | `kody:@kody/bluesky/like-post` | | repost | `kody:@kody/bluesky/repost` | | follow | `kody:@kody/bluesky/follow` | | unfollow | `kody:@kody/bluesky/unfollow` | | default-handle | `kody:@kody/bluesky/default-handle` | Prefer static `kody:@kody/bluesky/...` imports from `execute`. Do not lead with `packages.invoke`. ## Smoke test (read-only) Safe before credentials exist: returns `{ ok: true, live: false }` plus setup URLs. With secret + handle, reads the session profile and does not post. ```js import smokeTest from 'kody:@kody/bluesky/smoke-test' export default async function main() { return await smokeTest() // => { ok: true, live: false, setup: { ... } } or live profile metadata } ``` Public profile read (no secret): ```js import getProfile from 'kody:@kody/bluesky/get-profile' export default async function main() { return await getProfile({ actor: 'bsky.app' }) } ``` ## dryRun / confirm (writes) Mutating helpers never create a session when `dryRun: true` (or when `confirm` is omitted). Live writes require `confirm: true` after the user approves the exact text or target. ```js import createPost from 'kody:@kody/bluesky/create-post' export default async function main() { return await createPost({ text: 'Watch this: https://example.com', externalCard: { uri: 'https://example.com', title: 'Example video', description: 'A useful video.', thumbnailUrl: 'https://example.com/thumbnail.jpg', }, dryRun: true, }) } ``` `create-post` adds clickable facets for HTTP(S) URLs unless callers provide `facets` or set `autoLinkFacets: false`. Pass `externalCard` with `uri`, `title`, `description`, and optional `thumbnailUrl`. Thumbnails over Bluesky's 1 MB blob limit are fitted before upload. Dry-runs report `originalBytes`, `uploadBytes`, `mimeType`, and `fitted` instead of uploading. ## Edge cases / fork notes - Fork first. Platform `@kody/bluesky` storage is shared by the official listing, not a personal account. - Persist the caller's handle with `packageStorage().set('blueskyHandle', …)` on the **forked** package, or pass identifiers per call. - Mutating XRPC via `./request` still needs `dryRun` / `confirm` like dedicated helpers. - Never treat smoke success as permission to post. - Keep `LICENSE` (MIT), `## Intent`, and `community-icon.svg` for community listing.