A CLI for publishing standard.site documents to ATProto sequoia.pub
standard site lexicon cli publishing
59
fork

Configure Feed

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

at main 94 lines 2.4 kB view raw
1import { 2 NodeOAuthClient, 3 type NodeOAuthClientOptions, 4} from "@atproto/oauth-client-node"; 5import { sessionStore, stateStore } from "./oauth-store"; 6 7const CALLBACK_PORT = 4000; 8const CALLBACK_HOST = "127.0.0.1"; 9const CALLBACK_URL = `http://${CALLBACK_HOST}:${CALLBACK_PORT}/oauth/callback`; 10 11// OAuth scope for Sequoia CLI - includes atproto base scope plus our collections 12const OAUTH_SCOPE = 13 "atproto repo:site.standard.document repo:site.standard.publication repo:app.bsky.feed.post blob:*/*"; 14 15let oauthClient: NodeOAuthClient | null = null; 16 17// Simple lock implementation for CLI (single process, no contention) 18// This prevents the "No lock mechanism provided" warning 19const locks = new Map<string, Promise<void>>(); 20 21async function requestLock<T>( 22 key: string, 23 fn: () => T | PromiseLike<T>, 24): Promise<T> { 25 // Wait for any existing lock on this key 26 while (locks.has(key)) { 27 await locks.get(key); 28 } 29 30 // Create our lock 31 let resolve: () => void; 32 const lockPromise = new Promise<void>((r) => { 33 resolve = r; 34 }); 35 locks.set(key, lockPromise); 36 37 try { 38 return await fn(); 39 } finally { 40 locks.delete(key); 41 resolve!(); 42 } 43} 44 45/** 46 * Get or create the OAuth client singleton 47 */ 48export async function getOAuthClient(): Promise<NodeOAuthClient> { 49 if (oauthClient) { 50 return oauthClient; 51 } 52 53 // Build client_id with required parameters 54 const clientIdParams = new URLSearchParams(); 55 clientIdParams.append("redirect_uri", CALLBACK_URL); 56 clientIdParams.append("scope", OAUTH_SCOPE); 57 58 const clientOptions: NodeOAuthClientOptions = { 59 clientMetadata: { 60 client_id: `http://localhost?${clientIdParams.toString()}`, 61 client_name: "Sequoia CLI", 62 client_uri: "https://sequoia.pub", 63 redirect_uris: [CALLBACK_URL], 64 grant_types: ["authorization_code", "refresh_token"], 65 response_types: ["code"], 66 token_endpoint_auth_method: "none", 67 application_type: "web", 68 scope: OAUTH_SCOPE, 69 dpop_bound_access_tokens: false, 70 }, 71 stateStore, 72 sessionStore, 73 // Configure identity resolution 74 plcDirectoryUrl: "https://plc.directory", 75 // Provide lock mechanism to prevent warning 76 requestLock, 77 }; 78 79 oauthClient = new NodeOAuthClient(clientOptions); 80 81 return oauthClient; 82} 83 84export function getOAuthScope(): string { 85 return OAUTH_SCOPE; 86} 87 88export function getCallbackUrl(): string { 89 return CALLBACK_URL; 90} 91 92export function getCallbackPort(): number { 93 return CALLBACK_PORT; 94}