See the best posts from any Bluesky account
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feeds:publish — take handle/password as CLI flags, not env vars

Previously the command read FEED_PUBLISHER_HANDLE and
FEED_PUBLISHER_APP_PASSWORD from the environment, which duplicated
information: FEED_PUBLISHER_DID was already needed by the web process,
and after login the publish command can read the DID straight from the
session.

Now the publish command takes --handle and --password flags. The only
persistent config is FEED_PUBLISHER_DID for the web process, which
keeps credentials out of .env.

https://claude.ai/code/session_01NJ15XWB5biJMu4e3zVHdC7

Claude bf35f0d1 93c13dc9

+20 -13
+20 -13
commands/feeds_publish.ts
··· 4 4 import { FEEDS, publisherDid, serviceDid } from '#services/feed_generator' 5 5 6 6 /** 7 - * Ace command: `node ace feeds:publish` 7 + * Ace command: `node ace feeds:publish --handle <handle> --password <app-password>` 8 8 * 9 9 * Publishes `app.bsky.feed.generator` records on an atproto account's PDS for 10 10 * each configured feed (top-1k, top-10k). This is a one-shot bootstrap step 11 11 * that makes the feeds discoverable in Bluesky clients. 12 12 * 13 - * Required env / flags: 14 - * FEED_PUBLISHER_HANDLE — handle that owns the feed records 15 - * FEED_PUBLISHER_APP_PASSWORD — an app password for that account 16 - * FEED_PUBLISHER_DID — (optional) asserted DID to sanity-check 13 + * Flags: 14 + * --handle handle that will own the feed records (required) 15 + * --password app password for that account (required) 16 + * --pds PDS service URL (default: https://bsky.social) 17 + * --dry-run print what would be published without writing 18 + * 19 + * After publishing, the command logs the account's DID. Set 20 + * FEED_PUBLISHER_DID=<that DID> in the web process's env so 21 + * describeFeedGenerator advertises the feeds. 17 22 * 18 23 * The service DID served at the generator endpoint is derived from APP_URL 19 24 * (did:web:<host>) — the feed record points to that DID, not the publisher. ··· 26 31 static description = 'Publish app.bsky.feed.generator records for top-1k and top-10k' 27 32 static options: CommandOptions = { startApp: true } 28 33 34 + @flags.string({ description: 'Handle of the account that will own the feed records' }) 35 + declare handle: string 36 + 37 + @flags.string({ description: 'App password for the publisher account' }) 38 + declare password: string 39 + 29 40 @flags.string({ description: 'PDS service URL', default: 'https://bsky.social' }) 30 41 declare pds: string 31 42 ··· 33 44 declare dryRun: boolean 34 45 35 46 async run() { 36 - const handle = process.env.FEED_PUBLISHER_HANDLE 37 - const password = process.env.FEED_PUBLISHER_APP_PASSWORD 38 - if (!handle || !password) { 39 - this.logger.error( 40 - 'FEED_PUBLISHER_HANDLE and FEED_PUBLISHER_APP_PASSWORD must be set in the environment' 41 - ) 47 + if (!this.handle || !this.password) { 48 + this.logger.error('--handle and --password are required') 42 49 this.exitCode = 1 43 50 return 44 51 } ··· 49 56 const agent = new AtpAgent({ service: this.pds }) 50 57 51 58 try { 52 - await agent.login({ identifier: handle, password }) 59 + await agent.login({ identifier: this.handle, password: this.password }) 53 60 } catch (err) { 54 61 this.logger.error(`Login failed: ${err instanceof Error ? err.message : String(err)}`) 55 62 this.exitCode = 1 ··· 63 70 return 64 71 } 65 72 66 - this.logger.info(`Authenticated as ${handle} (${accountDid})`) 73 + this.logger.info(`Authenticated as ${this.handle} (${accountDid})`) 67 74 68 75 const expected = publisherDid() 69 76 if (expected && expected !== accountDid) {