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

chore: version bump and clean up

authored by stevedylan.dev and committed by tangled.org c1c21ad5 43be9f09

+17 -96
+1 -1
packages/cli/biome.json
··· 2 2 "$schema": "https://biomejs.dev/schemas/2.3.13/schema.json", 3 3 "extends": ["../../biome.json"], 4 4 "files": { 5 - "includes": ["**", "!!**/dist"] 5 + "includes": ["!!**/dist"] 6 6 } 7 7 }
+1 -1
packages/cli/package.json
··· 1 1 { 2 2 "name": "sequoia-cli", 3 - "version": "0.4.0", 3 + "version": "0.5.0", 4 4 "type": "module", 5 5 "bin": { 6 6 "sequoia": "dist/index.js"
+3 -1
packages/cli/src/commands/add.ts
··· 40 40 intro("Add Sequoia Component"); 41 41 42 42 // Validate component name 43 - const component = AVAILABLE_COMPONENTS.find((c) => c.name === componentName); 43 + const component = AVAILABLE_COMPONENTS.find( 44 + (c) => c.name === componentName, 45 + ); 44 46 if (!component) { 45 47 log.error(`Component '${componentName}' not found`); 46 48 log.info("Available components:");
+1 -86
packages/cli/src/components/sequoia-subscribe.js
··· 127 127 // ============================================================================ 128 128 129 129 /** 130 - * Resolve a DID to its PDS URL. 131 - * Supports did:plc and did:web methods. 132 - * @param {string} did - Decentralized Identifier 133 - * @returns {Promise<string>} PDS URL 134 - */ 135 - async function resolvePDS(did) { 136 - let pdsUrl; 137 - 138 - if (did.startsWith("did:plc:")) { 139 - const didDocUrl = `https://plc.directory/${did}`; 140 - const didDocResponse = await fetch(didDocUrl); 141 - if (!didDocResponse.ok) { 142 - throw new Error(`Could not fetch DID document: ${didDocResponse.status}`); 143 - } 144 - const didDoc = await didDocResponse.json(); 145 - 146 - const pdsService = didDoc.service?.find( 147 - (s) => s.id === "#atproto_pds" || s.type === "AtprotoPersonalDataServer", 148 - ); 149 - pdsUrl = pdsService?.serviceEndpoint; 150 - } else if (did.startsWith("did:web:")) { 151 - const domain = did.replace("did:web:", ""); 152 - const didDocUrl = `https://${domain}/.well-known/did.json`; 153 - const didDocResponse = await fetch(didDocUrl); 154 - if (!didDocResponse.ok) { 155 - throw new Error(`Could not fetch DID document: ${didDocResponse.status}`); 156 - } 157 - const didDoc = await didDocResponse.json(); 158 - 159 - const pdsService = didDoc.service?.find( 160 - (s) => s.id === "#atproto_pds" || s.type === "AtprotoPersonalDataServer", 161 - ); 162 - pdsUrl = pdsService?.serviceEndpoint; 163 - } else { 164 - throw new Error(`Unsupported DID method: ${did}`); 165 - } 166 - 167 - if (!pdsUrl) { 168 - throw new Error("Could not find PDS URL for user"); 169 - } 170 - 171 - return pdsUrl; 172 - } 173 - 174 - /** 175 - * Create a site.standard.graph.subscription record in the subscriber's PDS. 176 - * @param {string} did - DID of the subscriber 177 - * @param {string} accessToken - AT Protocol access token 178 - * @param {string} publicationUri - AT URI of the publication to subscribe to 179 - * @returns {Promise<{uri: string, cid: string}>} The created record's URI and CID 180 - */ 181 - async function createRecord(did, accessToken, publicationUri) { 182 - const pdsUrl = await resolvePDS(did); 183 - 184 - const collection = "site.standard.graph.subscription"; 185 - const url = `${pdsUrl}/xrpc/com.atproto.repo.createRecord`; 186 - const response = await fetch(url, { 187 - method: "POST", 188 - headers: { 189 - "Content-Type": "application/json", 190 - Authorization: `Bearer ${accessToken}`, 191 - }, 192 - body: JSON.stringify({ 193 - repo: did, 194 - collection, 195 - record: { 196 - $type: "site.standard.graph.subscription", 197 - publication: publicationUri, 198 - }, 199 - }), 200 - }); 201 - 202 - if (!response.ok) { 203 - const body = await response.json().catch(() => ({})); 204 - const message = body?.message ?? body?.error ?? `HTTP ${response.status}`; 205 - throw new Error(`Failed to create record: ${message}`); 206 - } 207 - 208 - const data = await response.json(); 209 - return { uri: data.uri, cid: data.cid }; 210 - } 211 - 212 - /** 213 130 * Fetch the publication AT URI from the host site's well-known endpoint. 214 131 * @param {string} [origin] - Origin to fetch from (defaults to current page origin) 215 132 * @returns {Promise<string>} Publication AT URI ··· 219 136 const url = `${base}/.well-known/site.standard.publication`; 220 137 const response = await fetch(url); 221 138 if (!response.ok) { 222 - throw new Error( 223 - `Could not fetch publication URI: ${response.status}`, 224 - ); 139 + throw new Error(`Could not fetch publication URI: ${response.status}`); 225 140 } 226 141 227 142 // Accept either plain text (the AT URI itself) or JSON with a `uri` field.
+1 -1
packages/cli/src/index.ts
··· 36 36 37 37 > https://tangled.org/stevedylan.dev/sequoia 38 38 `, 39 - version: "0.4.0", 39 + version: "0.5.0", 40 40 cmds: { 41 41 add: addCommand, 42 42 auth: authCommand,
+10 -6
packages/cli/src/lib/atproto.ts
··· 622 622 agent: Agent, 623 623 options: CreateBlueskyPostOptions, 624 624 ): Promise<StrongRef> { 625 - const { title, description, bskyPost, canonicalUrl, coverImage, publishedAt } = options; 625 + const { 626 + title, 627 + description, 628 + bskyPost, 629 + canonicalUrl, 630 + coverImage, 631 + publishedAt, 632 + } = options; 626 633 627 634 // Build post text: title + description 628 635 // Max 300 graphemes for Bluesky posts ··· 633 640 if (bskyPost) { 634 641 // Custom bsky post overrides any default behavior 635 642 postText = bskyPost; 636 - } 637 - else if (description) { 643 + } else if (description) { 638 644 // Try: title + description 639 645 const fullText = `${title}\n\n${description}`; 640 646 if (countGraphemes(fullText) <= MAX_GRAPHEMES) { ··· 642 648 } else { 643 649 // Truncate description to fit 644 650 const availableForDesc = 645 - MAX_GRAPHEMES - 646 - countGraphemes(title) - 647 - countGraphemes("\n\n"); 651 + MAX_GRAPHEMES - countGraphemes(title) - countGraphemes("\n\n"); 648 652 if (availableForDesc > 10) { 649 653 const truncatedDesc = truncateToGraphemes( 650 654 description,