chore: version bump and clean up
c1c21ad5
6 file(s) · +17 −96
| 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 | { |
|
| 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" |
| 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:"); |
| 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. |
|
| 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, |
| 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, |
|