Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kody/bluesky

README.md

150 lines · 5.9 KB · Markdown

@kody/bluesky

Official Bluesky butterfly from Bluesky brand assets (bluesky_media_kit_logo_transparent_1.svg). Paths are unmodified. Bluesky® and the butterfly mark are trademarks of Bluesky Social PBC.

Intent

Give agents fork-ready Bluesky and AT Protocol helpers for profiles, posts, search, notifications, and guarded social writes. Posts should behave like posts created in the Bluesky UI, including clickable links and external cards with uploaded thumbnails.

This listing is meant to be forked. The live @kody/bluesky package does not share a handle. After you fork, store your handle in packageStorage and save your app password.

When To Use

  • Verify Bluesky auth and read a profile, feed, thread, or notifications
  • Resolve handles and search posts
  • Call uncommon AT Protocol XRPC methods through one helper
  • Draft or publish posts, likes, reposts, follows only after confirm: true
  • Publish links with UTF-8 byte-indexed facets and optional external cards

Required setup

Auth is a Bluesky handle plus an app password. There is no Kody Bluesky OAuth integration.

1. App password secret
  1. Create an app password at https://bsky.app/settings/app-passwords. Do not use your account password. Do not paste the value into chat.
  2. Save it as blueskyAppPassword and approve the hosts below:

https://kody.codes/account/secrets/new?name=blueskyAppPassword&description=Bluesky%20app%20password%20for%20AT%20Protocol%20session%20creation&allowedHosts=bsky.social,*.bsky.network&scope=user

HostWhen to approve
bsky.socialSession create and authenticated XRPC (default auth service)
*.bsky.networkAccount PDS hosts after session (for example *.host.bsky.network)

Unauthenticated AppView reads (get-profile, get-author-feed, get-post-thread, and other GET request calls) default to https://public.api.bsky.app and do not need the secret. Session creation and authenticated calls use https://bsky.social. Pass service to override either.

2. Handle in package storage

After you fork (or when invoking your own copy), persist the default actor:

import { packageStorage } from 'kody:runtime'

await packageStorage().set('blueskyHandle', 'yourhandle.bsky.social')

Or pass identifier / actor / handle on each call. Do not treat the live @kody/bluesky storage bucket as yours.

Smoke test

./smoke-test is safe before anyone saves credentials: it returns { ok: true, live: false } and the prefilled secret URL. After blueskyAppPassword and a handle are present, invoke the package so the secret placeholder resolves:

import { packages } from 'kody:runtime'

export default async function main() {
	return await packages.invoke({
		kodyId: 'bluesky',
		exportName: './smoke-test',
	})
}

A live result reads the session profile and does not post.

dryRun

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.

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.',
		},
		dryRun: true,
	})
}

create-post automatically adds clickable facets for HTTP(S) URLs unless callers provide facets or set autoLinkFacets: false. Pass externalCard with uri, title, description, and an optional thumbnailUrl to render a link preview. The helper fetches thumbnails and, when a Cloudinary image is over Bluesky's 1 MB blob limit, requests a fitted JPEG derivative before upload. Dry-runs report originalBytes, uploadBytes, mimeType, and fitted for the thumbnail instead of uploading a blob.

Exports

ExportDescription
.Package overview, auth hosts, and export map
./smoke-testLocal setup check plus optional live profile read
./requestGeneric XRPC helper; mutating calls need dryRun / confirm
./resolve-handleResolve a handle to a DID
./get-profileRead an actor profile (public AppView by default)
./search-postsSearch posts (authenticated)
./get-author-feedRead an author feed
./get-post-threadRead a post thread
./list-notificationsList authenticated notifications
./create-postPreview or create a post
./delete-postPreview or delete a post
./like-postPreview or like a post
./repostPreview or repost
./followPreview or follow an actor
./unfollowPreview or unfollow
./default-handleRead the stored handle
./migrate-from-valuesCopy a leftover user value into package storage

Examples

Public profile read (no secret):

import getProfile from 'kody:@kody/bluesky/get-profile'

export default async function main() {
	return await getProfile({ actor: 'bsky.app' })
}

Preview a post, then publish only after confirmation:

import createPost from 'kody:@kody/bluesky/create-post'

export default async function main() {
	const preview = await createPost({
		text: 'Hello from Kody',
		dryRun: true,
	})
	// After the user confirms the exact text:
	return await createPost({
		text: 'Hello from Kody',
		confirm: true,
	})
}

Notes

  • This package is not affiliated with or endorsed by Bluesky Social PBC.
  • Fork first. Platform @kody/bluesky storage is shared by the official listing, not a personal account.
  • migrate-from-values exists only for leftover value_get('blueskyHandle') rows. New setups should write packageStorage directly.

Docs