Monorepo for Aesthetic.Computer aesthetic.computer

chore(at): bump @atproto/api to ^0.19.4, migrate BskyAgent → AtpAgent

- Align @atproto/api across all three package.json files (root, system/, at/)
- Replace deprecated BskyAgent with AtpAgent in 12 .mjs files
- Add at/cli.mjs — unified AT Protocol CLI (ac-at)
- Add PDS contact email (mail@aesthetic.computer) to pds.env.example
- Include AT Protocol integration audit report

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+1178 -824
+2 -2
at/bulk-share-paintings.mjs
··· 10 10 * node bulk-share-paintings.mjs 10 --delay 3000 11 11 */ 12 12 13 - import { BskyAgent, RichText } from '@atproto/api' 13 + import { AtpAgent, RichText } from '@atproto/api' 14 14 import { config } from 'dotenv' 15 15 16 16 config() ··· 34 34 } 35 35 36 36 console.log(`🔐 Logging in as @${BSKY_IDENTIFIER}...`) 37 - agent = new BskyAgent({ service: BSKY_SERVICE }) 37 + agent = new AtpAgent({ service: BSKY_SERVICE }) 38 38 await agent.login({ 39 39 identifier: BSKY_IDENTIFIER, 40 40 password: BSKY_APP_PASSWORD
+580
at/cli.mjs
··· 1 + #!/usr/bin/env node 2 + // at/cli.mjs — Unified CLI for AT Protocol tooling on Aesthetic Computer. 3 + // Usage: node at/cli.mjs <command> [options] 4 + // 5 + // Consolidates scattered AT scripts into one entry point. 6 + // Follows the same pattern as memory/cli.mjs and papers/cli.mjs. 7 + 8 + import { config } from "dotenv"; 9 + config(); // Load .env from at/ directory 10 + 11 + const PDS_URL = process.env.PDS_URL || "https://at.aesthetic.computer"; 12 + const BSKY_SERVICE = 13 + process.env.BSKY_SERVICE || "https://public.api.bsky.app"; 14 + 15 + // --------------------------------------------------------------------------- 16 + // Argument parser (same style as memory/cli.mjs) 17 + // --------------------------------------------------------------------------- 18 + 19 + function parseArgs(argv) { 20 + const out = { _: [] }; 21 + for (let i = 0; i < argv.length; i++) { 22 + const token = argv[i]; 23 + if (!token.startsWith("--")) { 24 + out._.push(token); 25 + continue; 26 + } 27 + const eqIdx = token.indexOf("="); 28 + if (eqIdx !== -1) { 29 + out[token.slice(2, eqIdx)] = token.slice(eqIdx + 1); 30 + } else { 31 + const next = argv[i + 1]; 32 + if (next && !next.startsWith("--")) { 33 + out[token.slice(2)] = next; 34 + i++; 35 + } else { 36 + out[token.slice(2)] = true; 37 + } 38 + } 39 + } 40 + return out; 41 + } 42 + 43 + // --------------------------------------------------------------------------- 44 + // Helpers 45 + // --------------------------------------------------------------------------- 46 + 47 + function requireArg(args, position, name) { 48 + const val = args._[position]; 49 + if (!val) { 50 + console.error(`Missing required argument: <${name}>`); 51 + process.exit(1); 52 + } 53 + return val; 54 + } 55 + 56 + async function fetchJSON(url) { 57 + const res = await fetch(url); 58 + if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`); 59 + return res.json(); 60 + } 61 + 62 + function adminAuth() { 63 + const pw = process.env.PDS_ADMIN_PASSWORD; 64 + if (!pw) { 65 + console.error("PDS_ADMIN_PASSWORD environment variable is required."); 66 + process.exit(1); 67 + } 68 + return `Basic ${Buffer.from(`admin:${pw}`).toString("base64")}`; 69 + } 70 + 71 + // --------------------------------------------------------------------------- 72 + // Commands 73 + // --------------------------------------------------------------------------- 74 + 75 + async function commandHealth() { 76 + console.log(`\nPDS Health Check — ${PDS_URL}\n`); 77 + 78 + // HTTP health 79 + try { 80 + const res = await fetch(`${PDS_URL}/xrpc/_health`); 81 + if (res.ok) { 82 + const data = await res.json(); 83 + console.log(` HTTP: OK (version ${data.version || "unknown"})`); 84 + } else { 85 + console.log(` HTTP: FAIL (${res.status})`); 86 + } 87 + } catch (e) { 88 + console.log(` HTTP: FAIL (${e.message})`); 89 + } 90 + 91 + // Describe server 92 + try { 93 + const desc = await fetchJSON( 94 + `${PDS_URL}/xrpc/com.atproto.server.describeServer`, 95 + ); 96 + console.log(` DID: ${desc.did || "?"}`); 97 + console.log( 98 + ` Invite: ${desc.inviteCodeRequired ? "required" : "open"}`, 99 + ); 100 + if (desc.availableUserDomains?.length) { 101 + console.log(` Domains: ${desc.availableUserDomains.join(", ")}`); 102 + } 103 + if (desc.contact?.email) { 104 + console.log(` Contact: ${desc.contact.email}`); 105 + } 106 + } catch (e) { 107 + console.log(` Server: Could not describe (${e.message})`); 108 + } 109 + 110 + console.log(); 111 + } 112 + 113 + async function commandResolve(args) { 114 + const input = requireArg(args, 1, "handle-or-did"); 115 + 116 + let did = input; 117 + 118 + // Resolve handle → DID 119 + if (!input.startsWith("did:")) { 120 + console.log(`Resolving handle: ${input}`); 121 + const profile = await fetchJSON( 122 + `${BSKY_SERVICE}/xrpc/app.bsky.actor.getProfile?actor=${encodeURIComponent(input)}`, 123 + ); 124 + did = profile.did; 125 + console.log(` @${input} -> ${did}\n`); 126 + } 127 + 128 + // Fetch DID document 129 + if (did.startsWith("did:plc:")) { 130 + const doc = await fetchJSON(`https://plc.directory/${did}`); 131 + 132 + console.log(`DID: ${did}`); 133 + if (doc.alsoKnownAs?.length) { 134 + doc.alsoKnownAs.forEach((aka) => { 135 + const label = aka.startsWith("at://") ? `@${aka.slice(5)}` : aka; 136 + console.log(`AKA: ${label}`); 137 + }); 138 + } 139 + if (doc.service?.length) { 140 + doc.service.forEach((svc) => { 141 + const star = 142 + svc.serviceEndpoint.includes("aesthetic.computer") ? " (ours)" : ""; 143 + console.log(`Service: ${svc.type} -> ${svc.serviceEndpoint}${star}`); 144 + }); 145 + } 146 + if (doc.verificationMethod?.length) { 147 + doc.verificationMethod.forEach((vm) => { 148 + const key = vm.publicKeyMultibase 149 + ? vm.publicKeyMultibase.slice(0, 24) + "..." 150 + : "?"; 151 + console.log(`Key: ${vm.id} (${key})`); 152 + }); 153 + } 154 + 155 + if (args.json) { 156 + console.log(`\n${JSON.stringify(doc, null, 2)}`); 157 + } 158 + } else if (did.startsWith("did:web:")) { 159 + const domain = did.replace("did:web:", ""); 160 + const doc = await fetchJSON(`https://${domain}/.well-known/did.json`); 161 + console.log(JSON.stringify(doc, null, 2)); 162 + } else { 163 + console.error(`Unsupported DID method: ${did}`); 164 + } 165 + console.log(); 166 + } 167 + 168 + async function commandProfile(args) { 169 + const actor = requireArg(args, 1, "handle-or-did"); 170 + const profile = await fetchJSON( 171 + `${BSKY_SERVICE}/xrpc/app.bsky.actor.getProfile?actor=${encodeURIComponent(actor)}`, 172 + ); 173 + const p = profile; 174 + 175 + console.log(`\n Handle: @${p.handle}`); 176 + console.log(` DID: ${p.did}`); 177 + console.log(` Name: ${p.displayName || "(none)"}`); 178 + console.log(` Bio: ${p.description || "(none)"}`); 179 + console.log(` Followers: ${p.followersCount || 0}`); 180 + console.log(` Following: ${p.followsCount || 0}`); 181 + console.log(` Posts: ${p.postsCount || 0}`); 182 + if (p.avatar) console.log(` Avatar: ${p.avatar}`); 183 + console.log(); 184 + } 185 + 186 + async function commandPosts(args) { 187 + const actor = requireArg(args, 1, "handle-or-did"); 188 + const limit = parseInt(args.limit) || 10; 189 + 190 + const data = await fetchJSON( 191 + `${BSKY_SERVICE}/xrpc/app.bsky.feed.getAuthorFeed?actor=${encodeURIComponent(actor)}&limit=${limit}`, 192 + ); 193 + 194 + if (!data.feed?.length) { 195 + console.log("(no posts)"); 196 + return; 197 + } 198 + 199 + console.log(`\n${data.feed.length} posts from @${actor}:\n`); 200 + 201 + data.feed.forEach((item, i) => { 202 + const post = item.post; 203 + const text = post.record?.text || "(no text)"; 204 + const date = new Date(post.indexedAt).toLocaleDateString(); 205 + const likes = post.likeCount || 0; 206 + const replies = post.replyCount || 0; 207 + const reposts = post.repostCount || 0; 208 + console.log( 209 + ` ${i + 1}. [${date}] ${text.slice(0, 80)}${text.length > 80 ? "..." : ""}`, 210 + ); 211 + console.log(` likes:${likes} replies:${replies} reposts:${reposts}`); 212 + console.log(` ${post.uri}\n`); 213 + }); 214 + } 215 + 216 + async function commandPost(args) { 217 + const text = requireArg(args, 1, "text"); 218 + 219 + const identifier = process.env.BSKY_IDENTIFIER; 220 + const appPassword = process.env.BSKY_APP_PASSWORD; 221 + const service = process.env.BSKY_SERVICE || "https://bsky.social"; 222 + 223 + if (!identifier || !appPassword) { 224 + console.error( 225 + "Set BSKY_IDENTIFIER and BSKY_APP_PASSWORD in your environment.", 226 + ); 227 + process.exit(1); 228 + } 229 + 230 + const { AtpAgent, RichText } = await import("@atproto/api"); 231 + const agent = new AtpAgent({ service }); 232 + 233 + console.log(`Logging in as @${identifier}...`); 234 + await agent.login({ identifier, password: appPassword }); 235 + 236 + const rt = new RichText({ text }); 237 + await rt.detectFacets(agent); 238 + 239 + const postRecord = { 240 + text: rt.text, 241 + facets: rt.facets, 242 + createdAt: new Date().toISOString(), 243 + }; 244 + 245 + // Attach image if provided 246 + if (args.image) { 247 + const { readFileSync } = await import("fs"); 248 + const imageData = readFileSync(args.image); 249 + const { data } = await agent.uploadBlob(imageData, { 250 + encoding: "image/png", 251 + }); 252 + postRecord.embed = { 253 + $type: "app.bsky.embed.images", 254 + images: [ 255 + { image: data.blob, alt: args.alt || "Image from Aesthetic Computer" }, 256 + ], 257 + }; 258 + console.log(`Uploaded image: ${args.image}`); 259 + } 260 + 261 + const response = await agent.post(postRecord); 262 + const rkey = response.uri.split("/").pop(); 263 + 264 + console.log(`Posted: https://bsky.app/profile/${identifier}/post/${rkey}`); 265 + } 266 + 267 + async function commandRecords(args) { 268 + const repo = requireArg(args, 1, "did"); 269 + const collection = requireArg(args, 2, "collection"); 270 + const limit = parseInt(args.limit) || 25; 271 + 272 + const data = await fetchJSON( 273 + `${PDS_URL}/xrpc/com.atproto.repo.listRecords?repo=${encodeURIComponent(repo)}&collection=${encodeURIComponent(collection)}&limit=${limit}`, 274 + ); 275 + 276 + if (!data.records?.length) { 277 + console.log(`(no records in ${collection})`); 278 + return; 279 + } 280 + 281 + console.log(`\n${data.records.length} records in ${collection}:\n`); 282 + 283 + data.records.forEach((rec, i) => { 284 + const rkey = rec.uri.split("/").pop(); 285 + const val = rec.value; 286 + // Show a compact summary depending on type 287 + const when = val.when || val.createdAt || ""; 288 + const label = 289 + val.slug || val.code || val.mood || val.headline || val.text || ""; 290 + console.log( 291 + ` ${i + 1}. ${rkey} ${label.slice(0, 60)} ${when ? `(${when.slice(0, 10)})` : ""}`, 292 + ); 293 + }); 294 + console.log(); 295 + } 296 + 297 + async function commandLexicons() { 298 + const { readdirSync, readFileSync } = await import("fs"); 299 + const { join, dirname } = await import("path"); 300 + const { fileURLToPath } = await import("url"); 301 + 302 + const __dirname = dirname(fileURLToPath(import.meta.url)); 303 + const lexDir = join(__dirname, "lexicons", "computer", "aesthetic"); 304 + 305 + let files; 306 + try { 307 + files = readdirSync(lexDir).filter((f) => f.endsWith(".json")); 308 + } catch { 309 + console.error(`Lexicon directory not found: ${lexDir}`); 310 + return; 311 + } 312 + 313 + console.log(`\nAesthetic Computer Lexicons (${files.length}):\n`); 314 + 315 + for (const file of files) { 316 + const lex = JSON.parse(readFileSync(join(lexDir, file), "utf8")); 317 + const main = lex.defs?.main; 318 + const desc = main?.description || ""; 319 + const required = main?.record?.required || []; 320 + const props = Object.keys(main?.record?.properties || {}); 321 + 322 + console.log(` ${lex.id}`); 323 + console.log(` ${desc}`); 324 + console.log(` required: ${required.join(", ") || "(none)"}`); 325 + console.log(` fields: ${props.join(", ")}`); 326 + console.log(); 327 + } 328 + } 329 + 330 + async function commandInvite() { 331 + const auth = adminAuth(); 332 + 333 + const res = await fetch( 334 + `${PDS_URL}/xrpc/com.atproto.server.createInviteCode`, 335 + { 336 + method: "POST", 337 + headers: { 338 + "Content-Type": "application/json", 339 + Authorization: auth, 340 + }, 341 + body: JSON.stringify({ useCount: 1 }), 342 + }, 343 + ); 344 + 345 + if (!res.ok) { 346 + const text = await res.text(); 347 + console.error(`Failed to create invite: ${res.status} ${text}`); 348 + process.exit(1); 349 + } 350 + 351 + const data = await res.json(); 352 + console.log(`Invite code: ${data.code}`); 353 + } 354 + 355 + async function commandAccounts(args) { 356 + const limit = parseInt(args.limit) || 50; 357 + 358 + // listRepos is a public endpoint 359 + const res = await fetch( 360 + `${PDS_URL}/xrpc/com.atproto.sync.listRepos?limit=${limit}`, 361 + ); 362 + 363 + if (!res.ok) { 364 + console.error(`Failed to list accounts: ${res.status}`); 365 + process.exit(1); 366 + } 367 + 368 + const data = await res.json(); 369 + printRepos(data); 370 + } 371 + 372 + function printRepos(data) { 373 + const repos = data.repos || []; 374 + console.log(`\n${repos.length} accounts on PDS:\n`); 375 + repos.forEach((repo, i) => { 376 + const active = repo.active !== false ? "" : " (inactive)"; 377 + console.log( 378 + ` ${String(i + 1).padStart(3)}. ${repo.did}${active}`, 379 + ); 380 + }); 381 + console.log(); 382 + } 383 + 384 + async function commandAccountCheck(args) { 385 + const input = requireArg(args, 1, "handle-or-did"); 386 + 387 + // Resolve to DID if needed 388 + let did = input; 389 + if (!input.startsWith("did:")) { 390 + const profile = await fetchJSON( 391 + `${BSKY_SERVICE}/xrpc/app.bsky.actor.getProfile?actor=${encodeURIComponent(input)}`, 392 + ); 393 + did = profile.did; 394 + } 395 + 396 + console.log(`\nAccount check for: ${did}\n`); 397 + 398 + // Check DID document 399 + try { 400 + const doc = await fetchJSON(`https://plc.directory/${did}`); 401 + const pds = doc.service?.find( 402 + (s) => s.type === "AtprotoPersonalDataServer", 403 + ); 404 + const handle = doc.alsoKnownAs 405 + ?.find((a) => a.startsWith("at://")) 406 + ?.slice(5); 407 + 408 + console.log(` Handle: @${handle || "?"}`); 409 + console.log(` PDS: ${pds?.serviceEndpoint || "?"}`); 410 + 411 + if (pds?.serviceEndpoint?.includes("aesthetic.computer")) { 412 + console.log(` Ours: yes`); 413 + } 414 + } catch (e) { 415 + console.log(` DID doc: failed (${e.message})`); 416 + } 417 + 418 + // List collections on our PDS 419 + const collections = [ 420 + "computer.aesthetic.painting", 421 + "computer.aesthetic.mood", 422 + "computer.aesthetic.piece", 423 + "computer.aesthetic.kidlisp", 424 + "computer.aesthetic.tape", 425 + "computer.aesthetic.news", 426 + ]; 427 + 428 + console.log(`\n Records on ${PDS_URL}:`); 429 + for (const col of collections) { 430 + try { 431 + const data = await fetchJSON( 432 + `${PDS_URL}/xrpc/com.atproto.repo.listRecords?repo=${encodeURIComponent(did)}&collection=${encodeURIComponent(col)}&limit=1`, 433 + ); 434 + const count = data.records?.length 435 + ? `${data.records.length}+ records` 436 + : "0 records"; 437 + console.log(` ${col.replace("computer.aesthetic.", "")}: ${count}`); 438 + } catch { 439 + console.log( 440 + ` ${col.replace("computer.aesthetic.", "")}: (error or not found)`, 441 + ); 442 + } 443 + } 444 + console.log(); 445 + } 446 + 447 + async function commandSyncStatus() { 448 + console.log(`\nSync Status — ${PDS_URL}\n`); 449 + 450 + // Get the art account DID (guest) 451 + const artDid = "did:plc:tliuubv7lyv2uiknsjbf4ppw"; 452 + 453 + const collections = [ 454 + "computer.aesthetic.painting", 455 + "computer.aesthetic.mood", 456 + "computer.aesthetic.piece", 457 + "computer.aesthetic.kidlisp", 458 + "computer.aesthetic.tape", 459 + "computer.aesthetic.news", 460 + ]; 461 + 462 + // Check record counts on art account as a quick indicator 463 + console.log(` Art account (${artDid}):`); 464 + for (const col of collections) { 465 + try { 466 + const data = await fetchJSON( 467 + `${PDS_URL}/xrpc/com.atproto.repo.listRecords?repo=${encodeURIComponent(artDid)}&collection=${encodeURIComponent(col)}&limit=100`, 468 + ); 469 + const name = col.replace("computer.aesthetic.", ""); 470 + const count = data.records?.length || 0; 471 + const cursor = data.cursor ? " (more available)" : ""; 472 + console.log(` ${name.padEnd(12)} ${count} records${cursor}`); 473 + } catch { 474 + const name = col.replace("computer.aesthetic.", ""); 475 + console.log(` ${name.padEnd(12)} (error)`); 476 + } 477 + } 478 + 479 + // List all repos to get user count 480 + try { 481 + const repos = await fetchJSON( 482 + `${PDS_URL}/xrpc/com.atproto.sync.listRepos?limit=200`, 483 + ); 484 + const count = repos.repos?.length || 0; 485 + console.log(`\n Total accounts: ${count}`); 486 + } catch { 487 + console.log(`\n Total accounts: (could not fetch)`); 488 + } 489 + 490 + console.log(); 491 + } 492 + 493 + // --------------------------------------------------------------------------- 494 + // Help 495 + // --------------------------------------------------------------------------- 496 + 497 + function printHelp() { 498 + console.log(`ac-at — AT Protocol CLI for Aesthetic Computer 499 + 500 + Usage: ac-at <command> [options] 501 + 502 + Query & Inspect: 503 + health PDS health check 504 + resolve <handle-or-did> [--json] Resolve DID document 505 + profile <handle-or-did> Query profile 506 + posts <handle-or-did> [--limit=N] Query posts 507 + records <did> <collection> [--limit=N] List records 508 + lexicons Show AC custom lexicon schemas 509 + 510 + Publish: 511 + post <text> [--image=path] [--alt=text] Post to Bluesky 512 + 513 + Admin (requires PDS_ADMIN_PASSWORD): 514 + invite Generate PDS invite code 515 + accounts [--limit=N] List PDS accounts 516 + account:check <handle-or-did> Inspect account & record counts 517 + sync:status Record counts across collections 518 + 519 + Environment: 520 + PDS_URL PDS endpoint (default: https://at.aesthetic.computer) 521 + PDS_ADMIN_PASSWORD Admin password for PDS operations 522 + BSKY_IDENTIFIER Bluesky handle for posting 523 + BSKY_APP_PASSWORD Bluesky app password for posting 524 + BSKY_SERVICE Bluesky API (default: https://public.api.bsky.app) 525 + 526 + Examples: 527 + ac-at health 528 + ac-at resolve aesthetic.computer 529 + ac-at profile jeffrey.at.aesthetic.computer 530 + ac-at posts aesthetic.computer --limit=5 531 + ac-at records did:plc:k3k3wknzkcnekbnyde4dbatz computer.aesthetic.painting 532 + ac-at post "Hello from AC!" --image=painting.png 533 + ac-at invite 534 + ac-at account:check jeffrey.at.aesthetic.computer 535 + ac-at sync:status 536 + `); 537 + } 538 + 539 + // --------------------------------------------------------------------------- 540 + // Main 541 + // --------------------------------------------------------------------------- 542 + 543 + const COMMANDS = { 544 + health: commandHealth, 545 + resolve: commandResolve, 546 + profile: commandProfile, 547 + posts: commandPosts, 548 + post: commandPost, 549 + records: commandRecords, 550 + lexicons: commandLexicons, 551 + invite: commandInvite, 552 + accounts: commandAccounts, 553 + "account:check": commandAccountCheck, 554 + "sync:status": commandSyncStatus, 555 + }; 556 + 557 + async function main() { 558 + const args = parseArgs(process.argv.slice(2)); 559 + const command = args._[0] || "help"; 560 + 561 + if (command === "help" || command === "--help" || command === "-h") { 562 + printHelp(); 563 + return; 564 + } 565 + 566 + const handler = COMMANDS[command]; 567 + if (!handler) { 568 + console.error(`Unknown command: ${command}\n`); 569 + printHelp(); 570 + process.exitCode = 1; 571 + return; 572 + } 573 + 574 + await handler(args); 575 + } 576 + 577 + main().catch((err) => { 578 + console.error(`ac-at: ${err.message}`); 579 + process.exit(1); 580 + });
+2 -2
at/delete-all-posts.mjs
··· 10 10 * node delete-all-posts.mjs --confirm 11 11 */ 12 12 13 - import { BskyAgent } from '@atproto/api' 13 + import { AtpAgent } from '@atproto/api' 14 14 import { config } from 'dotenv' 15 15 16 16 config() ··· 31 31 console.log(`\n🗑️ Delete All Posts from @${BSKY_IDENTIFIER}\n`) 32 32 console.log('═'.repeat(50) + '\n') 33 33 34 - const agent = new BskyAgent({ service: BSKY_SERVICE }) 34 + const agent = new AtpAgent({ service: BSKY_SERVICE }) 35 35 36 36 try { 37 37 // Login
+68 -35
at/package-lock.json
··· 9 9 "version": "0.0.1", 10 10 "license": "UNLICENSED", 11 11 "dependencies": { 12 - "@atproto/api": "^0.17.0", 12 + "@atproto/api": "^0.19.4", 13 13 "@atproto/identity": "^0.4.3", 14 14 "@atproto/lexicon": "^0.4.2", 15 15 "@atproto/syntax": "^0.3.1", ··· 22 22 } 23 23 }, 24 24 "node_modules/@atproto/api": { 25 - "version": "0.17.0", 26 - "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.17.0.tgz", 27 - "integrity": "sha512-FNS9SW7/3kslAnJH7F4fO9/jPjXzC0NMD6u9NjJ/h4EnaIEpWHZQPkmD9Q2hvAwD6+Uo2boYZEPKkOa55Lr5Dg==", 25 + "version": "0.19.4", 26 + "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.19.4.tgz", 27 + "integrity": "sha512-fYNM62vdXxer0h8a9Jzl4/ag9uFIe0nTO+LkC6KTlx1yUDigrAoQMMbllIiCWj62GhUMxAkHabk/BZjjVAfKng==", 28 28 "license": "MIT", 29 29 "dependencies": { 30 - "@atproto/common-web": "^0.4.3", 31 - "@atproto/lexicon": "^0.5.1", 32 - "@atproto/syntax": "^0.4.1", 33 - "@atproto/xrpc": "^0.7.5", 30 + "@atproto/common-web": "^0.4.18", 31 + "@atproto/lexicon": "^0.6.2", 32 + "@atproto/syntax": "^0.5.1", 33 + "@atproto/xrpc": "^0.7.7", 34 34 "await-lock": "^2.2.2", 35 35 "multiformats": "^9.9.0", 36 36 "tlds": "^1.234.0", ··· 38 38 } 39 39 }, 40 40 "node_modules/@atproto/api/node_modules/@atproto/lexicon": { 41 - "version": "0.5.1", 42 - "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.5.1.tgz", 43 - "integrity": "sha512-y8AEtYmfgVl4fqFxqXAeGvhesiGkxiy3CWoJIfsFDDdTlZUC8DFnZrYhcqkIop3OlCkkljvpSJi1hbeC1tbi8A==", 41 + "version": "0.6.2", 42 + "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.6.2.tgz", 43 + "integrity": "sha512-p3Ly6hinVZW0ETuAXZMeUGwuMm3g8HvQMQ41yyEE6AL0hAkfeKFaZKos6BdBrr6CjkpbrDZqE8M+5+QOceysMw==", 44 44 "license": "MIT", 45 45 "dependencies": { 46 - "@atproto/common-web": "^0.4.3", 47 - "@atproto/syntax": "^0.4.1", 46 + "@atproto/common-web": "^0.4.18", 47 + "@atproto/syntax": "^0.5.0", 48 48 "iso-datestring-validator": "^2.2.2", 49 49 "multiformats": "^9.9.0", 50 50 "zod": "^3.23.8" 51 51 } 52 52 }, 53 53 "node_modules/@atproto/api/node_modules/@atproto/syntax": { 54 - "version": "0.4.1", 55 - "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.1.tgz", 56 - "integrity": "sha512-CJdImtLAiFO+0z3BWTtxwk6aY5w4t8orHTMVJgkf++QRJWTxPbIFko/0hrkADB7n2EruDxDSeAgfUGehpH6ngw==", 57 - "license": "MIT" 54 + "version": "0.5.1", 55 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.5.1.tgz", 56 + "integrity": "sha512-J8DJjgKgACIyCTbpfvoTnf7+ofTx1kxTGO7KAftkC+jczaMdQhKdgIBAg2DaYy+80cvYGTHy5q/HI9qMAwGbWw==", 57 + "license": "MIT", 58 + "dependencies": { 59 + "tslib": "^2.8.1" 60 + } 58 61 }, 59 62 "node_modules/@atproto/api/node_modules/@atproto/xrpc": { 60 - "version": "0.7.5", 61 - "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.5.tgz", 62 - "integrity": "sha512-MUYNn5d2hv8yVegRL0ccHvTHAVj5JSnW07bkbiaz96UH45lvYNRVwt44z+yYVnb0/mvBzyD3/ZQ55TRGt7fHkA==", 63 + "version": "0.7.7", 64 + "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.7.tgz", 65 + "integrity": "sha512-K1ZyO/BU8JNtXX5dmPp7b5UrkLMMqpsIa/Lrj5D3Su+j1Xwq1m6QJ2XJ1AgjEjkI1v4Muzm7klianLE6XGxtmA==", 63 66 "license": "MIT", 64 67 "dependencies": { 65 - "@atproto/lexicon": "^0.5.1", 68 + "@atproto/lexicon": "^0.6.0", 66 69 "zod": "^3.23.8" 67 70 } 68 71 }, 69 72 "node_modules/@atproto/common-web": { 70 - "version": "0.4.3", 71 - "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.3.tgz", 72 - "integrity": "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg==", 73 + "version": "0.4.19", 74 + "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.19.tgz", 75 + "integrity": "sha512-3BTi58p5WpT+9/zb6UZrdsXcfPo5P45UJm0E4iwHLILr+jc37CuBj9JReDSZ4U0i9RTrI3ZkfySyZ9bd+LnMsw==", 73 76 "license": "MIT", 74 77 "dependencies": { 75 - "graphemer": "^1.4.0", 76 - "multiformats": "^9.9.0", 77 - "uint8arrays": "3.0.0", 78 + "@atproto/lex-data": "^0.0.14", 79 + "@atproto/lex-json": "^0.0.14", 80 + "@atproto/syntax": "^0.5.1", 78 81 "zod": "^3.23.8" 82 + } 83 + }, 84 + "node_modules/@atproto/common-web/node_modules/@atproto/syntax": { 85 + "version": "0.5.1", 86 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.5.1.tgz", 87 + "integrity": "sha512-J8DJjgKgACIyCTbpfvoTnf7+ofTx1kxTGO7KAftkC+jczaMdQhKdgIBAg2DaYy+80cvYGTHy5q/HI9qMAwGbWw==", 88 + "license": "MIT", 89 + "dependencies": { 90 + "tslib": "^2.8.1" 79 91 } 80 92 }, 81 93 "node_modules/@atproto/crypto": { ··· 103 115 }, 104 116 "engines": { 105 117 "node": ">=18.7.0" 118 + } 119 + }, 120 + "node_modules/@atproto/lex-data": { 121 + "version": "0.0.14", 122 + "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.14.tgz", 123 + "integrity": "sha512-53DUa9664SS76nGAMYopWsO10OH0AAdf7P/HSKB6Wzx3iqe6lk/K61QZnKxOG1LreYl5CfvIJU6eNf4txI6GlQ==", 124 + "license": "MIT", 125 + "dependencies": { 126 + "multiformats": "^9.9.0", 127 + "tslib": "^2.8.1", 128 + "uint8arrays": "3.0.0", 129 + "unicode-segmenter": "^0.14.0" 130 + } 131 + }, 132 + "node_modules/@atproto/lex-json": { 133 + "version": "0.0.14", 134 + "resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.0.14.tgz", 135 + "integrity": "sha512-6lPkDKqe7teEu4WrN5q7400cvZKgYS3uwUMvzG3F9XkgVYhOwSDCtouV/nSLBbpvo3l9OP0kiigtclcNcyekww==", 136 + "license": "MIT", 137 + "dependencies": { 138 + "@atproto/lex-data": "^0.0.14", 139 + "tslib": "^2.8.1" 106 140 } 107 141 }, 108 142 "node_modules/@atproto/lexicon": { ··· 641 675 "url": "https://dotenvx.com" 642 676 } 643 677 }, 644 - "node_modules/graphemer": { 645 - "version": "1.4.0", 646 - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", 647 - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", 648 - "license": "MIT" 649 - }, 650 678 "node_modules/iso-datestring-validator": { 651 679 "version": "2.2.2", 652 680 "resolved": "https://registry.npmjs.org/iso-datestring-validator/-/iso-datestring-validator-2.2.2.tgz", ··· 726 754 "version": "2.8.1", 727 755 "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", 728 756 "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", 729 - "license": "0BSD", 730 - "optional": true 757 + "license": "0BSD" 731 758 }, 732 759 "node_modules/uint8arrays": { 733 760 "version": "3.0.0", ··· 743 770 "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", 744 771 "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", 745 772 "dev": true, 773 + "license": "MIT" 774 + }, 775 + "node_modules/unicode-segmenter": { 776 + "version": "0.14.5", 777 + "resolved": "https://registry.npmjs.org/unicode-segmenter/-/unicode-segmenter-0.14.5.tgz", 778 + "integrity": "sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g==", 746 779 "license": "MIT" 747 780 }, 748 781 "node_modules/zod": {
+1 -1
at/package.json
··· 14 14 "test": "node test-all.mjs" 15 15 }, 16 16 "dependencies": { 17 - "@atproto/api": "^0.17.0", 17 + "@atproto/api": "^0.19.4", 18 18 "@atproto/identity": "^0.4.3", 19 19 "@atproto/lexicon": "^0.4.2", 20 20 "@atproto/syntax": "^0.3.1",
+6
at/pds/config/pds.env.example
··· 88 88 PDS_EMAIL_FROM_ADDRESS=noreply@aesthetic.computer 89 89 90 90 # ============================================================================= 91 + # OPTIONAL: Contact Information (shown in describeServer) 92 + # ============================================================================= 93 + 94 + PDS_CONTACT_EMAIL_ADDRESS=mail@aesthetic.computer 95 + 96 + # ============================================================================= 91 97 # OPTIONAL: Service Configuration 92 98 # ============================================================================= 93 99
+2 -2
at/post-to-bluesky.mjs
··· 11 11 * node post-to-bluesky.mjs "New painting!" --image painting.png 12 12 */ 13 13 14 - import { BskyAgent } from '@atproto/api' 14 + import { AtpAgent } from '@atproto/api' 15 15 import { config } from 'dotenv' 16 16 import { readFileSync } from 'fs' 17 17 ··· 34 34 console.log(`\n📤 Posting to Bluesky as @${BSKY_IDENTIFIER}`) 35 35 console.log(`📡 Using service: ${BSKY_SERVICE}\n`) 36 36 37 - const agent = new BskyAgent({ service: BSKY_SERVICE }) 37 + const agent = new AtpAgent({ service: BSKY_SERVICE }) 38 38 39 39 try { 40 40 // Login
+2 -2
at/query-posts.mjs
··· 10 10 * node query-posts.mjs aesthetic.computer --limit 20 11 11 */ 12 12 13 - import { BskyAgent } from '@atproto/api' 13 + import { AtpAgent } from '@atproto/api' 14 14 import { config } from 'dotenv' 15 15 16 16 config() ··· 22 22 console.log(`📡 Using service: ${BSKY_SERVICE}`) 23 23 console.log(`📊 Limit: ${limit}\n`) 24 24 25 - const agent = new BskyAgent({ service: BSKY_SERVICE }) 25 + const agent = new AtpAgent({ service: BSKY_SERVICE }) 26 26 27 27 try { 28 28 const feed = await agent.getAuthorFeed({ actor, limit })
+2 -2
at/query-profile.mjs
··· 17 17 * node query-profile.mjs did:plc:z72i7hdynmk6r22z27h6tvur 18 18 */ 19 19 20 - import { BskyAgent } from '@atproto/api' 20 + import { AtpAgent } from '@atproto/api' 21 21 import { config } from 'dotenv' 22 22 23 23 config() // Load .env if it exists ··· 28 28 console.log(`\n🔍 Querying profile for: ${actor}`) 29 29 console.log(`📡 Using service: ${BSKY_SERVICE}\n`) 30 30 31 - const agent = new BskyAgent({ service: BSKY_SERVICE }) 31 + const agent = new AtpAgent({ service: BSKY_SERVICE }) 32 32 33 33 try { 34 34 // Use unauthenticated API endpoint
+2 -2
at/scripts/publish-lexicons.mjs
··· 26 26 * node publish-lexicons.mjs --verify-dns # Verify DNS is configured correctly 27 27 */ 28 28 29 - import { BskyAgent } from '@atproto/api'; 29 + import { AtpAgent } from '@atproto/api'; 30 30 import { readdir, readFile } from 'fs/promises'; 31 31 import path from 'path'; 32 32 import { fileURLToPath } from 'url'; ··· 348 348 log(`Identifier: ${identifier}`, 'dim'); 349 349 log(`Note: Publishing lexicons to main account (aesthetic.computer)`, 'yellow'); 350 350 351 - const agent = new BskyAgent({ service: pdsUrl }); 351 + const agent = new AtpAgent({ service: pdsUrl }); 352 352 353 353 try { 354 354 await agent.login({
+2 -2
at/share-latest-painting.mjs
··· 12 12 * node share-latest-painting.mjs @handle --message "Custom message" 13 13 */ 14 14 15 - import { BskyAgent, RichText } from '@atproto/api' 15 + import { AtpAgent, RichText } from '@atproto/api' 16 16 import { config } from 'dotenv' 17 17 import { writeFileSync } from 'fs' 18 18 ··· 115 115 console.log(`📤 Posting to Bluesky as @${BSKY_IDENTIFIER}`) 116 116 console.log(`📡 Using service: ${BSKY_SERVICE}\n`) 117 117 118 - const agent = new BskyAgent({ service: BSKY_SERVICE }) 118 + const agent = new AtpAgent({ service: BSKY_SERVICE }) 119 119 120 120 try { 121 121 // Login
+2 -2
at/share-random-painting.mjs
··· 11 11 * node share-random-painting.mjs --preview 12 12 */ 13 13 14 - import { BskyAgent, RichText } from '@atproto/api' 14 + import { AtpAgent, RichText } from '@atproto/api' 15 15 import { config } from 'dotenv' 16 16 import { writeFileSync } from 'fs' 17 17 import sharp from 'sharp' ··· 191 191 192 192 console.log(`📤 Posting to Bluesky as @${BSKY_IDENTIFIER}\n`) 193 193 194 - const agent = new BskyAgent({ service: BSKY_SERVICE }) 194 + const agent = new AtpAgent({ service: BSKY_SERVICE }) 195 195 196 196 try { 197 197 // Login
+2 -2
at/test-all.mjs
··· 9 9 console.log('\n🧪 Testing ATProto Tools\n') 10 10 console.log('═══════════════════════════════════════\n') 11 11 12 - import { BskyAgent } from '@atproto/api' 12 + import { AtpAgent } from '@atproto/api' 13 13 14 14 const testHandle = 'aesthetic.computer' 15 15 const service = 'https://public.api.bsky.app' ··· 43 43 console.log(' ⏭️ Skipped (no credentials in .env)') 44 44 console.log(' 💡 To test: Copy .env.example to .env and add credentials\n') 45 45 } else { 46 - const agent = new BskyAgent({ service: 'https://bsky.social' }) 46 + const agent = new AtpAgent({ service: 'https://bsky.social' }) 47 47 await agent.login({ 48 48 identifier: process.env.BSKY_IDENTIFIER, 49 49 password: process.env.BSKY_APP_PASSWORD
+12 -4
oven/native-builder.mjs
··· 58 58 job.logs.splice(0, job.logs.length - MAX_LOG_LINES); 59 59 job.updatedAt = nowISO(); 60 60 61 - // Throttled progress broadcast (every 2s max) 62 - if (progressCallback && Date.now() - lastProgressBroadcast > 2000) { 63 - lastProgressBroadcast = Date.now(); 64 - progressCallback(makeSnapshot(job)); 61 + // Broadcast every log line with the last few lines attached 62 + if (progressCallback) { 63 + const now = Date.now(); 64 + // Full snapshot every 2s, lightweight log-only message in between 65 + if (now - lastProgressBroadcast > 2000) { 66 + lastProgressBroadcast = now; 67 + const snap = makeSnapshot(job); 68 + snap.recentLines = job.logs.slice(-8).map(l => l.line); 69 + progressCallback(snap); 70 + } else { 71 + progressCallback({ id: job.id, line: clean, stage: job.stage, percent: job.percent }); 72 + } 65 73 } 66 74 67 75 // Parse progress hints from build-and-flash.sh + upload-release.sh output
+64 -708
package-lock.json
··· 9 9 "version": "1.0.0", 10 10 "license": "ISC", 11 11 "dependencies": { 12 - "@atproto/api": "^0.17.2", 12 + "@atproto/api": "^0.19.4", 13 13 "@aws-sdk/client-s3": "^3.934.0", 14 14 "@babel/core": "^7.28.5", 15 15 "@babel/preset-react": "^7.28.5", ··· 23 23 "gbasm": "^0.0.21", 24 24 "gif-encoder-2": "^1.0.5", 25 25 "ink": "^6.5.1", 26 - "instagram-private-api": "^1.46.1", 27 26 "jszip": "^3.10.1", 28 27 "mongodb": "^6.20.0", 29 28 "node-datachannel": "^0.30.0", ··· 93 92 } 94 93 }, 95 94 "node_modules/@atproto/api": { 96 - "version": "0.17.7", 97 - "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.17.7.tgz", 98 - "integrity": "sha512-V+OJBZq9chcrD21xk1bUa6oc5DSKfQj5DmUPf5rmZncqL1w9ZEbS38H5cMyqqdhfgo2LWeDRdZHD0rvNyJsIaw==", 95 + "version": "0.19.4", 96 + "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.19.4.tgz", 97 + "integrity": "sha512-fYNM62vdXxer0h8a9Jzl4/ag9uFIe0nTO+LkC6KTlx1yUDigrAoQMMbllIiCWj62GhUMxAkHabk/BZjjVAfKng==", 99 98 "license": "MIT", 100 99 "dependencies": { 101 - "@atproto/common-web": "^0.4.3", 102 - "@atproto/lexicon": "^0.5.1", 103 - "@atproto/syntax": "^0.4.1", 104 - "@atproto/xrpc": "^0.7.5", 100 + "@atproto/common-web": "^0.4.18", 101 + "@atproto/lexicon": "^0.6.2", 102 + "@atproto/syntax": "^0.5.1", 103 + "@atproto/xrpc": "^0.7.7", 105 104 "await-lock": "^2.2.2", 106 105 "multiformats": "^9.9.0", 107 106 "tlds": "^1.234.0", ··· 109 108 } 110 109 }, 111 110 "node_modules/@atproto/common-web": { 112 - "version": "0.4.3", 113 - "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.3.tgz", 114 - "integrity": "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg==", 111 + "version": "0.4.19", 112 + "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.19.tgz", 113 + "integrity": "sha512-3BTi58p5WpT+9/zb6UZrdsXcfPo5P45UJm0E4iwHLILr+jc37CuBj9JReDSZ4U0i9RTrI3ZkfySyZ9bd+LnMsw==", 115 114 "license": "MIT", 116 115 "dependencies": { 117 - "graphemer": "^1.4.0", 116 + "@atproto/lex-data": "^0.0.14", 117 + "@atproto/lex-json": "^0.0.14", 118 + "@atproto/syntax": "^0.5.1", 119 + "zod": "^3.23.8" 120 + } 121 + }, 122 + "node_modules/@atproto/lex-data": { 123 + "version": "0.0.14", 124 + "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.14.tgz", 125 + "integrity": "sha512-53DUa9664SS76nGAMYopWsO10OH0AAdf7P/HSKB6Wzx3iqe6lk/K61QZnKxOG1LreYl5CfvIJU6eNf4txI6GlQ==", 126 + "license": "MIT", 127 + "dependencies": { 118 128 "multiformats": "^9.9.0", 129 + "tslib": "^2.8.1", 119 130 "uint8arrays": "3.0.0", 120 - "zod": "^3.23.8" 131 + "unicode-segmenter": "^0.14.0" 132 + } 133 + }, 134 + "node_modules/@atproto/lex-json": { 135 + "version": "0.0.14", 136 + "resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.0.14.tgz", 137 + "integrity": "sha512-6lPkDKqe7teEu4WrN5q7400cvZKgYS3uwUMvzG3F9XkgVYhOwSDCtouV/nSLBbpvo3l9OP0kiigtclcNcyekww==", 138 + "license": "MIT", 139 + "dependencies": { 140 + "@atproto/lex-data": "^0.0.14", 141 + "tslib": "^2.8.1" 121 142 } 122 143 }, 123 144 "node_modules/@atproto/lexicon": { 124 - "version": "0.5.1", 125 - "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.5.1.tgz", 126 - "integrity": "sha512-y8AEtYmfgVl4fqFxqXAeGvhesiGkxiy3CWoJIfsFDDdTlZUC8DFnZrYhcqkIop3OlCkkljvpSJi1hbeC1tbi8A==", 145 + "version": "0.6.2", 146 + "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.6.2.tgz", 147 + "integrity": "sha512-p3Ly6hinVZW0ETuAXZMeUGwuMm3g8HvQMQ41yyEE6AL0hAkfeKFaZKos6BdBrr6CjkpbrDZqE8M+5+QOceysMw==", 127 148 "license": "MIT", 128 149 "dependencies": { 129 - "@atproto/common-web": "^0.4.3", 130 - "@atproto/syntax": "^0.4.1", 150 + "@atproto/common-web": "^0.4.18", 151 + "@atproto/syntax": "^0.5.0", 131 152 "iso-datestring-validator": "^2.2.2", 132 153 "multiformats": "^9.9.0", 133 154 "zod": "^3.23.8" 134 155 } 135 156 }, 136 157 "node_modules/@atproto/syntax": { 137 - "version": "0.4.1", 138 - "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.1.tgz", 139 - "integrity": "sha512-CJdImtLAiFO+0z3BWTtxwk6aY5w4t8orHTMVJgkf++QRJWTxPbIFko/0hrkADB7n2EruDxDSeAgfUGehpH6ngw==", 140 - "license": "MIT" 158 + "version": "0.5.1", 159 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.5.1.tgz", 160 + "integrity": "sha512-J8DJjgKgACIyCTbpfvoTnf7+ofTx1kxTGO7KAftkC+jczaMdQhKdgIBAg2DaYy+80cvYGTHy5q/HI9qMAwGbWw==", 161 + "license": "MIT", 162 + "dependencies": { 163 + "tslib": "^2.8.1" 164 + } 141 165 }, 142 166 "node_modules/@atproto/xrpc": { 143 - "version": "0.7.5", 144 - "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.5.tgz", 145 - "integrity": "sha512-MUYNn5d2hv8yVegRL0ccHvTHAVj5JSnW07bkbiaz96UH45lvYNRVwt44z+yYVnb0/mvBzyD3/ZQ55TRGt7fHkA==", 167 + "version": "0.7.7", 168 + "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.7.tgz", 169 + "integrity": "sha512-K1ZyO/BU8JNtXX5dmPp7b5UrkLMMqpsIa/Lrj5D3Su+j1Xwq1m6QJ2XJ1AgjEjkI1v4Muzm7klianLE6XGxtmA==", 146 170 "license": "MIT", 147 171 "dependencies": { 148 - "@atproto/lexicon": "^0.5.1", 172 + "@atproto/lexicon": "^0.6.0", 149 173 "zod": "^3.23.8" 150 174 } 151 175 }, ··· 3095 3119 "@jridgewell/resolve-uri": "^3.1.0", 3096 3120 "@jridgewell/sourcemap-codec": "^1.4.14" 3097 3121 } 3098 - }, 3099 - "node_modules/@lifeomic/attempt": { 3100 - "version": "3.1.0", 3101 - "resolved": "https://registry.npmjs.org/@lifeomic/attempt/-/attempt-3.1.0.tgz", 3102 - "integrity": "sha512-QZqem4QuAnAyzfz+Gj5/+SLxqwCAw2qmt7732ZXodr6VDWGeYLG6w1i/vYLa55JQM9wRuBKLmXmiZ2P0LtE5rw==", 3103 - "license": "MIT" 3104 3122 }, 3105 3123 "node_modules/@malept/cross-spawn-promise": { 3106 3124 "version": "2.0.0", ··· 6294 6312 "tslib": "^2.4.0" 6295 6313 } 6296 6314 }, 6297 - "node_modules/@types/bluebird": { 6298 - "version": "3.5.42", 6299 - "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.42.tgz", 6300 - "integrity": "sha512-Jhy+MWRlro6UjVi578V/4ZGNfeCOcNCp0YaFNIUGFKlImowqwb1O/22wDVk3FDGMLqxdpOV3qQHD5fPEH4hK6A==", 6301 - "license": "MIT" 6302 - }, 6303 6315 "node_modules/@types/bn.js": { 6304 6316 "version": "5.2.0", 6305 6317 "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.2.0.tgz", ··· 6331 6343 "@types/responselike": "^1.0.0" 6332 6344 } 6333 6345 }, 6334 - "node_modules/@types/caseless": { 6335 - "version": "0.12.5", 6336 - "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz", 6337 - "integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==", 6338 - "license": "MIT" 6339 - }, 6340 - "node_modules/@types/chance": { 6341 - "version": "1.1.7", 6342 - "resolved": "https://registry.npmjs.org/@types/chance/-/chance-1.1.7.tgz", 6343 - "integrity": "sha512-40you9610GTQPJyvjMBgmj9wiDO6qXhbfjizNYod/fmvLSfUUxURAJMTD8tjmbcZSsyYE5iEUox61AAcCjW/wQ==", 6344 - "license": "MIT" 6345 - }, 6346 6346 "node_modules/@types/connect": { 6347 6347 "version": "3.4.38", 6348 6348 "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", ··· 6472 6472 "xmlbuilder": ">=11.0.1" 6473 6473 } 6474 6474 }, 6475 - "node_modules/@types/request": { 6476 - "version": "2.48.13", 6477 - "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.13.tgz", 6478 - "integrity": "sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg==", 6479 - "license": "MIT", 6480 - "dependencies": { 6481 - "@types/caseless": "*", 6482 - "@types/node": "*", 6483 - "@types/tough-cookie": "*", 6484 - "form-data": "^2.5.5" 6485 - } 6486 - }, 6487 - "node_modules/@types/request-promise": { 6488 - "version": "4.1.51", 6489 - "resolved": "https://registry.npmjs.org/@types/request-promise/-/request-promise-4.1.51.tgz", 6490 - "integrity": "sha512-qVcP9Fuzh9oaAh8oPxiSoWMFGnWKkJDknnij66vi09Yiy62bsSDqtd+fG5kIM9wLLgZsRP3Y6acqj9O/v2ZtRw==", 6491 - "license": "MIT", 6492 - "dependencies": { 6493 - "@types/bluebird": "*", 6494 - "@types/request": "*" 6495 - } 6496 - }, 6497 - "node_modules/@types/request/node_modules/form-data": { 6498 - "version": "2.5.5", 6499 - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz", 6500 - "integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==", 6501 - "license": "MIT", 6502 - "dependencies": { 6503 - "asynckit": "^0.4.0", 6504 - "combined-stream": "^1.0.8", 6505 - "es-set-tostringtag": "^2.1.0", 6506 - "hasown": "^2.0.2", 6507 - "mime-types": "^2.1.35", 6508 - "safe-buffer": "^5.2.1" 6509 - }, 6510 - "engines": { 6511 - "node": ">= 0.12" 6512 - } 6513 - }, 6514 - "node_modules/@types/request/node_modules/safe-buffer": { 6515 - "version": "5.2.1", 6516 - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 6517 - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 6518 - "funding": [ 6519 - { 6520 - "type": "github", 6521 - "url": "https://github.com/sponsors/feross" 6522 - }, 6523 - { 6524 - "type": "patreon", 6525 - "url": "https://www.patreon.com/feross" 6526 - }, 6527 - { 6528 - "type": "consulting", 6529 - "url": "https://feross.org/support" 6530 - } 6531 - ], 6532 - "license": "MIT" 6533 - }, 6534 6475 "node_modules/@types/responselike": { 6535 6476 "version": "1.0.3", 6536 6477 "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", ··· 6569 6510 "resolved": "https://registry.npmjs.org/@types/tinycolor2/-/tinycolor2-1.4.6.tgz", 6570 6511 "integrity": "sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==", 6571 6512 "dev": true, 6572 - "license": "MIT" 6573 - }, 6574 - "node_modules/@types/tough-cookie": { 6575 - "version": "4.0.5", 6576 - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", 6577 - "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", 6578 6513 "license": "MIT" 6579 6514 }, 6580 6515 "node_modules/@types/verror": { ··· 6746 6681 "version": "6.12.6", 6747 6682 "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 6748 6683 "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 6684 + "dev": true, 6749 6685 "license": "MIT", 6750 6686 "dependencies": { 6751 6687 "fast-deep-equal": "^3.1.1", ··· 7018 6954 "dev": true, 7019 6955 "license": "Python-2.0" 7020 6956 }, 7021 - "node_modules/asn1": { 7022 - "version": "0.2.6", 7023 - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", 7024 - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", 7025 - "license": "MIT", 7026 - "dependencies": { 7027 - "safer-buffer": "~2.1.0" 7028 - } 7029 - }, 7030 6957 "node_modules/assert-plus": { 7031 6958 "version": "1.0.0", 7032 6959 "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 7033 6960 "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", 6961 + "dev": true, 7034 6962 "license": "MIT", 6963 + "optional": true, 7035 6964 "engines": { 7036 6965 "node": ">=0.8" 7037 6966 } ··· 7136 7065 "integrity": "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==", 7137 7066 "license": "MIT" 7138 7067 }, 7139 - "node_modules/aws-sign2": { 7140 - "version": "0.7.0", 7141 - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 7142 - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", 7143 - "license": "Apache-2.0", 7144 - "engines": { 7145 - "node": "*" 7146 - } 7147 - }, 7148 - "node_modules/aws4": { 7149 - "version": "1.13.2", 7150 - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", 7151 - "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", 7152 - "license": "MIT" 7153 - }, 7154 7068 "node_modules/axe-core": { 7155 7069 "version": "4.11.0", 7156 7070 "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.0.tgz", ··· 7320 7234 "node": ">=10.0.0" 7321 7235 } 7322 7236 }, 7323 - "node_modules/bcrypt-pbkdf": { 7324 - "version": "1.0.2", 7325 - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 7326 - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", 7327 - "license": "BSD-3-Clause", 7328 - "dependencies": { 7329 - "tweetnacl": "^0.14.3" 7330 - } 7331 - }, 7332 7237 "node_modules/bignumber.js": { 7333 7238 "version": "9.3.1", 7334 7239 "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", ··· 7427 7332 "engines": { 7428 7333 "node": ">= 0.8.0" 7429 7334 } 7430 - }, 7431 - "node_modules/bluebird": { 7432 - "version": "3.7.2", 7433 - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", 7434 - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", 7435 - "license": "MIT" 7436 7335 }, 7437 7336 "node_modules/bn.js": { 7438 7337 "version": "5.2.2", ··· 7925 7824 "dev": true, 7926 7825 "license": "MIT" 7927 7826 }, 7928 - "node_modules/caseless": { 7929 - "version": "0.12.0", 7930 - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 7931 - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", 7932 - "license": "Apache-2.0" 7933 - }, 7934 7827 "node_modules/chalk": { 7935 7828 "version": "5.6.2", 7936 7829 "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", ··· 7943 7836 "url": "https://github.com/chalk/chalk?sponsor=1" 7944 7837 } 7945 7838 }, 7946 - "node_modules/chance": { 7947 - "version": "1.1.13", 7948 - "resolved": "https://registry.npmjs.org/chance/-/chance-1.1.13.tgz", 7949 - "integrity": "sha512-V6lQCljcLznE7tUYUM9EOAnnKXbctE6j/rdQkYOHIWbfGQbrzTsAXNW9CdU5XCo4ArXQCj/rb6HgxPlmGJcaUg==", 7950 - "license": "MIT" 7951 - }, 7952 7839 "node_modules/chokidar": { 7953 7840 "version": "3.6.0", 7954 7841 "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", ··· 8085 7972 "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", 8086 7973 "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", 8087 7974 "dev": true, 8088 - "license": "MIT" 8089 - }, 8090 - "node_modules/class-transformer": { 8091 - "version": "0.3.1", 8092 - "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.3.1.tgz", 8093 - "integrity": "sha512-cKFwohpJbuMovS8xVLmn8N2AUbAuc8pVo4zEfsUVo8qgECOogns1WVk/FkOZoxhOPTyTYFckuoH+13FO+MQ8GA==", 8094 7975 "license": "MIT" 8095 7976 }, 8096 7977 "node_modules/clean-stack": { ··· 8761 8642 "uniq": "^1.0.0" 8762 8643 } 8763 8644 }, 8764 - "node_modules/dashdash": { 8765 - "version": "1.14.1", 8766 - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 8767 - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", 8768 - "license": "MIT", 8769 - "dependencies": { 8770 - "assert-plus": "^1.0.0" 8771 - }, 8772 - "engines": { 8773 - "node": ">=0.10" 8774 - } 8775 - }, 8776 8645 "node_modules/data-uri-to-buffer": { 8777 8646 "version": "4.0.1", 8778 8647 "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", ··· 9162 9031 "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", 9163 9032 "license": "MIT" 9164 9033 }, 9165 - "node_modules/ecc-jsbn": { 9166 - "version": "0.1.2", 9167 - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 9168 - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", 9169 - "license": "MIT", 9170 - "dependencies": { 9171 - "jsbn": "~0.1.0", 9172 - "safer-buffer": "^2.1.0" 9173 - } 9174 - }, 9175 9034 "node_modules/ejs": { 9176 9035 "version": "3.1.10", 9177 9036 "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", ··· 9786 9645 "dev": true, 9787 9646 "license": "Apache-2.0" 9788 9647 }, 9789 - "node_modules/extend": { 9790 - "version": "3.0.2", 9791 - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 9792 - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", 9793 - "license": "MIT" 9794 - }, 9795 9648 "node_modules/extract-zip": { 9796 9649 "version": "2.0.1", 9797 9650 "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", ··· 9844 9697 "version": "3.1.3", 9845 9698 "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 9846 9699 "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 9700 + "dev": true, 9847 9701 "license": "MIT" 9848 9702 }, 9849 9703 "node_modules/fast-fifo": { ··· 10027 9881 }, 10028 9882 "funding": { 10029 9883 "url": "https://github.com/sponsors/isaacs" 10030 - } 10031 - }, 10032 - "node_modules/forever-agent": { 10033 - "version": "0.6.1", 10034 - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 10035 - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", 10036 - "license": "Apache-2.0", 10037 - "engines": { 10038 - "node": "*" 10039 9884 } 10040 9885 }, 10041 9886 "node_modules/form-data": { ··· 10367 10212 "node": ">= 14" 10368 10213 } 10369 10214 }, 10370 - "node_modules/getpass": { 10371 - "version": "0.1.7", 10372 - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 10373 - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", 10374 - "license": "MIT", 10375 - "dependencies": { 10376 - "assert-plus": "^1.0.0" 10377 - } 10378 - }, 10379 10215 "node_modules/gif-encoder-2": { 10380 10216 "version": "1.0.5", 10381 10217 "resolved": "https://registry.npmjs.org/gif-encoder-2/-/gif-encoder-2-1.0.5.tgz", ··· 10522 10358 "node": ">=14" 10523 10359 } 10524 10360 }, 10525 - "node_modules/graphemer": { 10526 - "version": "1.4.0", 10527 - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", 10528 - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", 10529 - "license": "MIT" 10530 - }, 10531 - "node_modules/har-schema": { 10532 - "version": "2.0.0", 10533 - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 10534 - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", 10535 - "license": "ISC", 10536 - "engines": { 10537 - "node": ">=4" 10538 - } 10539 - }, 10540 - "node_modules/har-validator": { 10541 - "version": "5.1.5", 10542 - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", 10543 - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", 10544 - "deprecated": "this library is no longer supported", 10545 - "license": "MIT", 10546 - "dependencies": { 10547 - "ajv": "^6.12.3", 10548 - "har-schema": "^2.0.0" 10549 - }, 10550 - "engines": { 10551 - "node": ">=6" 10552 - } 10553 - }, 10554 10361 "node_modules/has-flag": { 10555 10362 "version": "4.0.0", 10556 10363 "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", ··· 10857 10664 "node": ">=8" 10858 10665 } 10859 10666 }, 10860 - "node_modules/http-signature": { 10861 - "version": "1.2.0", 10862 - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 10863 - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", 10864 - "license": "MIT", 10865 - "dependencies": { 10866 - "assert-plus": "^1.0.0", 10867 - "jsprim": "^1.2.2", 10868 - "sshpk": "^1.7.0" 10869 - }, 10870 - "engines": { 10871 - "node": ">=0.8", 10872 - "npm": ">=1.3.7" 10873 - } 10874 - }, 10875 10667 "node_modules/http2-wrapper": { 10876 10668 "version": "2.2.1", 10877 10669 "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", ··· 10967 10759 "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", 10968 10760 "dev": true, 10969 10761 "license": "ISC" 10970 - }, 10971 - "node_modules/image-size": { 10972 - "version": "0.7.5", 10973 - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.7.5.tgz", 10974 - "integrity": "sha512-Hiyv+mXHfFEP7LzUL/llg9RwFxxY+o9N3JVLIeG5E7iFIFAalxvRU9UZthBdYDEVnzHMgjnKJPPpay5BWf1g9g==", 10975 - "license": "MIT", 10976 - "bin": { 10977 - "image-size": "bin/image-size.js" 10978 - }, 10979 - "engines": { 10980 - "node": ">=6.9.0" 10981 - } 10982 10762 }, 10983 10763 "node_modules/image-ssim": { 10984 10764 "version": "0.2.0", ··· 11272 11052 "url": "https://github.com/sponsors/sindresorhus" 11273 11053 } 11274 11054 }, 11275 - "node_modules/instagram-private-api": { 11276 - "version": "1.46.1", 11277 - "resolved": "https://registry.npmjs.org/instagram-private-api/-/instagram-private-api-1.46.1.tgz", 11278 - "integrity": "sha512-fq0q6UfhpikKZ5Kw8HNwS6YpsNghE9I/uc8AM9Do9nsQ+3H1u0jLz+0t/FcGkGTjZz5VGvU8s2VbWj9wxchwYg==", 11279 - "license": "MIT", 11280 - "dependencies": { 11281 - "@lifeomic/attempt": "^3.0.0", 11282 - "@types/chance": "^1.0.2", 11283 - "@types/request-promise": "^4.1.43", 11284 - "bluebird": "^3.7.1", 11285 - "chance": "^1.0.18", 11286 - "class-transformer": "^0.3.1", 11287 - "debug": "^4.1.1", 11288 - "image-size": "^0.7.3", 11289 - "json-bigint": "^1.0.0", 11290 - "lodash": "^4.17.20", 11291 - "luxon": "^1.12.1", 11292 - "reflect-metadata": "^0.1.13", 11293 - "request": "^2.88.0", 11294 - "request-promise": "^4.2.4", 11295 - "rxjs": "^6.5.2", 11296 - "snakecase-keys": "^3.1.0", 11297 - "tough-cookie": "^2.5.0", 11298 - "ts-custom-error": "^2.2.2", 11299 - "ts-xor": "^1.0.6", 11300 - "url-regex-safe": "^3.0.0", 11301 - "utility-types": "^3.10.0" 11302 - }, 11303 - "engines": { 11304 - "node": ">=8.0.0" 11305 - }, 11306 - "peerDependencies": { 11307 - "re2": "^1.17.2" 11308 - }, 11309 - "peerDependenciesMeta": { 11310 - "re2": { 11311 - "optional": true 11312 - } 11313 - } 11314 - }, 11315 - "node_modules/instagram-private-api/node_modules/rxjs": { 11316 - "version": "6.6.7", 11317 - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", 11318 - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", 11319 - "license": "Apache-2.0", 11320 - "dependencies": { 11321 - "tslib": "^1.9.0" 11322 - }, 11323 - "engines": { 11324 - "npm": ">=2.0.0" 11325 - } 11326 - }, 11327 - "node_modules/instagram-private-api/node_modules/tslib": { 11328 - "version": "1.14.1", 11329 - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 11330 - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", 11331 - "license": "0BSD" 11332 - }, 11333 11055 "node_modules/intl-messageformat": { 11334 11056 "version": "10.7.18", 11335 11057 "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.18.tgz", ··· 11358 11080 "license": "MIT", 11359 11081 "engines": { 11360 11082 "node": ">= 12" 11361 - } 11362 - }, 11363 - "node_modules/ip-regex": { 11364 - "version": "4.3.0", 11365 - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", 11366 - "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", 11367 - "license": "MIT", 11368 - "engines": { 11369 - "node": ">=8" 11370 11083 } 11371 11084 }, 11372 11085 "node_modules/is-arrayish": { ··· 11566 11279 "url": "https://github.com/sponsors/ljharb" 11567 11280 } 11568 11281 }, 11569 - "node_modules/is-typedarray": { 11570 - "version": "1.0.0", 11571 - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 11572 - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", 11573 - "license": "MIT" 11574 - }, 11575 11282 "node_modules/is-unicode-supported": { 11576 11283 "version": "0.1.0", 11577 11284 "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", ··· 11637 11344 "engines": { 11638 11345 "node": ">=0.10.0" 11639 11346 } 11640 - }, 11641 - "node_modules/isstream": { 11642 - "version": "0.1.2", 11643 - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 11644 - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", 11645 - "license": "MIT" 11646 11347 }, 11647 11348 "node_modules/jackspeak": { 11648 11349 "version": "3.4.3", ··· 11734 11435 "js-yaml": "bin/js-yaml.js" 11735 11436 } 11736 11437 }, 11737 - "node_modules/jsbn": { 11738 - "version": "0.1.1", 11739 - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 11740 - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", 11741 - "license": "MIT" 11742 - }, 11743 11438 "node_modules/jsesc": { 11744 11439 "version": "3.1.0", 11745 11440 "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", ··· 11752 11447 "node": ">=6" 11753 11448 } 11754 11449 }, 11755 - "node_modules/json-bigint": { 11756 - "version": "1.0.0", 11757 - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", 11758 - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", 11759 - "license": "MIT", 11760 - "dependencies": { 11761 - "bignumber.js": "^9.0.0" 11762 - } 11763 - }, 11764 11450 "node_modules/json-buffer": { 11765 11451 "version": "3.0.1", 11766 11452 "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", ··· 11775 11461 "dev": true, 11776 11462 "license": "MIT" 11777 11463 }, 11778 - "node_modules/json-schema": { 11779 - "version": "0.4.0", 11780 - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", 11781 - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", 11782 - "license": "(AFL-2.1 OR BSD-3-Clause)" 11783 - }, 11784 11464 "node_modules/json-schema-traverse": { 11785 11465 "version": "0.4.1", 11786 11466 "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 11787 11467 "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 11468 + "dev": true, 11788 11469 "license": "MIT" 11789 11470 }, 11790 11471 "node_modules/json-stringify-safe": { ··· 11815 11496 "graceful-fs": "^4.1.6" 11816 11497 } 11817 11498 }, 11818 - "node_modules/jsprim": { 11819 - "version": "1.4.2", 11820 - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", 11821 - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", 11822 - "license": "MIT", 11823 - "dependencies": { 11824 - "assert-plus": "1.0.0", 11825 - "extsprintf": "1.3.0", 11826 - "json-schema": "0.4.0", 11827 - "verror": "1.10.0" 11828 - }, 11829 - "engines": { 11830 - "node": ">=0.6.0" 11831 - } 11832 - }, 11833 - "node_modules/jsprim/node_modules/core-util-is": { 11834 - "version": "1.0.2", 11835 - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 11836 - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", 11837 - "license": "MIT" 11838 - }, 11839 - "node_modules/jsprim/node_modules/extsprintf": { 11840 - "version": "1.3.0", 11841 - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 11842 - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", 11843 - "engines": [ 11844 - "node >=0.6.0" 11845 - ], 11846 - "license": "MIT" 11847 - }, 11848 - "node_modules/jsprim/node_modules/verror": { 11849 - "version": "1.10.0", 11850 - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 11851 - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", 11852 - "engines": [ 11853 - "node >=0.6.0" 11854 - ], 11855 - "license": "MIT", 11856 - "dependencies": { 11857 - "assert-plus": "^1.0.0", 11858 - "core-util-is": "1.0.2", 11859 - "extsprintf": "^1.2.0" 11860 - } 11861 - }, 11862 11499 "node_modules/jszip": { 11863 11500 "version": "3.10.1", 11864 11501 "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", ··· 12207 11844 "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", 12208 11845 "license": "ISC" 12209 11846 }, 12210 - "node_modules/luxon": { 12211 - "version": "1.28.1", 12212 - "resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.1.tgz", 12213 - "integrity": "sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw==", 12214 - "license": "MIT", 12215 - "engines": { 12216 - "node": "*" 12217 - } 12218 - }, 12219 11847 "node_modules/make-dir": { 12220 11848 "version": "2.1.0", 12221 11849 "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", ··· 12329 11957 }, 12330 11958 "engines": { 12331 11959 "node": ">=8" 12332 - } 12333 - }, 12334 - "node_modules/map-obj": { 12335 - "version": "4.3.0", 12336 - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", 12337 - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", 12338 - "license": "MIT", 12339 - "engines": { 12340 - "node": ">=8" 12341 - }, 12342 - "funding": { 12343 - "url": "https://github.com/sponsors/sindresorhus" 12344 11960 } 12345 11961 }, 12346 11962 "node_modules/marky": { ··· 13056 12672 "node": ">= 0.10.0" 13057 12673 } 13058 12674 }, 13059 - "node_modules/oauth-sign": { 13060 - "version": "0.9.0", 13061 - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", 13062 - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", 13063 - "license": "Apache-2.0", 13064 - "engines": { 13065 - "node": "*" 13066 - } 13067 - }, 13068 12675 "node_modules/object-assign": { 13069 12676 "version": "4.1.1", 13070 12677 "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", ··· 13578 13185 "dev": true, 13579 13186 "license": "MIT" 13580 13187 }, 13581 - "node_modules/performance-now": { 13582 - "version": "2.1.0", 13583 - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 13584 - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", 13585 - "license": "MIT" 13586 - }, 13587 13188 "node_modules/pg-int8": { 13588 13189 "version": "1.0.1", 13589 13190 "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", ··· 13984 13585 "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", 13985 13586 "dev": true, 13986 13587 "license": "MIT" 13987 - }, 13988 - "node_modules/psl": { 13989 - "version": "1.15.0", 13990 - "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", 13991 - "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", 13992 - "license": "MIT", 13993 - "dependencies": { 13994 - "punycode": "^2.3.1" 13995 - }, 13996 - "funding": { 13997 - "url": "https://github.com/sponsors/lupomontero" 13998 - } 13999 13588 }, 14000 13589 "node_modules/pstree.remy": { 14001 13590 "version": "1.1.8", ··· 14427 14016 "node": ">=8.10.0" 14428 14017 } 14429 14018 }, 14430 - "node_modules/reflect-metadata": { 14431 - "version": "0.1.14", 14432 - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", 14433 - "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", 14434 - "license": "Apache-2.0" 14435 - }, 14436 14019 "node_modules/regenerator-runtime": { 14437 14020 "version": "0.13.11", 14438 14021 "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", 14439 14022 "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", 14440 14023 "license": "MIT" 14441 14024 }, 14442 - "node_modules/request": { 14443 - "version": "2.88.2", 14444 - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", 14445 - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", 14446 - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", 14447 - "license": "Apache-2.0", 14448 - "dependencies": { 14449 - "aws-sign2": "~0.7.0", 14450 - "aws4": "^1.8.0", 14451 - "caseless": "~0.12.0", 14452 - "combined-stream": "~1.0.6", 14453 - "extend": "~3.0.2", 14454 - "forever-agent": "~0.6.1", 14455 - "form-data": "~2.3.2", 14456 - "har-validator": "~5.1.3", 14457 - "http-signature": "~1.2.0", 14458 - "is-typedarray": "~1.0.0", 14459 - "isstream": "~0.1.2", 14460 - "json-stringify-safe": "~5.0.1", 14461 - "mime-types": "~2.1.19", 14462 - "oauth-sign": "~0.9.0", 14463 - "performance-now": "^2.1.0", 14464 - "qs": "~6.5.2", 14465 - "safe-buffer": "^5.1.2", 14466 - "tough-cookie": "~2.5.0", 14467 - "tunnel-agent": "^0.6.0", 14468 - "uuid": "^3.3.2" 14469 - }, 14470 - "engines": { 14471 - "node": ">= 6" 14472 - } 14473 - }, 14474 - "node_modules/request-promise": { 14475 - "version": "4.2.6", 14476 - "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.6.tgz", 14477 - "integrity": "sha512-HCHI3DJJUakkOr8fNoCc73E5nU5bqITjOYFMDrKHYOXWXrgD/SBaC7LjwuPymUprRyuF06UK7hd/lMHkmUXglQ==", 14478 - "deprecated": "request-promise has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142", 14479 - "license": "ISC", 14480 - "dependencies": { 14481 - "bluebird": "^3.5.0", 14482 - "request-promise-core": "1.1.4", 14483 - "stealthy-require": "^1.1.1", 14484 - "tough-cookie": "^2.3.3" 14485 - }, 14486 - "engines": { 14487 - "node": ">=0.10.0" 14488 - }, 14489 - "peerDependencies": { 14490 - "request": "^2.34" 14491 - } 14492 - }, 14493 - "node_modules/request-promise-core": { 14494 - "version": "1.1.4", 14495 - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", 14496 - "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", 14497 - "license": "ISC", 14498 - "dependencies": { 14499 - "lodash": "^4.17.19" 14500 - }, 14501 - "engines": { 14502 - "node": ">=0.10.0" 14503 - }, 14504 - "peerDependencies": { 14505 - "request": "^2.34" 14506 - } 14507 - }, 14508 - "node_modules/request/node_modules/form-data": { 14509 - "version": "2.3.3", 14510 - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", 14511 - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", 14512 - "license": "MIT", 14513 - "dependencies": { 14514 - "asynckit": "^0.4.0", 14515 - "combined-stream": "^1.0.6", 14516 - "mime-types": "^2.1.12" 14517 - }, 14518 - "engines": { 14519 - "node": ">= 0.12" 14520 - } 14521 - }, 14522 - "node_modules/request/node_modules/qs": { 14523 - "version": "6.5.5", 14524 - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.5.tgz", 14525 - "integrity": "sha512-mzR4sElr1bfCaPJe7m8ilJ6ZXdDaGoObcYR0ZHSsktM/Lt21MVHj5De30GQH2eiZ1qGRTO7LCAzQsUeXTNexWQ==", 14526 - "license": "BSD-3-Clause", 14527 - "engines": { 14528 - "node": ">=0.6" 14529 - } 14530 - }, 14531 14025 "node_modules/require-directory": { 14532 14026 "version": "2.1.1", 14533 14027 "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", ··· 14788 14282 "version": "2.1.2", 14789 14283 "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 14790 14284 "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 14285 + "devOptional": true, 14791 14286 "license": "MIT" 14792 14287 }, 14793 14288 "node_modules/sanitize-filename": { ··· 15254 14749 "npm": ">= 3.0.0" 15255 14750 } 15256 14751 }, 15257 - "node_modules/snakecase-keys": { 15258 - "version": "3.2.1", 15259 - "resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-3.2.1.tgz", 15260 - "integrity": "sha512-CjU5pyRfwOtaOITYv5C8DzpZ8XA/ieRsDpr93HI2r6e3YInC6moZpSQbmUtg8cTk58tq2x3jcG2gv+p1IZGmMA==", 15261 - "license": "MIT", 15262 - "dependencies": { 15263 - "map-obj": "^4.1.0", 15264 - "to-snake-case": "^1.0.0" 15265 - }, 15266 - "engines": { 15267 - "node": ">=8" 15268 - } 15269 - }, 15270 14752 "node_modules/socks": { 15271 14753 "version": "2.8.7", 15272 14754 "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", ··· 15361 14843 "license": "BSD-3-Clause", 15362 14844 "optional": true 15363 14845 }, 15364 - "node_modules/sshpk": { 15365 - "version": "1.18.0", 15366 - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", 15367 - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", 15368 - "license": "MIT", 15369 - "dependencies": { 15370 - "asn1": "~0.2.3", 15371 - "assert-plus": "^1.0.0", 15372 - "bcrypt-pbkdf": "^1.0.0", 15373 - "dashdash": "^1.12.0", 15374 - "ecc-jsbn": "~0.1.1", 15375 - "getpass": "^0.1.1", 15376 - "jsbn": "~0.1.0", 15377 - "safer-buffer": "^2.0.2", 15378 - "tweetnacl": "~0.14.0" 15379 - }, 15380 - "bin": { 15381 - "sshpk-conv": "bin/sshpk-conv", 15382 - "sshpk-sign": "bin/sshpk-sign", 15383 - "sshpk-verify": "bin/sshpk-verify" 15384 - }, 15385 - "engines": { 15386 - "node": ">=0.10.0" 15387 - } 15388 - }, 15389 14846 "node_modules/ssri": { 15390 14847 "version": "9.0.1", 15391 14848 "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", ··· 15441 14898 "license": "MIT", 15442 14899 "engines": { 15443 14900 "node": ">= 6" 15444 - } 15445 - }, 15446 - "node_modules/stealthy-require": { 15447 - "version": "1.1.1", 15448 - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", 15449 - "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==", 15450 - "license": "ISC", 15451 - "engines": { 15452 - "node": ">=0.10.0" 15453 14901 } 15454 14902 }, 15455 14903 "node_modules/streamx": { ··· 16058 15506 ], 16059 15507 "license": "MIT" 16060 15508 }, 16061 - "node_modules/to-no-case": { 16062 - "version": "1.0.2", 16063 - "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz", 16064 - "integrity": "sha512-Z3g735FxuZY8rodxV4gH7LxClE4H0hTIyHNIHdk+vpQxjLm0cwnKXq/OFVZ76SOQmto7txVcwSCwkU5kqp+FKg==", 16065 - "license": "MIT" 16066 - }, 16067 15509 "node_modules/to-regex-range": { 16068 15510 "version": "5.0.1", 16069 15511 "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", ··· 16077 15519 "node": ">=8.0" 16078 15520 } 16079 15521 }, 16080 - "node_modules/to-snake-case": { 16081 - "version": "1.0.0", 16082 - "resolved": "https://registry.npmjs.org/to-snake-case/-/to-snake-case-1.0.0.tgz", 16083 - "integrity": "sha512-joRpzBAk1Bhi2eGEYBjukEWHOe/IvclOkiJl3DtA91jV6NwQ3MwXA4FHYeqk8BNp/D8bmi9tcNbRu/SozP0jbQ==", 16084 - "license": "MIT", 16085 - "dependencies": { 16086 - "to-space-case": "^1.0.0" 16087 - } 16088 - }, 16089 - "node_modules/to-space-case": { 16090 - "version": "1.0.0", 16091 - "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz", 16092 - "integrity": "sha512-rLdvwXZ39VOn1IxGL3V6ZstoTbwLRckQmn/U8ZDLuWwIXNpuZDhQ3AiRUlhTbOXFVE9C+dR51wM0CBDhk31VcA==", 16093 - "license": "MIT", 16094 - "dependencies": { 16095 - "to-no-case": "^1.0.0" 16096 - } 16097 - }, 16098 15522 "node_modules/touch": { 16099 15523 "version": "3.1.1", 16100 15524 "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", ··· 16105 15529 "nodetouch": "bin/nodetouch.js" 16106 15530 } 16107 15531 }, 16108 - "node_modules/tough-cookie": { 16109 - "version": "2.5.0", 16110 - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", 16111 - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", 16112 - "license": "BSD-3-Clause", 16113 - "dependencies": { 16114 - "psl": "^1.1.28", 16115 - "punycode": "^2.1.1" 16116 - }, 16117 - "engines": { 16118 - "node": ">=0.8" 16119 - } 16120 - }, 16121 15532 "node_modules/tr46": { 16122 15533 "version": "5.1.1", 16123 15534 "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", ··· 16160 15571 "utf8-byte-length": "^1.0.1" 16161 15572 } 16162 15573 }, 16163 - "node_modules/ts-custom-error": { 16164 - "version": "2.2.2", 16165 - "resolved": "https://registry.npmjs.org/ts-custom-error/-/ts-custom-error-2.2.2.tgz", 16166 - "integrity": "sha512-I0FEdfdatDjeigRqh1JFj67bcIKyRNm12UVGheBjs2pXgyELg2xeiQLVaWu1pVmNGXZVnz/fvycSU41moBIpOg==", 16167 - "deprecated": "npm package tarball contains useless codeclimate-reporter binary, please update to version 3.1.1. See https://github.com/adriengibrat/ts-custom-error/issues/32", 16168 - "license": "WTFPL", 16169 - "engines": { 16170 - "node": ">=8.0.0" 16171 - } 16172 - }, 16173 - "node_modules/ts-xor": { 16174 - "version": "1.3.0", 16175 - "resolved": "https://registry.npmjs.org/ts-xor/-/ts-xor-1.3.0.tgz", 16176 - "integrity": "sha512-RLXVjliCzc1gfKQFLRpfeD0rrWmjnSTgj7+RFhoq3KRkUYa8LE/TIidYOzM5h+IdFBDSjjSgk9Lto9sdMfDFEA==", 16177 - "license": "MIT" 16178 - }, 16179 15574 "node_modules/tslib": { 16180 15575 "version": "2.8.1", 16181 15576 "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", ··· 16194 15589 "node": "*" 16195 15590 } 16196 15591 }, 16197 - "node_modules/tweetnacl": { 16198 - "version": "0.14.5", 16199 - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 16200 - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", 16201 - "license": "Unlicense" 16202 - }, 16203 15592 "node_modules/type-fest": { 16204 15593 "version": "4.41.0", 16205 15594 "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", ··· 16289 15678 "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", 16290 15679 "license": "MIT" 16291 15680 }, 15681 + "node_modules/unicode-segmenter": { 15682 + "version": "0.14.5", 15683 + "resolved": "https://registry.npmjs.org/unicode-segmenter/-/unicode-segmenter-0.14.5.tgz", 15684 + "integrity": "sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g==", 15685 + "license": "MIT" 15686 + }, 16292 15687 "node_modules/union": { 16293 15688 "version": "0.5.0", 16294 15689 "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", ··· 16378 15773 "version": "4.4.1", 16379 15774 "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 16380 15775 "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 15776 + "dev": true, 16381 15777 "license": "BSD-2-Clause", 16382 15778 "dependencies": { 16383 15779 "punycode": "^2.1.0" ··· 16390 15786 "dev": true, 16391 15787 "license": "MIT" 16392 15788 }, 16393 - "node_modules/url-regex-safe": { 16394 - "version": "3.0.0", 16395 - "resolved": "https://registry.npmjs.org/url-regex-safe/-/url-regex-safe-3.0.0.tgz", 16396 - "integrity": "sha512-+2U40NrcmtWFVjuxXVt9bGRw6c7/MgkGKN9xIfPrT/2RX0LTkkae6CCEDp93xqUN0UKm/rr821QnHd2dHQmN3A==", 16397 - "license": "MIT", 16398 - "dependencies": { 16399 - "ip-regex": "4.3.0", 16400 - "tlds": "^1.228.0" 16401 - }, 16402 - "engines": { 16403 - "node": ">= 10.12.0" 16404 - }, 16405 - "peerDependencies": { 16406 - "re2": "^1.17.2" 16407 - }, 16408 - "peerDependenciesMeta": { 16409 - "re2": { 16410 - "optional": true 16411 - } 16412 - } 16413 - }, 16414 15789 "node_modules/utf8-byte-length": { 16415 15790 "version": "1.0.5", 16416 15791 "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", ··· 16423 15798 "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 16424 15799 "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 16425 15800 "license": "MIT" 16426 - }, 16427 - "node_modules/utility-types": { 16428 - "version": "3.11.0", 16429 - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", 16430 - "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", 16431 - "license": "MIT", 16432 - "engines": { 16433 - "node": ">= 4" 16434 - } 16435 - }, 16436 - "node_modules/uuid": { 16437 - "version": "3.4.0", 16438 - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", 16439 - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", 16440 - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", 16441 - "license": "MIT", 16442 - "bin": { 16443 - "uuid": "bin/uuid" 16444 - } 16445 15801 }, 16446 15802 "node_modules/verror": { 16447 15803 "version": "1.10.1",
+12 -1
package.json
··· 40 40 "agent-memory:sync-codex": "node memory/codex-sync.mjs", 41 41 "agent-memory:install-hooks": "node memory/install-hooks.mjs", 42 42 "agent-memory:flush": "node memory/cli.mjs flush-remote", 43 + "at": "node at/cli.mjs", 44 + "at:health": "node at/cli.mjs health", 45 + "at:resolve": "node at/cli.mjs resolve", 46 + "at:profile": "node at/cli.mjs profile", 47 + "at:posts": "node at/cli.mjs posts", 48 + "at:post": "node at/cli.mjs post", 49 + "at:records": "node at/cli.mjs records", 50 + "at:lexicons": "node at/cli.mjs lexicons", 51 + "at:invite": "node at/cli.mjs invite", 52 + "at:accounts": "node at/cli.mjs accounts", 53 + "at:sync": "node at/cli.mjs sync:status", 43 54 "profile:secret:rotate": "node utilities/rotate-profile-stream-secret.mjs", 44 55 "ac": "./aesthetic", 45 56 "admin:udp": "ssh root@157.245.134.225", ··· 125 136 "terminal-kit": "^3.1.2" 126 137 }, 127 138 "dependencies": { 128 - "@atproto/api": "^0.17.2", 139 + "@atproto/api": "^0.19.4", 129 140 "@aws-sdk/client-s3": "^3.934.0", 130 141 "@babel/core": "^7.28.5", 131 142 "@babel/preset-react": "^7.28.5",
+287
reports/2026-03-23-at-protocol-audit.md
··· 1 + # AT Protocol Integration Audit 2 + 3 + **Date:** 2026-03-23 4 + **Scope:** at.aesthetic.computer PDS, ATProto sync, tooling, lexicons, CLI 5 + 6 + --- 7 + 8 + ## 1. Current State Overview 9 + 10 + Aesthetic Computer runs a **self-hosted PDS** (Personal Data Server) at `at.aesthetic.computer` on a DigitalOcean droplet. The integration is production-grade with: 11 + 12 + - 6 custom lexicons under `computer.aesthetic.*` 13 + - Bidirectional sync between MongoDB and PDS for paintings, moods, tapes, kidlisp, pieces, news 14 + - User account provisioning (handle format: `{handle}.at.aesthetic.computer`) 15 + - Guest/anonymous content via `art.at.aesthetic.computer` 16 + - 31+ admin scripts, 28+ sync/verification scripts 17 + - User profile pages at `{handle}.at.aesthetic.computer` 18 + 19 + **PDS Stack:** DigitalOcean droplet (1 CPU, 1GB RAM, 25GB SSD — $6/mo) + Spaces blob storage ($5/mo) 20 + 21 + --- 22 + 23 + ## 2. Architecture 24 + 25 + ### Data Flow (Hybrid Model) 26 + 27 + ``` 28 + User Action → AC Backend (Netlify Functions) 29 + ├── MongoDB (application data, search, features) 30 + ├── PDS (identity, ATProto records, federation) 31 + └── DO Spaces (media blobs — PNG, ZIP, MP4) 32 + ``` 33 + 34 + ### Key Backend Modules 35 + 36 + | Module | Location | Purpose | 37 + |--------|----------|---------| 38 + | `at.mjs` | `system/backend/at.mjs` | Account CRUD, handle sync | 39 + | `media-atproto.mjs` | `system/backend/media-atproto.mjs` | Unified record creation for all 5 media types | 40 + | `painting-atproto.mjs` | `system/backend/painting-atproto.mjs` | Painting-specific sync with thumbnails | 41 + | `mood-atproto.mjs` | `system/backend/mood-atproto.mjs` | Mood sync | 42 + | `tape-atproto.mjs` | `system/backend/tape-atproto.mjs` | Tape sync with MP4 conversion | 43 + | `news-atproto.mjs` | `system/backend/news-atproto.mjs` | News record sync | 44 + 45 + ### Netlify Functions Touching ATProto 46 + 47 + | Function | What It Does | 48 + |----------|-------------| 49 + | `handle.mjs` | Syncs AC handle changes → PDS handle | 50 + | `mood.mjs` | Creates mood + syncs to PDS | 51 + | `store-kidlisp.mjs` | Stores KidLisp + syncs to PDS | 52 + | `store-piece.mjs` | Stores piece + syncs to PDS | 53 + | `tv.mjs` / `tv-tapes.mjs` | Queries tapes from PDS via XRPC | 54 + | `atproto-user-stats.mjs` | Aggregate user stats from MongoDB | 55 + | `delete-erase-and-forget-me.mjs` | Full account deletion including PDS | 56 + 57 + --- 58 + 59 + ## 3. Custom Lexicons 60 + 61 + All live at `at/lexicons/computer/aesthetic/`: 62 + 63 + | Lexicon | Required Fields | Blob? | 64 + |---------|----------------|-------| 65 + | `computer.aesthetic.painting` | slug, code, imageUrl, when, ref | thumbnail (PNG/JPEG, 1MB max) | 66 + | `computer.aesthetic.mood` | mood, when, ref | — | 67 + | `computer.aesthetic.piece` | slug, when, ref | — | 68 + | `computer.aesthetic.kidlisp` | code, source, acUrl, when, ref | — | 69 + | `computer.aesthetic.tape` | slug, code, acUrl, when, ref | video (MP4), thumbnail | 70 + | `computer.aesthetic.news` | headline, when, ref | — | 71 + 72 + ### Lexicon Publication Status 73 + 74 + - **DNS TXT record** (`_lexicon.aesthetic.computer`) is planned but status unclear — needs verification. 75 + - Lexicon JSON schemas exist locally but may not be published to the network yet. 76 + 77 + --- 78 + 79 + ## 4. Accounts & Identity 80 + 81 + | Account | Handle | DID | Purpose | 82 + |---------|--------|-----|---------| 83 + | Main | `aesthetic.computer` | `did:plc:k3k3wknzkcnekbnyde4dbatz` | Official Bluesky presence | 84 + | Art (guest) | `art.at.aesthetic.computer` | `did:plc:tliuubv7lyv2uiknsjbf4ppw` | Anonymous/guest content | 85 + | Users | `{handle}.at.aesthetic.computer` | `did:plc:*` | Per-user PDS accounts | 86 + 87 + ### Account Provisioning Flow 88 + 89 + 1. User registers on AC (Auth0) 90 + 2. `createAtprotoAccount()` generates invite code, creates PDS account 91 + 3. Credentials stored in MongoDB: `users.atproto = { did, handle, password, created }` 92 + 4. Handle changes sync via `updateAtprotoHandle()` 93 + 5. Account deletion cascades to PDS via `deleteAtprotoAccount()` 94 + 95 + --- 96 + 97 + ## 5. Tooling Audit 98 + 99 + ### Existing Scripts (Scattered, No Unified CLI) 100 + 101 + **`at/` directory** — standalone query/post tools: 102 + - `query-profile.mjs` — fetch profile for any handle/DID 103 + - `query-posts.mjs` — fetch posts from account 104 + - `post-to-bluesky.mjs` — post to @aesthetic.computer 105 + - `resolve-did.mjs` — resolve DID documents, inspect PDS 106 + - `explore-lexicons.mjs` — browse lexicon schemas 107 + - `share-latest-painting.mjs` / `share-random-painting.mjs` 108 + - `test-mood-api.mjs` / `test-painting-atproto.mjs` / `test-all.mjs` 109 + 110 + **`at/pds/scripts/`** — admin tools (31 scripts): 111 + - Account: `create-account.mjs`, `create-bulk-accounts.mjs`, `recreate-jeffrey.mjs` 112 + - Invites: `generate-invite.mjs` 113 + - Checks: `check-missing-accounts.mjs`, `check-stale-atproto.mjs`, `check-handle-structure.mjs` 114 + - Migration: `migrate-pds-accounts.mjs`, `migrate-atproto-to-nested.mjs` 115 + - Monitoring: `health-check.sh`, `backup.sh` 116 + - Queries: `query-user.mjs`, `explore-collections.mjs`, `inspect-user-schema.mjs` 117 + 118 + **`scripts/atproto/`** — sync/backfill scripts (28 scripts): 119 + - Backfill: `backfill-moods-to-atproto.mjs`, `backfill-paintings-to-atproto.mjs` 120 + - Verify: `verify-paintings-sync.mjs`, `verify-tapes-sync.mjs` 121 + - Compare: `compare-moods-atproto-mongo.mjs` 122 + - Stats: `mood-sync-stats.mjs`, `painting-sync-stats.mjs` 123 + - Sync: `sync-atproto.mjs`, `sync-mongodb-from-atproto.mjs` 124 + 125 + ### Problem: No Unified Entry Point 126 + 127 + All ~60+ scripts require knowing exact paths and running `node path/to/script.mjs`. There is no `ac-at` CLI analogous to `ac-os`, `papers/cli.mjs`, or `memory/cli.mjs`. This makes the tooling: 128 + 129 + - **Hard to discover** — you need to know what exists and where 130 + - **Inconsistent invocation** — some use dotenv, some need env vars, some need SSH 131 + - **No help system** — no way to list available commands 132 + - **Duplicated patterns** — similar arg parsing, PDS URL resolution, etc. in each script 133 + 134 + --- 135 + 136 + ## 6. Issues & Improvement Opportunities 137 + 138 + ### P0 — Verify & Fix 139 + 140 + - [ ] **PDS health**: Run health check against `at.aesthetic.computer` — confirm it's online and responding 141 + - [ ] **DNS TXT record**: Verify `_lexicon.aesthetic.computer` TXT record exists for lexicon publication 142 + - [ ] **Wildcard DNS**: Confirm `*.at.aesthetic.computer` resolves correctly for user pages 143 + - [ ] **SSL certificates**: Check cert validity and auto-renewal (Caddy should handle this) 144 + - [ ] **@atproto/api version**: System uses `^0.18.0`, at/ experiments use `^0.17.0` — should unify 145 + 146 + ### P1 — Build 147 + 148 + - [ ] **`ac-at` CLI** — Unified command-line tool consolidating all AT tooling (see section 7) 149 + - [ ] **Lexicon publication** — Publish lexicon schemas to the network via DNS TXT + hosting 150 + - [ ] **User pages deployment verification** — Confirm user-page.html is being served at subdomains 151 + 152 + ### P2 — Improve 153 + 154 + - [ ] **Sync monitoring** — Automated checks for MongoDB ↔ PDS drift 155 + - [ ] **Credential rotation** — PDS user passwords are stored in MongoDB; consider rotation strategy 156 + - [ ] **Blob storage cleanup** — Orphaned blobs on PDS from deleted records 157 + - [ ] **Rate limiting audit** — Current config: `PDS_RATE_LIMIT_ENABLED=true`, `PDS_MAX_ACCOUNTS=100` 158 + - [ ] **Backup verification** — Confirm `backup.sh` runs and backups are restorable 159 + - [ ] **Error handling** — Some sync functions silently continue on failure; add alerting 160 + 161 + ### P3 — Future 162 + 163 + - [ ] **Federation** — Custom lexicon records are currently PDS-local; plan for relay/appview integration 164 + - [ ] **OAuth/DPOP** — ATProto is moving toward OAuth; plan migration from password-based auth 165 + - [ ] **Firehose consumer** — `firehose-monitor.mjs` exists but isn't integrated into monitoring 166 + - [ ] **Cross-post from PDS to Bluesky** — Paintings/tapes could auto-post to app.bsky.feed.post 167 + 168 + --- 169 + 170 + ## 7. `ac-at` CLI Specification 171 + 172 + Consolidate all AT tooling into a single CLI at `at/cli.mjs`, following the patterns of `memory/cli.mjs` and `papers/cli.mjs`. 173 + 174 + ### Commands 175 + 176 + ``` 177 + ac-at help Show all commands 178 + ac-at health PDS health check (port from health-check.sh) 179 + ac-at resolve <handle-or-did> Resolve DID document 180 + ac-at profile <handle-or-did> Query profile information 181 + ac-at posts <handle-or-did> [--limit=N] Query posts 182 + ac-at post <text> [--image=path] Post to Bluesky 183 + ac-at records <did> <collection> [--limit=N] List records in collection 184 + ac-at lexicons Show AC custom lexicons 185 + ac-at invite Generate PDS invite code 186 + ac-at accounts [--limit=N] List PDS accounts 187 + ac-at account:create <sub> Create PDS account for AC user 188 + ac-at account:check <handle-or-did> Inspect account status 189 + ac-at sync:status Show sync stats across all collections 190 + ac-at sync:paintings [--dry-run] Backfill paintings to PDS 191 + ac-at sync:moods [--dry-run] Backfill moods to PDS 192 + ac-at sync:verify <collection> Compare MongoDB ↔ PDS for a collection 193 + ``` 194 + 195 + ### npm scripts 196 + 197 + ```json 198 + "at": "node at/cli.mjs", 199 + "at:health": "node at/cli.mjs health", 200 + "at:resolve": "node at/cli.mjs resolve", 201 + "at:profile": "node at/cli.mjs profile", 202 + "at:post": "node at/cli.mjs post", 203 + "at:invite": "node at/cli.mjs invite" 204 + ``` 205 + 206 + --- 207 + 208 + ## 8. Dependency Versions 209 + 210 + | Package | system/ | at/ | Notes | 211 + |---------|---------|-----|-------| 212 + | `@atproto/api` | `^0.18.0` | `^0.17.0` | Should align to latest | 213 + | `@atproto/identity` | — | `^0.4.3` | Only in experiments | 214 + | `@atproto/lexicon` | — | `^0.4.2` | Only in experiments | 215 + | `@atproto/syntax` | — | `^0.3.1` | Only in experiments | 216 + | `@atproto/xrpc` | — | `^0.6.5` | Only in experiments | 217 + | `dotenv` | (via netlify) | `^16.4.5` | CLI scripts need this | 218 + 219 + --- 220 + 221 + ## 9. Environment Variables 222 + 223 + ### Required for PDS operations 224 + 225 + ``` 226 + PDS_URL=https://at.aesthetic.computer # PDS endpoint 227 + PDS_ADMIN_PASSWORD=<secret> # Admin API auth 228 + ``` 229 + 230 + ### Required for Bluesky posting 231 + 232 + ``` 233 + BSKY_IDENTIFIER=aesthetic.computer # Bluesky handle 234 + BSKY_APP_PASSWORD=<app-password> # From bsky.app/settings/app-passwords 235 + BSKY_SERVICE=https://bsky.social # Bluesky API endpoint 236 + ``` 237 + 238 + ### PDS Server Config (on droplet) 239 + 240 + ``` 241 + PDS_HOSTNAME=pds.aesthetic.computer 242 + PDS_ADMIN_EMAIL=me@jas.life 243 + PDS_PORT=3000 244 + PDS_BLOBSTORE_S3_BUCKET=aesthetic-pds-blobs 245 + PDS_INVITE_REQUIRED=true 246 + PDS_MAX_ACCOUNTS=100 247 + ``` 248 + 249 + --- 250 + 251 + ## 10. File Map 252 + 253 + ``` 254 + at/ 255 + ├── cli.mjs ← TO BUILD: unified CLI 256 + ├── package.json ← @atproto dependencies 257 + ├── lexicons/computer/aesthetic/ ← 6 lexicon JSON schemas 258 + ├── query-profile.mjs ← standalone tools (to consolidate) 259 + ├── query-posts.mjs 260 + ├── post-to-bluesky.mjs 261 + ├── resolve-did.mjs 262 + ├── explore-lexicons.mjs 263 + ├── pds/ 264 + │ ├── config/pds.env.example 265 + │ ├── scripts/ ← 31 admin scripts 266 + │ └── deployment/ ← DigitalOcean provisioning 267 + ├── scripts/atproto/ ← 28 sync/verify scripts (also at repo root) 268 + ├── user-page.html ← served at *.at.aesthetic.computer 269 + └── deploy-user-pages.fish 270 + 271 + system/backend/ 272 + ├── at.mjs ← core PDS account management 273 + ├── media-atproto.mjs ← unified record creation 274 + ├── painting-atproto.mjs ← painting sync 275 + ├── mood-atproto.mjs ← mood sync 276 + ├── tape-atproto.mjs ← tape sync 277 + └── news-atproto.mjs ← news sync 278 + 279 + system/netlify/functions/ 280 + ├── handle.mjs ← handle → PDS sync 281 + ├── mood.mjs ← mood creation + PDS 282 + ├── store-kidlisp.mjs ← kidlisp + PDS 283 + ├── store-piece.mjs ← piece + PDS 284 + ├── tv.mjs / tv-tapes.mjs ← query tapes from PDS 285 + ├── atproto-user-stats.mjs ← stats aggregation 286 + └── delete-erase-and-forget-me.mjs ← cascade delete 287 + ```
+3 -3
system/backend/bluesky-engagement.mjs
··· 2 2 // Fetch engagement (likes, reposts, replies) from Bluesky for mirrored moods 3 3 // 2026.01.28 4 4 5 - import { BskyAgent } from "@atproto/api"; 5 + import { AtpAgent } from "@atproto/api"; 6 6 import { shell } from "./shell.mjs"; 7 7 8 8 const BSKY_SERVICE = "https://bsky.social"; ··· 19 19 20 20 try { 21 21 // Public API - no auth needed for reading 22 - const agent = new BskyAgent({ service: BSKY_SERVICE }); 22 + const agent = new AtpAgent({ service: BSKY_SERVICE }); 23 23 24 24 // Get the post thread which includes engagement data 25 25 const response = await agent.getPostThread({ ··· 99 99 */ 100 100 export async function resolveDidToHandle(did) { 101 101 try { 102 - const agent = new BskyAgent({ service: BSKY_SERVICE }); 102 + const agent = new AtpAgent({ service: BSKY_SERVICE }); 103 103 const response = await agent.getProfile({ actor: did }); 104 104 return response.data.handle; 105 105 } catch {
+3 -3
system/backend/bluesky-mirror.mjs
··· 2 2 // Mirror moods to Bluesky @aesthetic.computer account 3 3 // 2026.01.28 4 4 5 - import { BskyAgent } from "@atproto/api"; 5 + import { AtpAgent } from "@atproto/api"; 6 6 import { shell } from "./shell.mjs"; 7 7 8 8 // Cache for Bluesky credentials from MongoDB ··· 66 66 } 67 67 68 68 try { 69 - const agent = new BskyAgent({ service: creds.service }); 69 + const agent = new AtpAgent({ service: creds.service }); 70 70 71 71 shell.log(`🦋 Logging into Bluesky as @${creds.identifier}...`); 72 72 await agent.login({ ··· 137 137 } 138 138 139 139 try { 140 - const agent = new BskyAgent({ service: creds.service }); 140 + const agent = new AtpAgent({ service: creds.service }); 141 141 142 142 await agent.login({ 143 143 identifier: creds.identifier,
+28 -28
system/package-lock.json
··· 9 9 "version": "1.0.0", 10 10 "license": "ISC", 11 11 "dependencies": { 12 - "@atproto/api": "^0.18.0", 12 + "@atproto/api": "^0.19.4", 13 13 "@ffmpeg/core": "^0.12.10", 14 14 "@ffmpeg/ffmpeg": "^0.12.15", 15 15 "@geckos.io/client": "^3.0.2", ··· 187 187 } 188 188 }, 189 189 "node_modules/@atproto/api": { 190 - "version": "0.18.21", 191 - "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.18.21.tgz", 192 - "integrity": "sha512-s35MIJerGT/pKe2xJtKKswqlIr/ola2r2iURBKBL0Mk1OKe6jP4YvTMh1N2d2PEANFzNNTbKoDaLfJPo2Uvc/w==", 190 + "version": "0.19.4", 191 + "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.19.4.tgz", 192 + "integrity": "sha512-fYNM62vdXxer0h8a9Jzl4/ag9uFIe0nTO+LkC6KTlx1yUDigrAoQMMbllIiCWj62GhUMxAkHabk/BZjjVAfKng==", 193 193 "license": "MIT", 194 194 "dependencies": { 195 - "@atproto/common-web": "^0.4.16", 196 - "@atproto/lexicon": "^0.6.1", 197 - "@atproto/syntax": "^0.4.3", 195 + "@atproto/common-web": "^0.4.18", 196 + "@atproto/lexicon": "^0.6.2", 197 + "@atproto/syntax": "^0.5.1", 198 198 "@atproto/xrpc": "^0.7.7", 199 199 "await-lock": "^2.2.2", 200 200 "multiformats": "^9.9.0", ··· 203 203 } 204 204 }, 205 205 "node_modules/@atproto/common-web": { 206 - "version": "0.4.16", 207 - "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.16.tgz", 208 - "integrity": "sha512-Ufvaff5JgxUyUyTAG0/3o7ltpy3lnZ1DvLjyAnvAf+hHfiK7OMQg+8byr+orN+KP9MtIQaRTsCgYPX+PxMKUoA==", 206 + "version": "0.4.19", 207 + "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.19.tgz", 208 + "integrity": "sha512-3BTi58p5WpT+9/zb6UZrdsXcfPo5P45UJm0E4iwHLILr+jc37CuBj9JReDSZ4U0i9RTrI3ZkfySyZ9bd+LnMsw==", 209 209 "license": "MIT", 210 210 "dependencies": { 211 - "@atproto/lex-data": "^0.0.11", 212 - "@atproto/lex-json": "^0.0.11", 213 - "@atproto/syntax": "^0.4.3", 211 + "@atproto/lex-data": "^0.0.14", 212 + "@atproto/lex-json": "^0.0.14", 213 + "@atproto/syntax": "^0.5.1", 214 214 "zod": "^3.23.8" 215 215 } 216 216 }, 217 217 "node_modules/@atproto/lex-data": { 218 - "version": "0.0.11", 219 - "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.11.tgz", 220 - "integrity": "sha512-4+KTtHdqwlhiTKA7D4SACea4jprsNpCQsNALW09wsZ6IHhCDGO5tr1cmV+QnLYe3G3mu1E1yXHXbPUHrUUDT/A==", 218 + "version": "0.0.14", 219 + "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.14.tgz", 220 + "integrity": "sha512-53DUa9664SS76nGAMYopWsO10OH0AAdf7P/HSKB6Wzx3iqe6lk/K61QZnKxOG1LreYl5CfvIJU6eNf4txI6GlQ==", 221 221 "license": "MIT", 222 222 "dependencies": { 223 223 "multiformats": "^9.9.0", ··· 227 227 } 228 228 }, 229 229 "node_modules/@atproto/lex-json": { 230 - "version": "0.0.11", 231 - "resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.0.11.tgz", 232 - "integrity": "sha512-2IExAoQ4KsR5fyPa1JjIvtR316PvdgRH/l3BVGLBd3cSxM3m5MftIv1B6qZ9HjNiK60SgkWp0mi9574bTNDhBQ==", 230 + "version": "0.0.14", 231 + "resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.0.14.tgz", 232 + "integrity": "sha512-6lPkDKqe7teEu4WrN5q7400cvZKgYS3uwUMvzG3F9XkgVYhOwSDCtouV/nSLBbpvo3l9OP0kiigtclcNcyekww==", 233 233 "license": "MIT", 234 234 "dependencies": { 235 - "@atproto/lex-data": "^0.0.11", 235 + "@atproto/lex-data": "^0.0.14", 236 236 "tslib": "^2.8.1" 237 237 } 238 238 }, 239 239 "node_modules/@atproto/lexicon": { 240 - "version": "0.6.1", 241 - "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.6.1.tgz", 242 - "integrity": "sha512-/vI1kVlY50Si+5MXpvOucelnYwb0UJ6Qto5mCp+7Q5C+Jtp+SoSykAPVvjVtTnQUH2vrKOFOwpb3C375vSKzXw==", 240 + "version": "0.6.2", 241 + "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.6.2.tgz", 242 + "integrity": "sha512-p3Ly6hinVZW0ETuAXZMeUGwuMm3g8HvQMQ41yyEE6AL0hAkfeKFaZKos6BdBrr6CjkpbrDZqE8M+5+QOceysMw==", 243 243 "license": "MIT", 244 244 "dependencies": { 245 - "@atproto/common-web": "^0.4.13", 246 - "@atproto/syntax": "^0.4.3", 245 + "@atproto/common-web": "^0.4.18", 246 + "@atproto/syntax": "^0.5.0", 247 247 "iso-datestring-validator": "^2.2.2", 248 248 "multiformats": "^9.9.0", 249 249 "zod": "^3.23.8" 250 250 } 251 251 }, 252 252 "node_modules/@atproto/syntax": { 253 - "version": "0.4.3", 254 - "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.3.tgz", 255 - "integrity": "sha512-YoZUz40YAJr5nPwvCDWgodEOlt5IftZqPJvA0JDWjuZKD8yXddTwSzXSaKQAzGOpuM+/A3uXRtPzJJqlScc+iA==", 253 + "version": "0.5.1", 254 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.5.1.tgz", 255 + "integrity": "sha512-J8DJjgKgACIyCTbpfvoTnf7+ofTx1kxTGO7KAftkC+jczaMdQhKdgIBAg2DaYy+80cvYGTHy5q/HI9qMAwGbWw==", 256 256 "license": "MIT", 257 257 "dependencies": { 258 258 "tslib": "^2.8.1"
+1 -1
system/package.json
··· 45 45 }, 46 46 "homepage": "https://github.com/whistlegraph/system#readme", 47 47 "dependencies": { 48 - "@atproto/api": "^0.18.0", 48 + "@atproto/api": "^0.19.4", 49 49 "@ffmpeg/core": "^0.12.10", 50 50 "@ffmpeg/ffmpeg": "^0.12.15", 51 51 "@geckos.io/client": "^3.0.2",
+44 -15
system/public/aesthetic.computer/disks/blank.mjs
··· 115 115 const availW = w * 0.42; 116 116 const availH = (contentBottom - 20) * 0.32; 117 117 const size = min(availW, availH); 118 - const fov = 260; 118 + const fov = size * 5.5; 119 119 120 120 // Slow turntable + gentle tilt variation (see it from all angles) 121 121 const ay = frame * 0.006; ··· 237 237 const viewDirY = sin(ax); 238 238 const viewDirZ = cos(ay) * cos(ax); 239 239 240 + const debugNormals = []; 240 241 const addFaces = (verts3d, proj, color, tag) => { 241 242 const frontFaces = new Set(); 242 243 // Compute box center for outward normal correction ··· 259 260 // Ensure normal points OUTWARD (away from box center) 260 261 const outDot = nx * (fcx - cx3) + ny * (fcy - cy3) + nz * (fcz - cz3); 261 262 if (outDot < 0) { nx = -nx; ny = -ny; nz = -nz; } 263 + // Normalize 264 + const nLen = sqrt(nx * nx + ny * ny + nz * nz) || 1; 265 + nx /= nLen; ny /= nLen; nz /= nLen; 262 266 // Dot with view direction — negative = faces camera (opposes view dir) 263 267 const dot = nx * viewDirX + ny * viewDirY + nz * viewDirZ; 268 + // Store debug normal (all faces, front or back) 269 + const nScale = 0.4; 270 + debugNormals.push({ 271 + from: [fcx, fcy, fcz], 272 + to: [fcx + nx * nScale, fcy + ny * nScale, fcz + nz * nScale], 273 + front: dot < 0, tag, fi, 274 + }); 264 275 if (dot >= 0) continue; // faces away from camera → skip 265 276 frontFaces.add(fi); 266 277 const z = (proj[a][2] + proj[b][2] + proj[c][2] + proj[d][2]) / 4; ··· 278 289 const kbTR = project([hw - kbInset, -0.001, kbInset]); 279 290 const kbBL = project([-hw + kbInset, -0.001, 2 * hd - kbInset * 3]); 280 291 const kbBR = project([hw - kbInset, -0.001, 2 * hd - kbInset * 3]); 281 - const ke1x = kbTR[0] - kbTL[0], ke1y = kbTR[1] - kbTL[1]; 282 - const ke2x = kbBL[0] - kbTL[0], ke2y = kbBL[1] - kbTL[1]; 283 - // Store keyboard data to draw after sorted loop (always on top of base) 284 - let kbVisible = ke1x * ke2y - ke1y * ke2x < 0; 292 + // Screen-space winding check (same approach as lid screen content) 293 + const kbE1x = kbTR[0] - kbTL[0], kbE1y = kbTR[1] - kbTL[1]; 294 + const kbE2x = kbBL[0] - kbTL[0], kbE2y = kbBL[1] - kbTL[1]; 295 + const kbCross = kbE1x * kbE2y - kbE1y * kbE2x; 296 + // Smooth alpha from cross product magnitude (same as screen content) 297 + const kbMaxCross = size * size * 0.5; 298 + const kbFacingAmount = kbCross < 0 ? min(1, abs(kbCross) / kbMaxCross) : 0; 299 + // Fade with hinge angle (smooth open/close) and lid occlusion 300 + const hingeFade = min(1, max(0, (hingeAngle - 0.1) / 0.5)); 301 + const lidOccludes = hingeAngle < PI && lidFrontFaces.has(1); 302 + const kbAlpha = floor(kbFacingAmount * hingeFade * (lidOccludes ? 0 : 1) * 255); 303 + const kbFacing = kbAlpha > 2; 285 304 const kbKeys = []; 286 - if (kbVisible) { 305 + if (kbFacing) { 287 306 // 6-row ThinkPad keyboard layout (key counts per row) 288 307 // Row 0: Fn keys (14 keys: Esc + F1-F12 + Del) 289 308 // Row 1: Number row (14 keys: ` 1-0 - = Bksp) ··· 395 414 } 396 415 } 397 416 398 - // ⌨️ Keyboard keys — only draw when base top face (faceQuads[0]) is front-facing 399 - // and lid isn't in front of base 400 - const baseTopVisible = baseFrontFaces.has(0); 401 - const lidAvgZ = projLid.reduce((s, v) => s + v[2], 0) / projLid.length; 402 - const baseAvgZ = projBase.reduce((s, v) => s + v[2], 0) / projBase.length; 403 - const kbNotOccluded = baseTopVisible && lidAvgZ >= baseAvgZ; 404 - if (kbVisible && kbNotOccluded) { 417 + // 🔍 Debug: draw face normals (green = front-facing, red = back-facing) 418 + for (const dn of debugNormals) { 419 + const p0 = project(dn.from); 420 + const p1 = project(dn.to); 421 + if (dn.front) { 422 + ink(0, 255, 0, 200); 423 + } else { 424 + ink(255, 0, 0, 120); 425 + } 426 + line(p0[0], p0[1], p1[0], p1[1]); 427 + // Dot at tip 428 + ink(255, 255, 0, 220).box(p1[0] - 1, p1[1] - 1, 3, 3); 429 + } 430 + 431 + // ⌨️ Keyboard keys — smooth fade based on facing + hinge + occlusion 432 + if (kbFacing) { 405 433 for (const key of kbKeys) { 406 434 const [[x0, y0], [x1, y1], [x2, y2], [x3, y3]] = key.pts; 407 435 const c = key.color || keyColor; 408 - ink(c[0], c[1], c[2]); 436 + ink(c[0], c[1], c[2], kbAlpha); 409 437 tri(x0, y0, x1, y1, x2, y2); 410 438 tri(x0, y0, x2, y2, x3, y3); 411 439 // White blinky wireframe outline 412 440 const blink = sin(frame * 0.08 + x0 * 0.3 + y0 * 0.7) * 0.5 + 0.5; 413 - ink(255, 255, 255, floor(30 + blink * 50)); 441 + const wireAlpha = floor((30 + blink * 50) * kbAlpha / 255); 442 + ink(255, 255, 255, wireAlpha); 414 443 line(x0, y0, x1, y1); 415 444 line(x1, y1, x2, y2); 416 445 line(x2, y2, x3, y3);
+51 -7
system/public/aesthetic.computer/disks/os.mjs
··· 61 61 // Build telemetry 62 62 let activeBuild = null; // { id, status, stage, percent, ref, error } 63 63 let buildLogLines = []; // last few log lines 64 - const MAX_BUILD_LOGS = 6; 64 + const MAX_BUILD_LOGS = 10; 65 65 let buildPollTimer = null; 66 66 67 67 // Color palettes for dark/light mode (deprecated builds use red tones) ··· 185 185 .then(data => { 186 186 if (data.active) { 187 187 activeBuild = data.active; 188 - needsPaint(); 188 + // Fetch log lines for active build 189 + if (data.activeJobId) { 190 + fetch(OVEN_BASE() + "/native-build/" + data.activeJobId + "?logs=true&tail=10") 191 + .then(r => r.json()) 192 + .then(detail => { 193 + if (detail.logs) { 194 + buildLogLines = detail.logs.slice(-MAX_BUILD_LOGS).map(l => l.line || l); 195 + } 196 + needsPaint(); 197 + }) 198 + .catch(() => needsPaint()); 199 + } else { 200 + needsPaint(); 201 + } 189 202 } else { 190 - activeBuild = null; 203 + if (activeBuild) { activeBuild = null; buildLogLines = []; needsPaint(); } 191 204 } 192 205 }) 193 206 .catch(() => {}); ··· 217 230 needsPaint(); 218 231 } 219 232 if (msg.type === "os:build-progress" && msg.build) { 220 - activeBuild = msg.build; 221 - if (msg.build.status === "success" || msg.build.status === "failed" || msg.build.status === "cancelled") { 233 + const b = msg.build; 234 + // Lightweight per-line message (has .line but no full snapshot fields) 235 + if (b.line && !b.status) { 236 + if (buildLogLines.length >= MAX_BUILD_LOGS) buildLogLines.shift(); 237 + buildLogLines.push(b.line); 238 + // Update stage/percent from lightweight msg 239 + if (activeBuild) { 240 + if (b.stage) activeBuild.stage = b.stage; 241 + if (b.percent) activeBuild.percent = b.percent; 242 + } 243 + needsPaint(); 244 + return; 245 + } 246 + // Full snapshot 247 + activeBuild = b; 248 + if (b.recentLines) { 249 + buildLogLines = b.recentLines.slice(-MAX_BUILD_LOGS); 250 + } 251 + if (b.status === "success" || b.status === "failed" || b.status === "cancelled") { 222 252 // Build done — refresh releases list 223 253 setTimeout(() => { 224 254 activeBuild = null; 255 + buildLogLines = []; 225 256 fetchReleases(ui, needsPaint); 226 257 needsPaint(); 227 - }, 3000); 258 + }, 5000); 228 259 } 229 260 needsPaint(); 230 261 } ··· 406 437 const rawStage = activeBuild.stage || "starting"; 407 438 const isCLBuild = rawStage.startsWith("cl-"); 408 439 const buildVariant = isCLBuild ? "Common Lisp" : "C"; 409 - sectionHeader("Building (" + buildVariant + ")", dark ? [20, 28, 20] : [215, 235, 215], dark ? [14, 20, 14] : [228, 240, 228], 120); 440 + const logH = buildLogLines.length * (matrixH + 1) + 8; 441 + sectionHeader("Building (" + buildVariant + ")", dark ? [20, 28, 20] : [215, 235, 215], dark ? [14, 20, 14] : [228, 240, 228], 120 + logH); 410 442 411 443 // Status line: stage + percentage 412 444 const stageLabel = isCLBuild ? rawStage.slice(3) : rawStage; ··· 451 483 ink(255, 80, 80); 452 484 $.write(activeBuild.error, { x: pad, y, wrap: wrapW }); 453 485 y += rowH + 2; 486 + } 487 + 488 + // Live log lines 489 + if (buildLogLines.length > 0) { 490 + y += 4; 491 + for (const line of buildLogLines) { 492 + const maxChars = Math.floor(wrapW / matrixW); 493 + const display = line.length > maxChars ? line.slice(0, maxChars - 1) + "~" : line; 494 + ink(...C.instText); 495 + $.write(display, { x: pad, y, wrap: wrapW }, undefined, undefined, false, "MatrixChunky8"); 496 + y += matrixH + 1; 497 + } 454 498 } 455 499 456 500 y += isMobile ? 10 : 6;