Monorepo for Aesthetic.Computer
aesthetic.computer
1#!/usr/bin/env node
2
3/**
4 * Query P console.log('═══════════════════════════════════════')
5 console.log('Profile Information')
6 console.log('═══════════════════════════════════════')
7 console.log(`Handle: ${p.handle}`)
8 console.log(`DID: ${p.did}`)
9 console.log(`Display Name: ${p.displayName || '(not set)'}`)
10 console.log(`Description: ${p.description || '(not set)'}`)
11 console.log(`Followers: ${p.followersCount || 0}`)
12 console.log(`Following: ${p.followsCount || 0}`)
13 console.log(`Posts: ${p.postsCount || 0}`)* Introspective tool to query ATProto profile information.
14 *
15 * Usage:
16 * node query-profile.mjs aesthetic.computer
17 * node query-profile.mjs did:plc:z72i7hdynmk6r22z27h6tvur
18 */
19
20import { AtpAgent } from '@atproto/api'
21import { config } from 'dotenv'
22
23config() // Load .env if it exists
24
25const BSKY_SERVICE = process.env.BSKY_SERVICE || 'https://public.api.bsky.app'
26
27async function queryProfile(actor) {
28 console.log(`\n🔍 Querying profile for: ${actor}`)
29 console.log(`📡 Using service: ${BSKY_SERVICE}\n`)
30
31 const agent = new AtpAgent({ service: BSKY_SERVICE })
32
33 try {
34 // Use unauthenticated API endpoint
35 const response = await fetch(`${BSKY_SERVICE}/xrpc/app.bsky.actor.getProfile?actor=${actor}`)
36
37 if (!response.ok) {
38 throw new Error(`HTTP ${response.status}: ${response.statusText}`)
39 }
40
41 const profile = await response.json()
42 const p = profile
43
44 console.log('═══════════════════════════════════════')
45 console.log('Profile Information')
46 console.log('═══════════════════════════════════════')
47 console.log(`Handle: ${p.handle}`)
48 console.log(`DID: ${p.did}`)
49 console.log(`Display Name: ${p.displayName || '(not set)'}`)
50 console.log(`Description: ${p.description || '(not set)'}`)
51 console.log(`Followers: ${p.followersCount || 0}`)
52 console.log(`Following: ${p.followsCount || 0}`)
53 console.log(`Posts: ${p.postsCount || 0}`)
54
55 if (p.avatar) {
56 console.log(`Avatar: ${p.avatar}`)
57 }
58
59 if (p.banner) {
60 console.log(`Banner: ${p.banner}`)
61 }
62
63 if (p.indexedAt) {
64 console.log(`Indexed: ${new Date(p.indexedAt).toLocaleString()}`)
65 }
66
67 console.log('═══════════════════════════════════════\n')
68
69 // Get some recent posts
70 console.log('📝 Recent Posts (last 5):\n')
71 const feedResponse = await fetch(`${BSKY_SERVICE}/xrpc/app.bsky.feed.getAuthorFeed?actor=${actor}&limit=5`)
72
73 if (feedResponse.ok) {
74 const feedData = await feedResponse.json()
75
76 if (!feedData.feed || feedData.feed.length === 0) {
77 console.log(' (no posts yet)')
78 } else {
79 feedData.feed.forEach((item, i) => {
80 const post = item.post
81 const text = post.record.text || '(no text)'
82 const date = new Date(post.indexedAt).toLocaleDateString()
83 const likes = post.likeCount || 0
84 const replies = post.replyCount || 0
85 const reposts = post.repostCount || 0
86
87 console.log(`${i + 1}. [${date}] ${text.slice(0, 60)}${text.length > 60 ? '...' : ''}`)
88 console.log(` ❤️ ${likes} 💬 ${replies} 🔁 ${reposts}`)
89 console.log(` URI: ${post.uri}\n`)
90 })
91 }
92 } else {
93 console.log(' (could not fetch posts)')
94 }
95
96 return profile
97
98 } catch (error) {
99 console.error('❌ Error querying profile:', error.message)
100
101 if (error.status === 400) {
102 console.error('\n💡 Tip: Make sure the handle or DID is valid')
103 console.error(' Examples: aesthetic.computer, did:plc:...')
104 }
105
106 process.exit(1)
107 }
108}
109
110// CLI
111const actor = process.argv[2]
112
113if (!actor) {
114 console.error('Usage: node query-profile.mjs <handle-or-did>')
115 console.error('\nExamples:')
116 console.error(' node query-profile.mjs aesthetic.computer')
117 console.error(' node query-profile.mjs bsky.app')
118 console.error(' node query-profile.mjs did:plc:z72i7hdynmk6r22z27h6tvur')
119 process.exit(1)
120}
121
122queryProfile(actor)